]>
Commit | Line | Data |
---|---|---|
59bc9e85 | 1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ |
50b4f4de | 2 | /* |
59bc9e85 | 3 | * toolbar.c - this file was part of Geany (v1.24.1), a fast and lightweight IDE |
50b4f4de RN |
4 | * |
5 | * Copyright 2009-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de> | |
6 | * Copyright 2009-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com> | |
59bc9e85 | 7 | * Copyright 2014 Rob Norris <rw_norris@hotmail.com> |
50b4f4de RN |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License along | |
20 | * with this program; if not, write to the Free Software Foundation, Inc., | |
21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
22 | */ | |
23 | ||
24 | /** | |
25 | * @file toolbar.h | |
26 | * Toolbar (prefs). | |
27 | */ | |
28 | /* Utility functions to create the toolbar */ | |
29 | ||
59bc9e85 RN |
30 | #ifdef HAVE_CONFIG_H |
31 | # include "config.h" | |
32 | #endif | |
50b4f4de | 33 | |
59bc9e85 RN |
34 | #include "toolbar.h" |
35 | #include "dir.h" | |
36 | #include "ui_util.h" | |
37 | #include "util.h" | |
50b4f4de | 38 | #include <string.h> |
59bc9e85 | 39 | #include <glib/gi18n.h> |
50b4f4de | 40 | #include <glib/gstdio.h> |
59bc9e85 RN |
41 | #include <gdk/gdkkeysyms.h> |
42 | ||
43 | #include "preferences.h" | |
44 | ||
45 | ||
46 | struct _VikToolbarClass | |
47 | { | |
48 | GObjectClass object_class; | |
49 | }; | |
50 | ||
51 | struct _VikToolbar { | |
52 | GObject obj; | |
53 | GtkWidget *widget; | |
54 | GtkUIManager *uim; | |
55 | guint merge_id; | |
56 | GtkActionGroup *group_actions; | |
57 | GtkActionGroup *group_toggles; | |
58 | GtkActionGroup *group_tools; | |
59 | GtkActionGroup *group_modes; | |
60 | GSList *list_of_actions; | |
61 | GSList *list_of_toggles; | |
62 | GSList *list_of_tools; | |
63 | GSList *list_of_modes; | |
64 | }; | |
65 | ||
66 | G_DEFINE_TYPE (VikToolbar, vik_toolbar, G_TYPE_OBJECT) | |
67 | ||
68 | static void vik_toolbar_class_init (VikToolbarClass *klass) | |
69 | { | |
70 | } | |
71 | ||
72 | static void vik_toolbar_init (VikToolbar *vtb) | |
73 | { | |
74 | vtb->widget = NULL; | |
75 | vtb->merge_id = 0; | |
76 | vtb->list_of_actions = NULL; | |
77 | vtb->list_of_toggles = NULL; | |
78 | vtb->list_of_tools = NULL; | |
79 | vtb->list_of_modes = NULL; | |
80 | } | |
50b4f4de | 81 | |
59bc9e85 RN |
82 | VikToolbar *vik_toolbar_new () |
83 | { | |
84 | VikToolbar *vtb = (VikToolbar *)g_object_new(vik_toolbar_get_type(), NULL); | |
85 | return vtb; | |
86 | } | |
50b4f4de | 87 | |
59bc9e85 RN |
88 | #define TOOLBAR_PARAMS_GROUP_KEY "toolbar" |
89 | #define TOOLBAR_PARAMS_NAMESPACE "toolbar." | |
90 | ||
91 | static gchar *params_icon_size[] = { N_("System Default"), N_("Small"), N_("Medium"), N_("Large"), NULL }; | |
92 | static gchar *params_icon_style[] = { N_("System Default"), N_("Icons Only"), N_("Text Only"), N_("Icons and Text"), NULL }; | |
93 | ||
94 | typedef struct { | |
95 | VikToolbar *vtb; | |
96 | GtkWindow *parent; | |
97 | GtkWidget *vbox; | |
98 | GtkWidget *hbox; | |
99 | ReloadCB *reload_cb; | |
100 | gpointer user_data; | |
101 | } config_t; | |
102 | ||
103 | static config_t extra_widget_data; | |
104 | ||
105 | static VikLayerParam prefs[] = { | |
106 | { VIK_LAYER_NUM_TYPES, TOOLBAR_PARAMS_NAMESPACE "append_to_menu", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Append to Menu:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, | |
107 | N_("Pack the toolbar to the main menu to save vertical space"), NULL, NULL, NULL }, | |
108 | { VIK_LAYER_NUM_TYPES, TOOLBAR_PARAMS_NAMESPACE "icon_size", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Icon Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_icon_size, NULL, | |
109 | NULL, NULL, NULL, NULL }, | |
110 | { VIK_LAYER_NUM_TYPES, TOOLBAR_PARAMS_NAMESPACE "icon_style", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Icon Style:"), VIK_LAYER_WIDGET_COMBOBOX, params_icon_style, NULL, | |
111 | NULL, NULL, NULL, NULL }, | |
112 | { VIK_LAYER_NUM_TYPES, TOOLBAR_PARAMS_NAMESPACE "NOTSAVED1", VIK_LAYER_PARAM_PTR, VIK_LAYER_GROUP_NONE, N_("Customize:"), VIK_LAYER_WIDGET_BUTTON, N_("Customize Buttons"), NULL, | |
113 | NULL, NULL, NULL, NULL }, | |
50b4f4de | 114 | }; |
50b4f4de | 115 | |
59bc9e85 RN |
116 | // Global storage to enable freeing upon closure |
117 | static GHashTable *signal_data; | |
118 | static GSList *toggle_overrides = NULL; | |
119 | ||
120 | // Forward declaration | |
121 | void toolbar_configure (VikToolbar *vtb, GtkWidget *toolbar, GtkWindow *parent, GtkWidget *vbox, GtkWidget *hbox, ReloadCB reload_cb, gpointer user_data); | |
122 | ||
123 | void toolbar_configure_cb(void) | |
124 | { | |
125 | // Values not known at prefs initialization. | |
126 | // So trying to pass these values via the UI builder is not possible currently. | |
127 | // ATM cheat via internal values - although this doesn't work properly for multiple Windows... | |
128 | toolbar_configure ( extra_widget_data.vtb, | |
129 | extra_widget_data.vtb->widget, | |
130 | extra_widget_data.parent, | |
131 | extra_widget_data.vbox, | |
132 | extra_widget_data.hbox, | |
133 | extra_widget_data.reload_cb, | |
134 | extra_widget_data.user_data ); | |
135 | } | |
136 | ||
137 | /** | |
138 | * a_toolbar_init: | |
139 | * | |
140 | * Initialize stuff for the toolbar. | |
141 | */ | |
142 | void a_toolbar_init (void) | |
143 | { | |
144 | // Preferences | |
145 | a_preferences_register_group ( TOOLBAR_PARAMS_GROUP_KEY, _("Toolbar") ); | |
146 | ||
147 | guint i = 0; | |
148 | VikLayerParamData tmp; | |
149 | tmp.b = FALSE; | |
150 | a_preferences_register (&prefs[i++], tmp, TOOLBAR_PARAMS_GROUP_KEY); | |
151 | tmp.u = 0; | |
152 | a_preferences_register (&prefs[i++], tmp, TOOLBAR_PARAMS_GROUP_KEY); | |
7ef455d5 RN |
153 | #ifdef WINDOWS |
154 | tmp.u = 1; // Small Icons for Windows by default as 'System Defaults' is more GNOME Theme driven. | |
155 | #else | |
59bc9e85 | 156 | tmp.u = 0; |
7ef455d5 | 157 | #endif |
59bc9e85 RN |
158 | a_preferences_register (&prefs[i++], tmp, TOOLBAR_PARAMS_GROUP_KEY); |
159 | tmp.ptr = toolbar_configure_cb; | |
160 | a_preferences_register (&prefs[i++], tmp, TOOLBAR_PARAMS_GROUP_KEY); | |
161 | ||
162 | // Signal data hash | |
163 | signal_data = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free ); | |
164 | } | |
165 | ||
166 | /** | |
167 | * a_toolbar_uninit: | |
168 | * | |
169 | * Uninitialize toolbar related stuff. | |
170 | */ | |
171 | void a_toolbar_uninit ( void ) | |
172 | { | |
173 | g_hash_table_destroy ( signal_data ); | |
174 | g_slist_foreach ( toggle_overrides, (GFunc)g_free, NULL ); | |
175 | g_slist_free ( toggle_overrides ); | |
176 | } | |
177 | ||
178 | static gboolean prefs_get_append_to_menu (void) | |
179 | { | |
180 | return a_preferences_get(TOOLBAR_PARAMS_NAMESPACE "append_to_menu")->b; | |
181 | } | |
182 | ||
183 | static guint prefs_get_icon_size (void) | |
184 | { | |
185 | return a_preferences_get(TOOLBAR_PARAMS_NAMESPACE "icon_size")->b; | |
186 | } | |
187 | ||
188 | static guint prefs_get_icon_style (void) | |
189 | { | |
190 | return a_preferences_get(TOOLBAR_PARAMS_NAMESPACE "icon_style")->b; | |
191 | } | |
50b4f4de RN |
192 | |
193 | /* Note: The returned widget pointer is only valid until the toolbar is reloaded. So, either | |
194 | * update the widget pointer in this case (i.e. request it again) or better use | |
195 | * toolbar_get_action_by_name() instead. The action objects will remain the same even when the | |
196 | * toolbar is reloaded. */ | |
59bc9e85 | 197 | GtkWidget *toolbar_get_widget_by_name(VikToolbar *vtb, const gchar *name) |
50b4f4de RN |
198 | { |
199 | GtkWidget *widget; | |
200 | gchar *path; | |
201 | ||
202 | g_return_val_if_fail(name != NULL, NULL); | |
59bc9e85 | 203 | g_return_val_if_fail(VIK_IS_TOOLBAR(vtb), NULL); |
50b4f4de | 204 | |
59bc9e85 RN |
205 | path = g_strconcat("/ui/MainToolbar/", name, NULL); |
206 | widget = gtk_ui_manager_get_widget(vtb->uim, path); | |
50b4f4de RN |
207 | |
208 | g_free(path); | |
209 | return widget; | |
210 | } | |
211 | ||
59bc9e85 | 212 | static GtkAction *get_action ( VikToolbar *vtb, const gchar *name ) |
50b4f4de | 213 | { |
59bc9e85 RN |
214 | // Try all groups |
215 | GtkAction *action = gtk_action_group_get_action (vtb->group_actions, name); | |
216 | if ( !action ) | |
217 | action = gtk_action_group_get_action (vtb->group_tools, name); | |
218 | if ( !action ) | |
219 | action = gtk_action_group_get_action (vtb->group_toggles, name); | |
220 | if ( !action ) | |
221 | action = gtk_action_group_get_action (vtb->group_modes, name); | |
222 | return action; | |
50b4f4de RN |
223 | } |
224 | ||
59bc9e85 RN |
225 | /** |
226 | * toolbar_get_action_by_name: | |
227 | * | |
228 | * Find an action in the specified toolbar via the action name | |
229 | */ | |
230 | GtkAction *toolbar_get_action_by_name(VikToolbar *vtb, const gchar *name) | |
50b4f4de RN |
231 | { |
232 | g_return_val_if_fail(name != NULL, NULL); | |
59bc9e85 | 233 | g_return_val_if_fail(VIK_IS_TOOLBAR(vtb), NULL); |
50b4f4de | 234 | |
59bc9e85 | 235 | return get_action(vtb,name); |
50b4f4de RN |
236 | } |
237 | ||
59bc9e85 RN |
238 | /** |
239 | * toolbar_action_tool_entry_register: | |
240 | * | |
241 | * Register a tool button in the specified toolbar | |
242 | * Only one of these tools can be active at a time (hence it is a GtkRadioActionEntry) | |
243 | */ | |
244 | void toolbar_action_tool_entry_register(VikToolbar *vtb, GtkRadioActionEntry *action) | |
245 | { | |
246 | g_return_if_fail(VIK_IS_TOOLBAR(vtb)); | |
247 | g_return_if_fail(action != NULL); | |
248 | vtb->list_of_tools = g_slist_append(vtb->list_of_tools, action); | |
249 | } | |
50b4f4de | 250 | |
59bc9e85 RN |
251 | /** |
252 | * toolbar_action_mode_entry_register: | |
253 | * | |
254 | * Register a drawing projection mode button in the specified toolbar | |
255 | * Only one of these modes can be active at a time (hence it is a GtkRadioActionEntry) | |
256 | */ | |
257 | void toolbar_action_mode_entry_register(VikToolbar *vtb, GtkRadioActionEntry *action) | |
50b4f4de | 258 | { |
59bc9e85 RN |
259 | g_return_if_fail(VIK_IS_TOOLBAR(vtb)); |
260 | g_return_if_fail(action != NULL); | |
261 | vtb->list_of_modes = g_slist_append(vtb->list_of_modes, action); | |
50b4f4de RN |
262 | } |
263 | ||
59bc9e85 RN |
264 | /** |
265 | * toolbar_action_toggle_entry_register: | |
266 | * | |
267 | * Register a toggle button in the specified toolbar with the specified callback | |
268 | * Used in preventing circluar callbacks of a toolbar toggle event calling the menu toggle event | |
269 | * (that then calls toolbar callback and so on and so on...) | |
270 | * The toggle action must be given a pointer to a function that is used on the callback for toolbar only | |
271 | * (that must offer a way to have a finite call chain!) | |
272 | */ | |
273 | void toolbar_action_toggle_entry_register(VikToolbar *vtb, GtkToggleActionEntry *action, gpointer callback) | |
274 | { | |
275 | g_return_if_fail(VIK_IS_TOOLBAR(vtb)); | |
276 | g_return_if_fail(action != NULL); | |
50b4f4de | 277 | |
59bc9e85 RN |
278 | GtkToggleActionEntry *myaction = g_malloc (sizeof (GtkToggleActionEntry) ); |
279 | memcpy ( myaction, action, sizeof (GtkToggleActionEntry) ); | |
280 | // Overwrite with specific callback | |
281 | myaction->callback = callback; | |
282 | vtb->list_of_toggles = g_slist_append(vtb->list_of_toggles, myaction); | |
283 | ||
284 | // Store override so it can be freed upon toolbar destruction | |
285 | toggle_overrides = g_slist_append ( toggle_overrides, myaction ); | |
286 | } | |
287 | ||
288 | /** | |
289 | * toolbar_action_entry_register: | |
290 | * | |
291 | * Register a standard action button in the specified toolbar | |
292 | */ | |
293 | void toolbar_action_entry_register(VikToolbar *vtb, GtkActionEntry *action) | |
50b4f4de | 294 | { |
59bc9e85 RN |
295 | g_return_if_fail(VIK_IS_TOOLBAR(vtb)); |
296 | g_return_if_fail(action != NULL); | |
297 | vtb->list_of_actions = g_slist_append(vtb->list_of_actions, action); | |
298 | } | |
50b4f4de | 299 | |
59bc9e85 RN |
300 | static void configure_cb (GtkWidget *widget, gpointer user_data) |
301 | { | |
302 | config_t *data = (config_t*)user_data; | |
303 | toolbar_configure ( data->vtb, data->vtb->widget, data->parent, data->vbox, data->hbox, data->reload_cb, data->user_data); | |
50b4f4de RN |
304 | } |
305 | ||
59bc9e85 RN |
306 | static gboolean toolbar_popup_menu (GtkWidget *widget, GdkEventButton *event, gpointer user_data) |
307 | { | |
308 | // Only display menu on right button clicks | |
309 | if (event->button == 3) { | |
310 | GtkWidget *tmenu; | |
311 | tmenu = gtk_menu_new(); | |
312 | GtkWidget *item = gtk_menu_item_new_with_mnemonic ( _("_Customize") ); | |
313 | g_signal_connect ( G_OBJECT(item), "activate", G_CALLBACK(configure_cb), user_data ); | |
314 | gtk_menu_shell_append ( GTK_MENU_SHELL(tmenu), item ); | |
315 | gtk_menu_popup ( GTK_MENU(tmenu), NULL, NULL, NULL, NULL, event->button, event->time ); | |
316 | gtk_widget_show_all ( GTK_WIDGET(tmenu) ); | |
317 | g_object_ref_sink (tmenu); | |
318 | return TRUE; | |
319 | } | |
320 | return FALSE; | |
321 | } | |
50b4f4de | 322 | |
59bc9e85 RN |
323 | /* sets the icon style of the toolbar */ |
324 | static void toolbar_set_icon_style (GtkWidget *toolbar) | |
50b4f4de | 325 | { |
59bc9e85 | 326 | gint icon_style = prefs_get_icon_style(); |
50b4f4de | 327 | |
59bc9e85 RN |
328 | if (icon_style == 0) |
329 | icon_style = ui_get_gtk_settings_integer("gtk-toolbar-style", GTK_TOOLBAR_ICONS); | |
330 | else | |
331 | // Adjust to enum GtkToolbarStyle | |
332 | icon_style--; | |
50b4f4de | 333 | |
59bc9e85 RN |
334 | gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), icon_style); |
335 | } | |
50b4f4de | 336 | |
50b4f4de | 337 | |
59bc9e85 RN |
338 | /* sets the icon size of the toolbar */ |
339 | static void toolbar_set_icon_size (GtkWidget *toolbar) | |
340 | { | |
341 | gint icon_size = prefs_get_icon_size(); | |
342 | ||
343 | if ( icon_size == 0 ) | |
344 | icon_size = ui_get_gtk_settings_integer("gtk-toolbar-icon-size", GTK_ICON_SIZE_SMALL_TOOLBAR); | |
345 | else { | |
346 | // Adjust to enum GtkIconSize | |
347 | if ( icon_size == 1 ) | |
348 | icon_size = GTK_ICON_SIZE_SMALL_TOOLBAR; | |
349 | else if ( icon_size == 2 ) | |
350 | icon_size = GTK_ICON_SIZE_LARGE_TOOLBAR; | |
351 | else if ( icon_size == 3 ) | |
352 | icon_size = GTK_ICON_SIZE_DND; | |
50b4f4de | 353 | } |
50b4f4de | 354 | |
59bc9e85 RN |
355 | gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), icon_size); |
356 | } | |
357 | ||
358 | /** | |
359 | * toolbar_apply_settings: | |
360 | * @vbox: Potential vertical container for the specified toolbar | |
361 | * @hbox: Potential horizontal container for the specified toolbar | |
362 | * @Reset: Specify if the toolbar should be reparented | |
363 | * (when called externally this should always be TRUE) | |
364 | * | |
365 | * Updates the specified toolbar with current setting values | |
366 | */ | |
367 | void toolbar_apply_settings(VikToolbar *vtb, | |
368 | GtkWidget *vbox, | |
369 | GtkWidget *hbox, | |
370 | gboolean reset) | |
371 | { | |
372 | g_return_if_fail(VIK_IS_TOOLBAR(vtb)); | |
373 | ||
374 | if ( reset ) { | |
375 | g_object_ref (vtb->widget); // ensure not deleted when removed | |
376 | // Try both places it could be | |
377 | if ( gtk_widget_get_parent (vtb->widget) == hbox ) | |
378 | gtk_container_remove(GTK_CONTAINER(hbox), vtb->widget ); | |
379 | if ( gtk_widget_get_parent (vtb->widget) == vbox ) | |
380 | gtk_container_remove(GTK_CONTAINER(vbox), vtb->widget ); | |
50b4f4de | 381 | } |
59bc9e85 RN |
382 | |
383 | toolbar_set_icon_style(vtb->widget); | |
384 | toolbar_set_icon_size(vtb->widget); | |
50b4f4de RN |
385 | |
386 | /* add the toolbar again to the main window */ | |
59bc9e85 RN |
387 | // Use reorder to ensure toolbar always comes after the menu |
388 | if (prefs_get_append_to_menu()) | |
50b4f4de | 389 | { |
59bc9e85 RN |
390 | if ( hbox ) { |
391 | gtk_box_pack_start(GTK_BOX(hbox), vtb->widget, TRUE, TRUE, 0); | |
392 | gtk_box_reorder_child(GTK_BOX(hbox), vtb->widget, 1); | |
393 | } | |
50b4f4de RN |
394 | } |
395 | else | |
396 | { | |
59bc9e85 RN |
397 | if ( vbox ) { |
398 | gtk_box_pack_start(GTK_BOX(vbox), vtb->widget, FALSE, TRUE, 0); | |
399 | gtk_box_reorder_child(GTK_BOX(vbox), vtb->widget, 1); | |
400 | } | |
50b4f4de | 401 | } |
59bc9e85 | 402 | } |
50b4f4de | 403 | |
59bc9e85 RN |
404 | /** |
405 | * toolbar_get_widget: | |
406 | * | |
407 | */ | |
408 | GtkWidget* toolbar_get_widget(VikToolbar *vtb) | |
409 | { | |
410 | g_return_val_if_fail(VIK_IS_TOOLBAR(vtb), NULL); | |
411 | return vtb->widget; | |
412 | } | |
413 | ||
414 | #include "toolbar.xml.h" | |
415 | static void toolbar_reload ( VikToolbar *vtb, | |
416 | const gchar *markup, | |
417 | GtkWindow *parent, | |
418 | GtkWidget *vbox, | |
419 | GtkWidget *hbox, | |
420 | ReloadCB reload_cb, | |
421 | gpointer user_data ) | |
422 | { | |
423 | GError *error = NULL; | |
424 | g_debug ( "%s: %d", __FUNCTION__, g_hash_table_size(signal_data) ); | |
425 | ||
426 | /* Cleanup old toolbar */ | |
427 | if (vtb->merge_id > 0) | |
50b4f4de | 428 | { |
59bc9e85 RN |
429 | /* Get rid of it! */ |
430 | gtk_widget_destroy(vtb->widget); | |
431 | ||
432 | gtk_ui_manager_remove_ui(vtb->uim, vtb->merge_id); | |
433 | gtk_ui_manager_ensure_update(vtb->uim); | |
434 | ||
435 | g_hash_table_remove ( signal_data, vtb ); | |
50b4f4de | 436 | } |
59bc9e85 RN |
437 | |
438 | if (markup != NULL) | |
50b4f4de | 439 | { |
59bc9e85 | 440 | vtb->merge_id = gtk_ui_manager_add_ui_from_string(vtb->uim, markup, -1, &error); |
50b4f4de | 441 | } |
59bc9e85 | 442 | else |
50b4f4de | 443 | { |
59bc9e85 RN |
444 | gchar *filename = NULL; |
445 | /* Load the toolbar UI XML file from disk */ | |
446 | // Consider using a_get_viking_data_path() first | |
447 | filename = g_build_filename (a_get_viking_dir(), "ui_toolbar.xml", NULL); | |
448 | vtb->merge_id = gtk_ui_manager_add_ui_from_file(vtb->uim, filename, &error); | |
449 | g_free(filename); | |
50b4f4de | 450 | } |
59bc9e85 | 451 | if (error != NULL) |
50b4f4de | 452 | { |
59bc9e85 RN |
453 | g_debug("UI creation failed, using internal fallback definition. Error message: %s", error->message); |
454 | g_error_free(error); | |
455 | error = NULL; | |
456 | ||
457 | /* finally load the internally defined markup as fallback */ | |
458 | vtb->merge_id = gtk_ui_manager_add_ui_from_string(vtb->uim, toolbar_xml, -1, &error); | |
459 | if (error) { | |
460 | // Abort - this should only happen if you're missing around with the code | |
461 | g_error("Internal UI creation failed. Error message: %s", error->message); | |
462 | } | |
463 | ||
50b4f4de | 464 | } |
59bc9e85 | 465 | vtb->widget = gtk_ui_manager_get_widget(vtb->uim, "/ui/MainToolbar"); |
50b4f4de RN |
466 | |
467 | /* update button states */ | |
59bc9e85 | 468 | reload_cb ( vtb->group_actions, user_data ); |
50b4f4de | 469 | |
59bc9e85 | 470 | toolbar_apply_settings(vtb, vbox, hbox, FALSE); |
50b4f4de | 471 | |
59bc9e85 | 472 | gtk_widget_show(vtb->widget); |
50b4f4de RN |
473 | |
474 | /* Signals */ | |
59bc9e85 RN |
475 | config_t *data = g_malloc(sizeof(config_t)); |
476 | data->vtb = vtb; | |
477 | data->parent = parent; | |
478 | data->vbox = vbox; | |
479 | data->hbox = hbox; | |
480 | data->reload_cb = reload_cb; | |
481 | data->user_data = user_data; | |
482 | ||
483 | // Store data in a hash so it can be freed when the toolbar is reconfigured | |
484 | g_hash_table_insert (signal_data, vtb, data); | |
485 | ||
486 | g_signal_connect(vtb->widget, "button-press-event", G_CALLBACK(toolbar_popup_menu), data); | |
50b4f4de RN |
487 | |
488 | /* We don't need to disconnect those signals as this is done automatically when the entry | |
489 | * widgets are destroyed, happens when the toolbar itself is destroyed. */ | |
50b4f4de RN |
490 | } |
491 | ||
50b4f4de RN |
492 | static void toolbar_notify_style_cb(GObject *object, GParamSpec *arg1, gpointer data) |
493 | { | |
494 | const gchar *arg_name = g_param_spec_get_name(arg1); | |
495 | gint value; | |
496 | ||
59bc9e85 | 497 | if (prefs_get_icon_style() == 0 && !g_strcmp0(arg_name, "gtk-toolbar-style")) |
50b4f4de | 498 | { |
59bc9e85 RN |
499 | value = ui_get_gtk_settings_integer(arg_name, GTK_TOOLBAR_ICONS); |
500 | if ( GTK_IS_TOOLBAR (data) ) | |
501 | gtk_toolbar_set_style(GTK_TOOLBAR(data), value); | |
50b4f4de | 502 | } |
59bc9e85 | 503 | else if (prefs_get_icon_size() == 0 && !g_strcmp0(arg_name, "gtk-toolbar-size")) |
50b4f4de | 504 | { |
59bc9e85 RN |
505 | value = ui_get_gtk_settings_integer(arg_name, GTK_ICON_SIZE_SMALL_TOOLBAR); |
506 | if ( GTK_IS_TOOLBAR (data) ) | |
507 | gtk_toolbar_set_icon_size(GTK_TOOLBAR(data), value); | |
50b4f4de RN |
508 | } |
509 | } | |
510 | ||
59bc9e85 RN |
511 | /** |
512 | * toolbar_init: | |
513 | * | |
514 | * Initialize the specified toolbar using the given values | |
515 | */ | |
516 | void toolbar_init (VikToolbar *vtb, | |
517 | GtkWindow *parent, | |
518 | GtkWidget *vbox, | |
519 | GtkWidget *hbox, | |
520 | ToolCB tool_cb, | |
521 | ReloadCB reload_cb, | |
522 | gpointer user_data) | |
50b4f4de | 523 | { |
59bc9e85 RN |
524 | vtb->uim = gtk_ui_manager_new(); |
525 | ||
526 | vtb->group_actions = gtk_action_group_new("MainToolbar"); | |
527 | gtk_action_group_set_translation_domain(vtb->group_actions, GETTEXT_PACKAGE); | |
528 | GtkActionEntry *actions = NULL; | |
529 | GSList *gl; | |
530 | gint nn = 0; | |
531 | foreach_slist(gl, vtb->list_of_actions) { | |
532 | GtkActionEntry *action = gl->data; | |
533 | actions = g_renew(GtkActionEntry, actions, nn+1); | |
534 | actions[nn] = *action; | |
535 | nn++; | |
50b4f4de | 536 | } |
59bc9e85 RN |
537 | gtk_action_group_add_actions(vtb->group_actions, actions, nn, user_data); |
538 | gtk_ui_manager_insert_action_group(vtb->uim, vtb->group_actions, 0); | |
539 | ||
540 | vtb->group_toggles = gtk_action_group_new("UIItems"); | |
541 | gtk_action_group_set_translation_domain(vtb->group_toggles, GETTEXT_PACKAGE); | |
542 | GtkToggleActionEntry *toggle_actions = NULL; | |
543 | nn = 0; | |
544 | foreach_slist(gl, vtb->list_of_toggles) { | |
545 | GtkToggleActionEntry *action = gl->data; | |
546 | toggle_actions = g_renew(GtkToggleActionEntry, toggle_actions, nn+1); | |
547 | toggle_actions[nn] = *action; | |
548 | nn++; | |
50b4f4de | 549 | } |
59bc9e85 RN |
550 | gtk_action_group_add_toggle_actions(vtb->group_toggles, toggle_actions, nn, user_data); |
551 | gtk_ui_manager_insert_action_group(vtb->uim, vtb->group_toggles, 0); | |
552 | ||
553 | vtb->group_tools = gtk_action_group_new("ToolItems"); | |
554 | gtk_action_group_set_translation_domain(vtb->group_tools, GETTEXT_PACKAGE); | |
555 | ||
556 | GtkRadioActionEntry *tool_actions = NULL; | |
557 | nn = 0; | |
558 | foreach_slist(gl, vtb->list_of_tools) { | |
559 | GtkRadioActionEntry *action = gl->data; | |
560 | tool_actions = g_renew(GtkRadioActionEntry, tool_actions, nn+1); | |
561 | tool_actions[nn] = *action; | |
562 | tool_actions[nn].value = nn; | |
563 | nn++; | |
50b4f4de | 564 | } |
59bc9e85 RN |
565 | gtk_action_group_add_radio_actions(vtb->group_tools, tool_actions, nn, 0, G_CALLBACK(tool_cb), user_data); |
566 | gtk_ui_manager_insert_action_group(vtb->uim, vtb->group_tools, 0); | |
567 | ||
568 | vtb->group_modes = gtk_action_group_new("ModeItems"); | |
569 | gtk_action_group_set_translation_domain(vtb->group_modes, GETTEXT_PACKAGE); | |
570 | ||
571 | GtkRadioActionEntry *mode_actions = NULL; | |
572 | nn = 0; | |
573 | foreach_slist(gl, vtb->list_of_modes) { | |
574 | GtkRadioActionEntry *action = gl->data; | |
575 | mode_actions = g_renew(GtkRadioActionEntry, mode_actions, nn+1); | |
576 | mode_actions[nn] = *action; | |
577 | mode_actions[nn].value = nn; | |
578 | nn++; | |
50b4f4de | 579 | } |
59bc9e85 RN |
580 | gtk_action_group_add_radio_actions(vtb->group_modes, mode_actions, nn, 0, G_CALLBACK(tool_cb), user_data); |
581 | gtk_ui_manager_insert_action_group(vtb->uim, vtb->group_modes, 0); | |
50b4f4de | 582 | |
59bc9e85 | 583 | toolbar_reload(vtb, NULL, parent, vbox, hbox, reload_cb, user_data); |
50b4f4de | 584 | |
59bc9e85 RN |
585 | #if GTK_CHECK_VERSION(3, 0, 0) |
586 | gtk_style_context_add_class(gtk_widget_get_style_context(vtb->widget), "primary-toolbar"); | |
587 | #endif | |
50b4f4de | 588 | |
59bc9e85 RN |
589 | GtkSettings *gtk_settings = gtk_widget_get_settings(vtb->widget); |
590 | if (gtk_settings != NULL) | |
591 | { | |
592 | g_signal_connect(gtk_settings, "notify::gtk-toolbar-style", | |
593 | G_CALLBACK(toolbar_notify_style_cb), vtb->widget); | |
50b4f4de | 594 | } |
50b4f4de | 595 | |
59bc9e85 RN |
596 | extra_widget_data.vtb = vtb; |
597 | extra_widget_data.parent = parent; | |
598 | extra_widget_data.vbox = vbox; | |
599 | extra_widget_data.reload_cb = reload_cb; | |
600 | extra_widget_data.user_data = user_data; | |
601 | } | |
50b4f4de | 602 | |
59bc9e85 RN |
603 | /** |
604 | * toolbar_action_set_sensitive: | |
50b4f4de | 605 | * |
59bc9e85 | 606 | * Set sensitivity of a particular action |
50b4f4de | 607 | */ |
59bc9e85 | 608 | void toolbar_action_set_sensitive (VikToolbar *vtb, const gchar *name, gboolean sensitive) |
50b4f4de | 609 | { |
59bc9e85 RN |
610 | g_return_if_fail(VIK_IS_TOOLBAR(vtb)); |
611 | g_return_if_fail(name != NULL); | |
612 | // Try all groups | |
613 | GtkAction *action = get_action ( vtb, name ); | |
614 | if ( action ) | |
615 | g_object_set ( action, "sensitive", sensitive, NULL); | |
50b4f4de RN |
616 | } |
617 | ||
59bc9e85 RN |
618 | /** |
619 | * vik_toolbar_finalize: | |
620 | * | |
621 | * Memory cleanups upon toolbar destruction | |
622 | */ | |
623 | void vik_toolbar_finalize ( VikToolbar *vtb ) | |
50b4f4de | 624 | { |
59bc9e85 | 625 | g_hash_table_remove ( signal_data, vtb ); |
50b4f4de RN |
626 | |
627 | /* unref'ing the GtkUIManager object will destroy all its widgets unless they were ref'ed */ | |
59bc9e85 RN |
628 | g_object_unref(vtb->uim); |
629 | g_object_unref(vtb->group_actions); | |
630 | g_object_unref(vtb->group_tools); | |
631 | g_object_unref(vtb->group_toggles); | |
632 | g_object_unref(vtb->group_modes); | |
633 | ||
634 | g_slist_free(vtb->list_of_actions); | |
635 | g_slist_free(vtb->list_of_tools); | |
636 | g_slist_free(vtb->list_of_toggles); | |
637 | g_slist_free(vtb->list_of_modes); | |
50b4f4de RN |
638 | } |
639 | ||
640 | ||
641 | #define TB_EDITOR_SEPARATOR _("Separator") | |
642 | #define TB_EDITOR_SEPARATOR_LABEL _("--- Separator ---") | |
643 | typedef struct | |
644 | { | |
645 | GtkWidget *dialog; | |
646 | ||
647 | GtkTreeView *tree_available; | |
648 | GtkTreeView *tree_used; | |
649 | ||
650 | GtkListStore *store_available; | |
651 | GtkListStore *store_used; | |
652 | ||
653 | GtkTreePath *last_drag_path; | |
654 | GtkTreeViewDropPosition last_drag_pos; | |
655 | ||
656 | GtkWidget *drag_source; | |
59bc9e85 RN |
657 | |
658 | config_t config; | |
50b4f4de RN |
659 | } TBEditorWidget; |
660 | ||
661 | static const GtkTargetEntry tb_editor_dnd_targets[] = | |
662 | { | |
59bc9e85 | 663 | { "VIKING_TB_EDITOR_ROW", 0, 0 } |
50b4f4de RN |
664 | }; |
665 | static const gint tb_editor_dnd_targets_len = G_N_ELEMENTS(tb_editor_dnd_targets); | |
666 | ||
667 | enum | |
668 | { | |
669 | TB_EDITOR_COL_ACTION, | |
670 | TB_EDITOR_COL_LABEL, | |
671 | TB_EDITOR_COL_ICON, | |
672 | TB_EDITOR_COLS_MAX | |
673 | }; | |
674 | ||
675 | static void tb_editor_handler_start_element(GMarkupParseContext *context, const gchar *element_name, | |
676 | const gchar **attribute_names, | |
677 | const gchar **attribute_values, gpointer data, | |
678 | GError **error) | |
679 | { | |
680 | gint i; | |
681 | GSList **actions = data; | |
682 | ||
683 | /* This is very basic parsing, stripped down any error checking, requires a valid UI markup. */ | |
59bc9e85 | 684 | if (!g_strcmp0(element_name, "separator")) |
50b4f4de RN |
685 | *actions = g_slist_append(*actions, g_strdup(TB_EDITOR_SEPARATOR)); |
686 | ||
687 | for (i = 0; attribute_names[i] != NULL; i++) | |
688 | { | |
59bc9e85 | 689 | if (!g_strcmp0(attribute_names[i], "action")) |
50b4f4de RN |
690 | { |
691 | *actions = g_slist_append(*actions, g_strdup(attribute_values[i])); | |
692 | } | |
693 | } | |
694 | } | |
695 | ||
696 | ||
697 | static const GMarkupParser tb_editor_xml_parser = | |
698 | { | |
699 | tb_editor_handler_start_element, NULL, NULL, NULL, NULL | |
700 | }; | |
701 | ||
702 | ||
703 | static GSList *tb_editor_parse_ui(const gchar *buffer, gssize length, GError **error) | |
704 | { | |
705 | GMarkupParseContext *context; | |
706 | GSList *list = NULL; | |
707 | ||
708 | context = g_markup_parse_context_new(&tb_editor_xml_parser, 0, &list, NULL); | |
709 | g_markup_parse_context_parse(context, buffer, length, error); | |
710 | g_markup_parse_context_free(context); | |
711 | ||
712 | return list; | |
713 | } | |
714 | ||
715 | ||
59bc9e85 | 716 | static void tb_editor_set_item_values(VikToolbar *vtb, const gchar *name, GtkListStore *store, GtkTreeIter *iter) |
50b4f4de RN |
717 | { |
718 | gchar *icon = NULL; | |
719 | gchar *label = NULL; | |
720 | gchar *label_clean = NULL; | |
50b4f4de | 721 | |
59bc9e85 RN |
722 | // Tries all action groups |
723 | GtkAction *action = get_action ( vtb, name ); | |
724 | ||
725 | if (action == NULL) { | |
726 | if (!g_strcmp0(name, TB_EDITOR_SEPARATOR)) | |
50b4f4de RN |
727 | label_clean = g_strdup(TB_EDITOR_SEPARATOR_LABEL); |
728 | else | |
729 | return; | |
730 | } | |
59bc9e85 | 731 | if (action != NULL) { |
50b4f4de RN |
732 | g_object_get(action, "icon-name", &icon, NULL); |
733 | if (icon == NULL) | |
734 | g_object_get(action, "stock-id", &icon, NULL); | |
735 | ||
736 | g_object_get(action, "label", &label, NULL); | |
737 | if (label != NULL) | |
59bc9e85 | 738 | label_clean = util_str_remove_chars(g_strdup(label), "_"); |
50b4f4de RN |
739 | } |
740 | ||
741 | gtk_list_store_set(store, iter, | |
742 | TB_EDITOR_COL_ACTION, name, | |
743 | TB_EDITOR_COL_LABEL, label_clean, | |
744 | TB_EDITOR_COL_ICON, icon, | |
745 | -1); | |
746 | ||
747 | g_free(icon); | |
748 | g_free(label); | |
749 | g_free(label_clean); | |
750 | } | |
751 | ||
752 | ||
753 | static void tb_editor_scroll_to_iter(GtkTreeView *treeview, GtkTreeIter *iter) | |
754 | { | |
755 | GtkTreePath *path = gtk_tree_model_get_path(gtk_tree_view_get_model(treeview), iter); | |
756 | gtk_tree_view_scroll_to_cell(treeview, path, NULL, TRUE, 0.5, 0.0); | |
757 | gtk_tree_path_free(path); | |
758 | } | |
759 | ||
760 | ||
761 | static void tb_editor_free_path(TBEditorWidget *tbw) | |
762 | { | |
763 | if (tbw->last_drag_path != NULL) | |
764 | { | |
765 | gtk_tree_path_free(tbw->last_drag_path); | |
766 | tbw->last_drag_path = NULL; | |
767 | } | |
768 | } | |
769 | ||
770 | ||
771 | static void tb_editor_btn_remove_clicked_cb(GtkWidget *button, TBEditorWidget *tbw) | |
772 | { | |
773 | GtkTreeModel *model_used; | |
774 | GtkTreeSelection *selection_used; | |
775 | GtkTreeIter iter_used, iter_new; | |
776 | gchar *action_name; | |
777 | ||
778 | selection_used = gtk_tree_view_get_selection(tbw->tree_used); | |
779 | if (gtk_tree_selection_get_selected(selection_used, &model_used, &iter_used)) | |
780 | { | |
781 | gtk_tree_model_get(model_used, &iter_used, TB_EDITOR_COL_ACTION, &action_name, -1); | |
782 | if (gtk_list_store_remove(tbw->store_used, &iter_used)) | |
783 | gtk_tree_selection_select_iter(selection_used, &iter_used); | |
784 | ||
59bc9e85 | 785 | if (g_strcmp0(action_name, TB_EDITOR_SEPARATOR)) |
50b4f4de RN |
786 | { |
787 | gtk_list_store_append(tbw->store_available, &iter_new); | |
59bc9e85 | 788 | tb_editor_set_item_values(tbw->config.vtb, action_name, tbw->store_available, &iter_new); |
50b4f4de RN |
789 | tb_editor_scroll_to_iter(tbw->tree_available, &iter_new); |
790 | } | |
791 | ||
792 | g_free(action_name); | |
793 | } | |
794 | } | |
795 | ||
796 | ||
797 | static void tb_editor_btn_add_clicked_cb(GtkWidget *button, TBEditorWidget *tbw) | |
798 | { | |
799 | GtkTreeModel *model_available; | |
800 | GtkTreeSelection *selection_available, *selection_used; | |
801 | GtkTreeIter iter_available, iter_new, iter_selected; | |
802 | gchar *action_name; | |
803 | ||
804 | selection_available = gtk_tree_view_get_selection(tbw->tree_available); | |
805 | if (gtk_tree_selection_get_selected(selection_available, &model_available, &iter_available)) | |
806 | { | |
807 | gtk_tree_model_get(model_available, &iter_available, | |
808 | TB_EDITOR_COL_ACTION, &action_name, -1); | |
59bc9e85 | 809 | if (g_strcmp0(action_name, TB_EDITOR_SEPARATOR)) |
50b4f4de RN |
810 | { |
811 | if (gtk_list_store_remove(tbw->store_available, &iter_available)) | |
812 | gtk_tree_selection_select_iter(selection_available, &iter_available); | |
813 | } | |
814 | ||
815 | selection_used = gtk_tree_view_get_selection(tbw->tree_used); | |
816 | if (gtk_tree_selection_get_selected(selection_used, NULL, &iter_selected)) | |
817 | gtk_list_store_insert_before(tbw->store_used, &iter_new, &iter_selected); | |
818 | else | |
819 | gtk_list_store_append(tbw->store_used, &iter_new); | |
820 | ||
59bc9e85 | 821 | tb_editor_set_item_values(tbw->config.vtb, action_name, tbw->store_used, &iter_new); |
50b4f4de RN |
822 | tb_editor_scroll_to_iter(tbw->tree_used, &iter_new); |
823 | ||
824 | g_free(action_name); | |
825 | } | |
826 | } | |
827 | ||
828 | ||
829 | static gboolean tb_editor_drag_motion_cb(GtkWidget *widget, GdkDragContext *drag_context, | |
59bc9e85 | 830 | gint x, gint y, guint ltime, TBEditorWidget *tbw) |
50b4f4de RN |
831 | { |
832 | if (tbw->last_drag_path != NULL) | |
833 | gtk_tree_path_free(tbw->last_drag_path); | |
834 | gtk_tree_view_get_drag_dest_row(GTK_TREE_VIEW(widget), | |
835 | &(tbw->last_drag_path), &(tbw->last_drag_pos)); | |
836 | ||
837 | return FALSE; | |
838 | } | |
839 | ||
840 | ||
841 | static void tb_editor_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context, | |
59bc9e85 RN |
842 | GtkSelectionData *data, guint info, guint ltime, |
843 | TBEditorWidget *tbw) | |
50b4f4de RN |
844 | { |
845 | GtkTreeIter iter; | |
846 | GtkTreeSelection *selection; | |
847 | GtkTreeModel *model; | |
848 | GdkAtom atom; | |
849 | gchar *name; | |
850 | ||
851 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); | |
852 | if (! gtk_tree_selection_get_selected(selection, &model, &iter)) | |
853 | return; | |
854 | ||
855 | gtk_tree_model_get(model, &iter, TB_EDITOR_COL_ACTION, &name, -1); | |
856 | if (G_UNLIKELY(EMPTY(name))) | |
857 | { | |
858 | g_free(name); | |
859 | return; | |
860 | } | |
861 | ||
862 | atom = gdk_atom_intern(tb_editor_dnd_targets[0].target, FALSE); | |
863 | gtk_selection_data_set(data, atom, 8, (guchar*) name, strlen(name)); | |
864 | ||
865 | g_free(name); | |
866 | ||
867 | tbw->drag_source = widget; | |
868 | } | |
869 | ||
870 | ||
871 | static void tb_editor_drag_data_rcvd_cb(GtkWidget *widget, GdkDragContext *context, | |
872 | gint x, gint y, GtkSelectionData *data, guint info, | |
873 | guint ltime, TBEditorWidget *tbw) | |
874 | { | |
875 | GtkTreeView *tree = GTK_TREE_VIEW(widget); | |
876 | gboolean del = FALSE; | |
877 | ||
878 | if (gtk_selection_data_get_length(data) >= 0 && gtk_selection_data_get_format(data) == 8) | |
879 | { | |
880 | gboolean is_sep; | |
881 | gchar *text = NULL; | |
882 | ||
883 | text = (gchar*) gtk_selection_data_get_data(data); | |
59bc9e85 | 884 | is_sep = !g_strcmp0(text, TB_EDITOR_SEPARATOR); |
50b4f4de RN |
885 | /* If the source of the action is equal to the target, we do just re-order and so need |
886 | * to delete the separator to get it moved, not just copied. */ | |
887 | if (is_sep && widget == tbw->drag_source) | |
888 | is_sep = FALSE; | |
889 | ||
890 | if (tree != tbw->tree_available || ! is_sep) | |
891 | { | |
892 | GtkTreeIter iter, iter_before, *iter_before_ptr; | |
893 | GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree)); | |
894 | ||
895 | if (tbw->last_drag_path != NULL) | |
896 | { | |
897 | gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter_before, tbw->last_drag_path); | |
898 | ||
899 | if (gtk_list_store_iter_is_valid(store, &iter_before)) | |
900 | iter_before_ptr = &iter_before; | |
901 | else | |
902 | iter_before_ptr = NULL; | |
903 | ||
904 | if (tbw->last_drag_pos == GTK_TREE_VIEW_DROP_BEFORE || | |
905 | tbw->last_drag_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) | |
906 | gtk_list_store_insert_before(store, &iter, iter_before_ptr); | |
907 | else | |
908 | gtk_list_store_insert_after(store, &iter, iter_before_ptr); | |
909 | } | |
910 | else | |
911 | gtk_list_store_append(store, &iter); | |
912 | ||
59bc9e85 | 913 | tb_editor_set_item_values(tbw->config.vtb, text, store, &iter); |
50b4f4de RN |
914 | tb_editor_scroll_to_iter(tree, &iter); |
915 | } | |
916 | if (tree != tbw->tree_used || ! is_sep) | |
917 | del = TRUE; | |
918 | } | |
919 | ||
920 | tbw->drag_source = NULL; /* reset the value just to be sure */ | |
921 | tb_editor_free_path(tbw); | |
922 | gtk_drag_finish(context, TRUE, del, ltime); | |
923 | } | |
924 | ||
925 | ||
926 | static gboolean tb_editor_foreach_used(GtkTreeModel *model, GtkTreePath *path, | |
59bc9e85 | 927 | GtkTreeIter *iter, gpointer data) |
50b4f4de RN |
928 | { |
929 | gchar *action_name; | |
930 | ||
931 | gtk_tree_model_get(model, iter, TB_EDITOR_COL_ACTION, &action_name, -1); | |
932 | ||
59bc9e85 | 933 | if (!g_strcmp0(action_name, TB_EDITOR_SEPARATOR)) |
50b4f4de RN |
934 | g_string_append_printf(data, "\t\t<separator/>\n"); |
935 | else if (G_LIKELY(!EMPTY(action_name))) | |
936 | g_string_append_printf(data, "\t\t<toolitem action='%s' />\n", action_name); | |
937 | ||
938 | g_free(action_name); | |
939 | return FALSE; | |
940 | } | |
941 | ||
942 | ||
943 | static void tb_editor_write_markup(TBEditorWidget *tbw) | |
944 | { | |
945 | /* <ui> must be the first tag, otherwise gtk_ui_manager_add_ui_from_string() will fail. */ | |
946 | const gchar *template = "<ui>\n<!--\n\ | |
59bc9e85 | 947 | This is Viking's toolbar UI definition.\nThe DTD can be found at \n\ |
50b4f4de | 948 | http://library.gnome.org/devel/gtk/stable/GtkUIManager.html#GtkUIManager.description.\n\n\ |
59bc9e85 | 949 | Generally one should use the toolbar editor in Viking rather than editing this file.\n\n\ |
7bf8e898 | 950 | For manual changes to this file to take effect, you need to restart Viking.\n-->\n\ |
59bc9e85 | 951 | \t<toolbar name='MainToolbar'>\n"; |
50b4f4de RN |
952 | GString *str = g_string_new(template); |
953 | ||
954 | gtk_tree_model_foreach(GTK_TREE_MODEL(tbw->store_used), tb_editor_foreach_used, str); | |
955 | ||
59bc9e85 | 956 | g_string_append(str, "\t</toolbar>\n</ui>\n"); |
50b4f4de | 957 | |
59bc9e85 RN |
958 | toolbar_reload(tbw->config.vtb, |
959 | str->str, | |
960 | tbw->config.parent, | |
961 | tbw->config.vbox, | |
962 | tbw->config.hbox, | |
963 | tbw->config.reload_cb, | |
964 | tbw->config.user_data); | |
50b4f4de | 965 | |
59bc9e85 RN |
966 | // ATM always save the toolbar when changed |
967 | gchar *filename = g_build_filename(a_get_viking_dir (), "ui_toolbar.xml", NULL); | |
968 | GError *error = NULL; | |
969 | if (! g_file_set_contents(filename, str->str, -1, &error)) { | |
970 | g_warning ("%s: could not write to file %s (%s)", __FUNCTION__, filename, error->message); | |
971 | g_error_free(error); | |
972 | } | |
50b4f4de RN |
973 | g_free(filename); |
974 | ||
975 | g_string_free(str, TRUE); | |
976 | } | |
977 | ||
978 | ||
979 | static void tb_editor_available_items_changed_cb(GtkTreeModel *model, GtkTreePath *arg1, | |
980 | GtkTreeIter *arg2, TBEditorWidget *tbw) | |
981 | { | |
982 | tb_editor_write_markup(tbw); | |
983 | } | |
984 | ||
985 | ||
986 | static void tb_editor_available_items_deleted_cb(GtkTreeModel *model, GtkTreePath *arg1, | |
987 | TBEditorWidget *tbw) | |
988 | { | |
989 | tb_editor_write_markup(tbw); | |
990 | } | |
991 | ||
992 | ||
59bc9e85 | 993 | static TBEditorWidget *tb_editor_create_dialog(VikToolbar *vtb, GtkWindow *parent, GtkWidget *toolbar, GtkWidget *vbox, GtkWidget *menu_hbox, ReloadCB reload_cb, gpointer user_data) |
50b4f4de | 994 | { |
59bc9e85 | 995 | GtkWidget *dialog, *hbox, *vbox_buttons, *button_add, *button_remove; |
50b4f4de RN |
996 | GtkWidget *swin_available, *swin_used, *tree_available, *tree_used, *label; |
997 | GtkCellRenderer *text_renderer, *icon_renderer; | |
998 | GtkTreeViewColumn *column; | |
50b4f4de | 999 | |
59bc9e85 RN |
1000 | if (parent == NULL) { |
1001 | g_warning ( "No parent" ); | |
1002 | return NULL; | |
1003 | } | |
1004 | ||
1005 | TBEditorWidget *tbw = g_new(TBEditorWidget, 1); | |
50b4f4de RN |
1006 | |
1007 | dialog = gtk_dialog_new_with_buttons(_("Customize Toolbar"), | |
59bc9e85 RN |
1008 | GTK_WINDOW(parent), |
1009 | GTK_DIALOG_DESTROY_WITH_PARENT, | |
1010 | GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); | |
1011 | gtk_widget_set_name(dialog, "VikingDialog"); | |
50b4f4de RN |
1012 | gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 400); |
1013 | gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE); | |
1014 | ||
1015 | tbw->store_available = gtk_list_store_new(TB_EDITOR_COLS_MAX, | |
1016 | G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); | |
1017 | tbw->store_used = gtk_list_store_new(TB_EDITOR_COLS_MAX, | |
1018 | G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); | |
1019 | ||
59bc9e85 RN |
1020 | tbw->config.vtb = vtb; |
1021 | tbw->config.parent = parent; | |
1022 | tbw->config.vbox = vbox; | |
1023 | tbw->config.hbox = menu_hbox; | |
1024 | tbw->config.reload_cb = reload_cb; | |
1025 | tbw->config.user_data = user_data; | |
1026 | ||
50b4f4de RN |
1027 | label = gtk_label_new( |
1028 | _("Select items to be displayed on the toolbar. Items can be reordered by drag and drop.")); | |
1029 | gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); | |
1030 | ||
1031 | tree_available = gtk_tree_view_new(); | |
1032 | gtk_tree_view_set_model(GTK_TREE_VIEW(tree_available), GTK_TREE_MODEL(tbw->store_available)); | |
1033 | gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_available), TRUE); | |
1034 | gtk_tree_sortable_set_sort_column_id( | |
1035 | GTK_TREE_SORTABLE(tbw->store_available), TB_EDITOR_COL_LABEL, GTK_SORT_ASCENDING); | |
1036 | ||
1037 | icon_renderer = gtk_cell_renderer_pixbuf_new(); | |
1038 | column = gtk_tree_view_column_new_with_attributes( | |
1039 | NULL, icon_renderer, "stock-id", TB_EDITOR_COL_ICON, NULL); | |
1040 | gtk_tree_view_append_column(GTK_TREE_VIEW(tree_available), column); | |
1041 | ||
1042 | text_renderer = gtk_cell_renderer_text_new(); | |
1043 | column = gtk_tree_view_column_new_with_attributes( | |
1044 | _("Available Items"), text_renderer, "text", TB_EDITOR_COL_LABEL, NULL); | |
1045 | gtk_tree_view_append_column(GTK_TREE_VIEW(tree_available), column); | |
1046 | ||
1047 | swin_available = gtk_scrolled_window_new(NULL, NULL); | |
1048 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin_available), | |
1049 | GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); | |
1050 | gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin_available), GTK_SHADOW_ETCHED_IN); | |
1051 | gtk_container_add(GTK_CONTAINER(swin_available), tree_available); | |
1052 | ||
1053 | tree_used = gtk_tree_view_new(); | |
1054 | gtk_tree_view_set_model(GTK_TREE_VIEW(tree_used), GTK_TREE_MODEL(tbw->store_used)); | |
1055 | gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_used), TRUE); | |
1056 | gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tree_used), TRUE); | |
1057 | ||
1058 | icon_renderer = gtk_cell_renderer_pixbuf_new(); | |
1059 | column = gtk_tree_view_column_new_with_attributes( | |
1060 | NULL, icon_renderer, "stock-id", TB_EDITOR_COL_ICON, NULL); | |
1061 | gtk_tree_view_append_column(GTK_TREE_VIEW(tree_used), column); | |
1062 | ||
1063 | text_renderer = gtk_cell_renderer_text_new(); | |
1064 | column = gtk_tree_view_column_new_with_attributes( | |
1065 | _("Displayed Items"), text_renderer, "text", TB_EDITOR_COL_LABEL, NULL); | |
1066 | gtk_tree_view_append_column(GTK_TREE_VIEW(tree_used), column); | |
1067 | ||
1068 | swin_used = gtk_scrolled_window_new(NULL, NULL); | |
1069 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin_used), | |
1070 | GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); | |
1071 | gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin_used), GTK_SHADOW_ETCHED_IN); | |
1072 | gtk_container_add(GTK_CONTAINER(swin_used), tree_used); | |
1073 | ||
1074 | /* drag'n'drop */ | |
1075 | gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(tree_available), GDK_BUTTON1_MASK, | |
1076 | tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE); | |
1077 | gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree_available), | |
1078 | tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE); | |
1079 | g_signal_connect(tree_available, "drag-data-get", | |
1080 | G_CALLBACK(tb_editor_drag_data_get_cb), tbw); | |
1081 | g_signal_connect(tree_available, "drag-data-received", | |
1082 | G_CALLBACK(tb_editor_drag_data_rcvd_cb), tbw); | |
1083 | g_signal_connect(tree_available, "drag-motion", | |
1084 | G_CALLBACK(tb_editor_drag_motion_cb), tbw); | |
1085 | ||
1086 | gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(tree_used), GDK_BUTTON1_MASK, | |
1087 | tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE); | |
1088 | gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree_used), | |
1089 | tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE); | |
1090 | g_signal_connect(tree_used, "drag-data-get", | |
1091 | G_CALLBACK(tb_editor_drag_data_get_cb), tbw); | |
1092 | g_signal_connect(tree_used, "drag-data-received", | |
1093 | G_CALLBACK(tb_editor_drag_data_rcvd_cb), tbw); | |
1094 | g_signal_connect(tree_used, "drag-motion", | |
1095 | G_CALLBACK(tb_editor_drag_motion_cb), tbw); | |
1096 | ||
1097 | ||
1098 | button_add = ui_button_new_with_image(GTK_STOCK_GO_FORWARD, NULL); | |
1099 | button_remove = ui_button_new_with_image(GTK_STOCK_GO_BACK, NULL); | |
1100 | g_signal_connect(button_add, "clicked", G_CALLBACK(tb_editor_btn_add_clicked_cb), tbw); | |
1101 | g_signal_connect(button_remove, "clicked", G_CALLBACK(tb_editor_btn_remove_clicked_cb), tbw); | |
1102 | ||
1103 | vbox_buttons = gtk_vbox_new(FALSE, 6); | |
1104 | /* FIXME this is a little hack'ish, any better ideas? */ | |
1105 | gtk_box_pack_start(GTK_BOX(vbox_buttons), gtk_label_new(""), TRUE, TRUE, 0); | |
1106 | gtk_box_pack_start(GTK_BOX(vbox_buttons), button_add, FALSE, FALSE, 0); | |
1107 | gtk_box_pack_start(GTK_BOX(vbox_buttons), button_remove, FALSE, FALSE, 0); | |
1108 | gtk_box_pack_start(GTK_BOX(vbox_buttons), gtk_label_new(""), TRUE, TRUE, 0); | |
1109 | ||
1110 | hbox = gtk_hbox_new(FALSE, 6); | |
1111 | gtk_box_pack_start(GTK_BOX(hbox), swin_available, TRUE, TRUE, 0); | |
1112 | gtk_box_pack_start(GTK_BOX(hbox), vbox_buttons, FALSE, FALSE, 0); | |
1113 | gtk_box_pack_start(GTK_BOX(hbox), swin_used, TRUE, TRUE, 0); | |
1114 | ||
59bc9e85 RN |
1115 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, FALSE, 6); |
1116 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0); | |
50b4f4de | 1117 | |
59bc9e85 | 1118 | gtk_widget_show_all(dialog); |
50b4f4de RN |
1119 | |
1120 | g_object_unref(tbw->store_available); | |
1121 | g_object_unref(tbw->store_used); | |
1122 | ||
1123 | tbw->dialog = dialog; | |
1124 | tbw->tree_available = GTK_TREE_VIEW(tree_available); | |
1125 | tbw->tree_used = GTK_TREE_VIEW(tree_used); | |
1126 | ||
1127 | tbw->last_drag_path = NULL; | |
1128 | ||
1129 | return tbw; | |
1130 | } | |
1131 | ||
59bc9e85 RN |
1132 | /** |
1133 | * toolbar_configure: | |
1134 | * | |
1135 | */ | |
1136 | void toolbar_configure (VikToolbar *vtb, GtkWidget *toolbar, GtkWindow *parent, GtkWidget *vbox, GtkWidget *hbox, ReloadCB reload_cb, gpointer user_data) | |
50b4f4de RN |
1137 | { |
1138 | gchar *markup; | |
1139 | const gchar *name; | |
1140 | GSList *sl, *used_items; | |
1141 | GList *l, *all_items; | |
1142 | GtkTreeIter iter; | |
1143 | GtkTreePath *path; | |
1144 | TBEditorWidget *tbw; | |
1145 | ||
1146 | /* read the current active toolbar items */ | |
59bc9e85 | 1147 | markup = gtk_ui_manager_get_ui(vtb->uim); |
50b4f4de RN |
1148 | used_items = tb_editor_parse_ui(markup, -1, NULL); |
1149 | g_free(markup); | |
1150 | ||
1151 | /* get all available actions */ | |
59bc9e85 RN |
1152 | all_items = gtk_action_group_list_actions(vtb->group_actions); |
1153 | all_items = g_list_concat ( all_items, gtk_action_group_list_actions(vtb->group_toggles) ); | |
1154 | all_items = g_list_concat ( all_items, gtk_action_group_list_actions(vtb->group_tools) ); | |
1155 | all_items = g_list_concat ( all_items, gtk_action_group_list_actions(vtb->group_modes) ); | |
50b4f4de RN |
1156 | |
1157 | /* create the GUI */ | |
59bc9e85 | 1158 | tbw = tb_editor_create_dialog(vtb, parent, toolbar, vbox, hbox, reload_cb, user_data); |
50b4f4de RN |
1159 | |
1160 | /* fill the stores */ | |
1161 | gtk_list_store_insert_with_values(tbw->store_available, NULL, -1, | |
1162 | TB_EDITOR_COL_ACTION, TB_EDITOR_SEPARATOR, | |
1163 | TB_EDITOR_COL_LABEL, TB_EDITOR_SEPARATOR_LABEL, | |
1164 | -1); | |
1165 | foreach_list(l, all_items) | |
1166 | { | |
1167 | name = gtk_action_get_name(l->data); | |
1168 | if (g_slist_find_custom(used_items, name, (GCompareFunc) strcmp) == NULL) | |
1169 | { | |
1170 | gtk_list_store_append(tbw->store_available, &iter); | |
59bc9e85 | 1171 | tb_editor_set_item_values(vtb, name, tbw->store_available, &iter); |
50b4f4de RN |
1172 | } |
1173 | } | |
1174 | foreach_slist(sl, used_items) | |
1175 | { | |
1176 | gtk_list_store_append(tbw->store_used, &iter); | |
59bc9e85 | 1177 | tb_editor_set_item_values(vtb, sl->data, tbw->store_used, &iter); |
50b4f4de RN |
1178 | } |
1179 | /* select first item */ | |
1180 | path = gtk_tree_path_new_from_string("0"); | |
1181 | gtk_tree_selection_select_path(gtk_tree_view_get_selection(tbw->tree_used), path); | |
1182 | gtk_tree_path_free(path); | |
1183 | ||
1184 | /* connect the changed signals after populating the store */ | |
1185 | g_signal_connect(tbw->store_used, "row-changed", | |
1186 | G_CALLBACK(tb_editor_available_items_changed_cb), tbw); | |
1187 | g_signal_connect(tbw->store_used, "row-deleted", | |
1188 | G_CALLBACK(tb_editor_available_items_deleted_cb), tbw); | |
1189 | ||
1190 | /* run it */ | |
1191 | gtk_dialog_run(GTK_DIALOG(tbw->dialog)); | |
1192 | ||
1193 | gtk_widget_destroy(tbw->dialog); | |
1194 | ||
1195 | g_slist_foreach(used_items, (GFunc) g_free, NULL); | |
1196 | g_slist_free(used_items); | |
1197 | g_list_free(all_items); | |
1198 | tb_editor_free_path(tbw); | |
1199 | g_free(tbw); | |
1200 | } |