]> git.street.me.uk Git - andy/viking.git/blob - src/toolbar.c
Some spelling fixes in a comment
[andy/viking.git] / src / toolbar.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  *      toolbar.c - this file was part of Geany (v1.24.1), a fast and lightweight IDE
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>
7  *      Copyright 2014 Rob Norris <rw_norris@hotmail.com>
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
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include "toolbar.h"
35 #include "dir.h"
36 #include "ui_util.h"
37 #include "util.h"
38 #include <string.h>
39 #include <glib/gi18n.h>
40 #include <glib/gstdio.h>
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 }
81
82 VikToolbar *vik_toolbar_new ()
83 {
84         VikToolbar *vtb = (VikToolbar *)g_object_new(vik_toolbar_get_type(), NULL);
85         return vtb;
86 }
87
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 },
114 };
115
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);
153 #ifdef WINDOWS
154         tmp.u = 1; // Small Icons for Windows by default as 'System Defaults' is more GNOME Theme driven.
155 #else
156         tmp.u = 0;
157 #endif
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")->u;
186 }
187
188 static guint prefs_get_icon_style (void)
189 {
190   return a_preferences_get(TOOLBAR_PARAMS_NAMESPACE "icon_style")->u;
191 }
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. */
197 GtkWidget *toolbar_get_widget_by_name(VikToolbar *vtb, const gchar *name)
198 {
199         GtkWidget *widget;
200         gchar *path;
201
202         g_return_val_if_fail(name != NULL, NULL);
203         g_return_val_if_fail(VIK_IS_TOOLBAR(vtb), NULL);
204
205         path = g_strconcat("/ui/MainToolbar/", name, NULL);
206         widget = gtk_ui_manager_get_widget(vtb->uim, path);
207
208         g_free(path);
209         return widget;
210 }
211
212 static GtkAction *get_action ( VikToolbar *vtb, const gchar *name )
213 {
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;
223 }
224
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)
231 {
232         g_return_val_if_fail(name != NULL, NULL);
233         g_return_val_if_fail(VIK_IS_TOOLBAR(vtb), NULL);
234
235         return get_action(vtb,name);
236 }
237
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 }
250
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)
258 {
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);
262 }
263
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);
277
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)
294 {
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 }
299
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);
304 }
305
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 }
322
323 /* sets the icon style of the toolbar */
324 static void toolbar_set_icon_style (GtkWidget *toolbar)
325 {
326         gint icon_style = prefs_get_icon_style();
327
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--;
333
334         gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), icon_style);
335 }
336
337
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;
353         }
354
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 );
381         }
382
383         toolbar_set_icon_style(vtb->widget);
384         toolbar_set_icon_size(vtb->widget);
385
386         /* add the toolbar again to the main window */
387         // Use reorder to ensure toolbar always comes after the menu
388         if (prefs_get_append_to_menu())
389         {
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                 }
394         }
395         else
396         {
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                 }
401         }
402 }
403
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)
428         {
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 );
436         }
437
438         if (markup != NULL)
439         {
440                 vtb->merge_id = gtk_ui_manager_add_ui_from_string(vtb->uim, markup, -1, &error);
441         }
442         else
443         {
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);
450         }
451         if (error != NULL)
452         {
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
464         }
465         vtb->widget = gtk_ui_manager_get_widget(vtb->uim, "/ui/MainToolbar");
466
467         /* update button states */
468         reload_cb ( vtb->group_actions, user_data );
469
470         toolbar_apply_settings(vtb, vbox, hbox, FALSE);
471
472         gtk_widget_show(vtb->widget);
473
474         /* Signals */
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);
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. */
490 }
491
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
497         if (prefs_get_icon_style() == 0 && !g_strcmp0(arg_name, "gtk-toolbar-style"))
498         {
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);
502         }
503         else if (prefs_get_icon_size() == 0 && !g_strcmp0(arg_name, "gtk-toolbar-size"))
504         {
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);
508         }
509 }
510
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)
523 {
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++;
536         }
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++;
549         }
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++;
564         }
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++;
579         }
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);
582
583         toolbar_reload(vtb, NULL, parent, vbox, hbox, reload_cb, user_data);
584
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
588
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);
594         }
595
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 }
602
603 /**
604  * toolbar_action_set_sensitive:
605  *
606  * Set sensitivity of a particular action
607  */
608 void toolbar_action_set_sensitive (VikToolbar *vtb, const gchar *name, gboolean sensitive)
609 {
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);
616 }
617
618 /**
619  * vik_toolbar_finalize:
620  *
621  * Memory cleanups upon toolbar destruction
622  */
623 void vik_toolbar_finalize ( VikToolbar *vtb )
624 {
625         g_hash_table_remove ( signal_data, vtb );
626
627         /* unref'ing the GtkUIManager object will destroy all its widgets unless they were ref'ed */
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);
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;
657
658         config_t config;
659 } TBEditorWidget;
660
661 static const GtkTargetEntry tb_editor_dnd_targets[] =
662 {
663         { "VIKING_TB_EDITOR_ROW", 0, 0 }
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. */
684         if (!g_strcmp0(element_name, "separator"))
685                 *actions = g_slist_append(*actions, g_strdup(TB_EDITOR_SEPARATOR));
686
687         for (i = 0; attribute_names[i] != NULL; i++)
688         {
689                 if (!g_strcmp0(attribute_names[i], "action"))
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
716 static void tb_editor_set_item_values(VikToolbar *vtb, const gchar *name, GtkListStore *store, GtkTreeIter *iter)
717 {
718         gchar *icon = NULL;
719         gchar *label = NULL;
720         gchar *label_clean = NULL;
721
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))
727                         label_clean = g_strdup(TB_EDITOR_SEPARATOR_LABEL);
728                 else
729                         return;
730         }
731         if (action != NULL) {
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)
738                         label_clean = util_str_remove_chars(g_strdup(label), "_");
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
785                 if (g_strcmp0(action_name, TB_EDITOR_SEPARATOR))
786                 {
787                         gtk_list_store_append(tbw->store_available, &iter_new);
788                         tb_editor_set_item_values(tbw->config.vtb, action_name, tbw->store_available, &iter_new);
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);
809                 if (g_strcmp0(action_name, TB_EDITOR_SEPARATOR))
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
821                 tb_editor_set_item_values(tbw->config.vtb, action_name, tbw->store_used, &iter_new);
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,
830                                          gint x, gint y, guint ltime, TBEditorWidget *tbw)
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,
842                                        GtkSelectionData *data, guint info, guint ltime,
843                                        TBEditorWidget *tbw)
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);
884                 is_sep = !g_strcmp0(text, TB_EDITOR_SEPARATOR);
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
913                         tb_editor_set_item_values(tbw->config.vtb, text, store, &iter);
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,
927                                        GtkTreeIter *iter, gpointer data)
928 {
929         gchar *action_name;
930
931         gtk_tree_model_get(model, iter, TB_EDITOR_COL_ACTION, &action_name, -1);
932
933         if (!g_strcmp0(action_name, TB_EDITOR_SEPARATOR))
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\
947 This is Viking's toolbar UI definition.\nThe DTD can be found at \n\
948 http://library.gnome.org/devel/gtk/stable/GtkUIManager.html#GtkUIManager.description.\n\n\
949 Generally one should use the toolbar editor in Viking rather than editing this file.\n\n\
950 For manual changes to this file to take effect, you need to restart Viking.\n-->\n\
951 \t<toolbar name='MainToolbar'>\n";
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
956         g_string_append(str, "\t</toolbar>\n</ui>\n");
957
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);
965
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         }
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
993 static TBEditorWidget *tb_editor_create_dialog(VikToolbar *vtb, GtkWindow *parent, GtkWidget *toolbar, GtkWidget *vbox, GtkWidget *menu_hbox, ReloadCB reload_cb, gpointer user_data)
994 {
995         GtkWidget *dialog, *hbox, *vbox_buttons, *button_add, *button_remove;
996         GtkWidget *swin_available, *swin_used, *tree_available, *tree_used, *label;
997         GtkCellRenderer *text_renderer, *icon_renderer;
998         GtkTreeViewColumn *column;
999
1000         if (parent == NULL) {
1001                 g_warning ( "No parent" );
1002                 return NULL;
1003         }
1004
1005         TBEditorWidget *tbw = g_new(TBEditorWidget, 1);
1006
1007         dialog = gtk_dialog_new_with_buttons(_("Customize Toolbar"),
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");
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
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
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
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);
1117
1118         gtk_widget_show_all(dialog);
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
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)
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 */
1147         markup = gtk_ui_manager_get_ui(vtb->uim);
1148         used_items = tb_editor_parse_ui(markup, -1, NULL);
1149         g_free(markup);
1150
1151         /* get all available actions */
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) );
1156
1157         /* create the GUI */
1158         tbw = tb_editor_create_dialog(vtb, parent, toolbar, vbox, hbox, reload_cb, user_data);
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);
1171                         tb_editor_set_item_values(vtb, name, tbw->store_available, &iter);
1172                 }
1173         }
1174         foreach_slist(sl, used_items)
1175         {
1176                 gtk_list_store_append(tbw->store_used, &iter);
1177                 tb_editor_set_item_values(vtb, sl->data, tbw->store_used, &iter);
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 }