]> git.street.me.uk Git - andy/viking.git/blame - src/toolbar.c
[QA] CID#101114+CID#101115: Result not floating point
[andy/viking.git] / src / toolbar.c
CommitLineData
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
46struct _VikToolbarClass
47{
48 GObjectClass object_class;
49};
50
51struct _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
66G_DEFINE_TYPE (VikToolbar, vik_toolbar, G_TYPE_OBJECT)
67
68static void vik_toolbar_class_init (VikToolbarClass *klass)
69{
70}
71
72static 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
82VikToolbar *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
91static gchar *params_icon_size[] = { N_("System Default"), N_("Small"), N_("Medium"), N_("Large"), NULL };
92static gchar *params_icon_style[] = { N_("System Default"), N_("Icons Only"), N_("Text Only"), N_("Icons and Text"), NULL };
93
94typedef 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
103static config_t extra_widget_data;
104
105static 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
117static GHashTable *signal_data;
118static GSList *toggle_overrides = NULL;
119
120// Forward declaration
121void toolbar_configure (VikToolbar *vtb, GtkWidget *toolbar, GtkWindow *parent, GtkWidget *vbox, GtkWidget *hbox, ReloadCB reload_cb, gpointer user_data);
122
123void 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 */
142void 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 */
171void 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
178static gboolean prefs_get_append_to_menu (void)
179{
180 return a_preferences_get(TOOLBAR_PARAMS_NAMESPACE "append_to_menu")->b;
181}
182
183static guint prefs_get_icon_size (void)
184{
fb0fe134 185 return a_preferences_get(TOOLBAR_PARAMS_NAMESPACE "icon_size")->u;
59bc9e85
RN
186}
187
188static guint prefs_get_icon_style (void)
189{
fb0fe134 190 return a_preferences_get(TOOLBAR_PARAMS_NAMESPACE "icon_style")->u;
59bc9e85 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 197GtkWidget *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 212static 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 */
230GtkAction *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 */
244void 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 */
257void 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 */
273void 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 */
293void 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
300static 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
306static 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 */
324static 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 */
339static 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 */
367void 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 */
408GtkWidget* 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"
415static 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
492static 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 */
516void 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 608void 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 */
623void 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 ---")
643typedef 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
661static const GtkTargetEntry tb_editor_dnd_targets[] =
662{
59bc9e85 663 { "VIKING_TB_EDITOR_ROW", 0, 0 }
50b4f4de
RN
664};
665static const gint tb_editor_dnd_targets_len = G_N_ELEMENTS(tb_editor_dnd_targets);
666
667enum
668{
669 TB_EDITOR_COL_ACTION,
670 TB_EDITOR_COL_LABEL,
671 TB_EDITOR_COL_ICON,
672 TB_EDITOR_COLS_MAX
673};
674
675static 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
697static const GMarkupParser tb_editor_xml_parser =
698{
699 tb_editor_handler_start_element, NULL, NULL, NULL, NULL
700};
701
702
703static 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 716static 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
753static 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
761static 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
771static 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
797static 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
829static 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
841static 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
871static 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
926static 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
943static 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 947This is Viking's toolbar UI definition.\nThe DTD can be found at \n\
50b4f4de 948http://library.gnome.org/devel/gtk/stable/GtkUIManager.html#GtkUIManager.description.\n\n\
59bc9e85 949Generally one should use the toolbar editor in Viking rather than editing this file.\n\n\
7bf8e898 950For 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
979static 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
986static 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 993static 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 */
1136void 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}