Make code more readable by renaming poorly named macros NZV and NVL
[geany-mirror.git] / src / toolbar.c
blob8369b905eec78eab33e97eb8c04b4e2ed5bce956
1 /*
2 * toolbar.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2009-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2009-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 /**
23 * @file toolbar.h
24 * Toolbar (prefs).
26 /* Utility functions to create the toolbar */
28 #include "geany.h"
29 #include "support.h"
30 #include "ui_utils.h"
31 #include "toolbar.h"
32 #include "callbacks.h"
33 #include "utils.h"
34 #include "dialogs.h"
35 #include "document.h"
36 #include "build.h"
37 #include "main.h"
38 #include "geanymenubuttonaction.h"
39 #include "geanyentryaction.h"
41 #include <string.h>
42 #include <glib/gstdio.h>
45 GeanyToolbarPrefs toolbar_prefs;
46 static GtkUIManager *uim;
47 static GtkActionGroup *group;
48 static GSList *plugin_items = NULL;
50 /* Available toolbar actions
51 * Fields: name, stock_id, label, accelerator, tooltip, callback */
52 static const GtkActionEntry ui_entries[] = {
53 /* custom actions defined in toolbar_init(): "New", "Open", "SearchEntry", "GotoEntry", "Build" */
54 { "Save", GTK_STOCK_SAVE, NULL, NULL, N_("Save the current file"), G_CALLBACK(on_toolbutton_save_clicked) },
55 { "SaveAs", GTK_STOCK_SAVE_AS, NULL, NULL, N_("Save as"), G_CALLBACK(on_save_as1_activate) },
56 { "SaveAll", GEANY_STOCK_SAVE_ALL, NULL, NULL, N_("Save all open files"), G_CALLBACK(on_save_all1_activate) },
57 { "Reload", GTK_STOCK_REVERT_TO_SAVED, NULL, NULL, N_("Reload the current file from disk"), G_CALLBACK(on_toolbutton_reload_clicked) },
58 { "Close", GTK_STOCK_CLOSE, NULL, NULL, N_("Close the current file"), G_CALLBACK(on_toolbutton_close_clicked) },
59 { "CloseAll", GEANY_STOCK_CLOSE_ALL, NULL, NULL, N_("Close all open files"), G_CALLBACK(on_toolbutton_close_all_clicked) },
60 { "Cut", GTK_STOCK_CUT, NULL, NULL, N_("Cut the current selection"), G_CALLBACK(on_cut1_activate) },
61 { "Copy", GTK_STOCK_COPY, NULL, NULL, N_("Copy the current selection"), G_CALLBACK(on_copy1_activate) },
62 { "Paste", GTK_STOCK_PASTE, NULL, NULL, N_("Paste the contents of the clipboard"), G_CALLBACK(on_paste1_activate) },
63 { "Delete", GTK_STOCK_DELETE, NULL, NULL, N_("Delete the current selection"), G_CALLBACK(on_delete1_activate) },
64 { "Undo", GTK_STOCK_UNDO, NULL, NULL, N_("Undo the last modification"), G_CALLBACK(on_undo1_activate) },
65 { "Redo", GTK_STOCK_REDO, NULL, NULL, N_("Redo the last modification"), G_CALLBACK(on_redo1_activate) },
66 { "NavBack", GTK_STOCK_GO_BACK, NULL, NULL, N_("Navigate back a location"), G_CALLBACK(on_back_activate) },
67 { "NavFor", GTK_STOCK_GO_FORWARD, NULL, NULL, N_("Navigate forward a location"), G_CALLBACK(on_forward_activate) },
68 { "Compile", GTK_STOCK_CONVERT, N_("Compile"), NULL, N_("Compile the current file"), G_CALLBACK(on_toolbutton_compile_clicked) },
69 { "Run", GTK_STOCK_EXECUTE, NULL, NULL, N_("Run or view the current file"), G_CALLBACK(on_toolbutton_run_clicked) },
70 { "Color", GTK_STOCK_SELECT_COLOR, N_("Color Chooser"), NULL, N_("Open a color chooser dialog, to interactively pick colors from a palette"), G_CALLBACK(on_show_color_chooser1_activate) },
71 { "ZoomIn", GTK_STOCK_ZOOM_IN, NULL, NULL, N_("Zoom in the text"), G_CALLBACK(on_zoom_in1_activate) },
72 { "ZoomOut", GTK_STOCK_ZOOM_OUT, NULL, NULL, N_("Zoom out the text"), G_CALLBACK(on_zoom_out1_activate) },
73 { "UnIndent", GTK_STOCK_UNINDENT, NULL, NULL, N_("Decrease indentation"), G_CALLBACK(on_menu_decrease_indent1_activate) },
74 { "Indent", GTK_STOCK_INDENT, NULL, NULL, N_("Increase indentation"), G_CALLBACK(on_menu_increase_indent1_activate) },
75 { "Search", GTK_STOCK_FIND, NULL, NULL, N_("Find the entered text in the current file"), G_CALLBACK(on_toolbutton_search_clicked) },
76 { "Goto", GTK_STOCK_JUMP_TO, NULL, NULL, N_("Jump to the entered line number"), G_CALLBACK(on_toolbutton_goto_clicked) },
77 { "Preferences", GTK_STOCK_PREFERENCES, NULL, NULL, N_("Show the preferences dialog"), G_CALLBACK(on_toolbutton_preferences_clicked) },
78 { "Quit", GTK_STOCK_QUIT, NULL, NULL, N_("Quit Geany"), G_CALLBACK(on_toolbutton_quit_clicked) },
79 { "Print", GTK_STOCK_PRINT, NULL, NULL, N_("Print document"), G_CALLBACK(on_print1_activate) },
80 { "Replace", GTK_STOCK_FIND_AND_REPLACE, NULL, NULL, N_("Replace text in the current document"), G_CALLBACK(on_replace1_activate) }
82 static const guint ui_entries_n = G_N_ELEMENTS(ui_entries);
85 /* fallback UI definition */
86 static const gchar *toolbar_markup =
87 "<ui>"
88 "<toolbar name='GeanyToolbar'>"
89 "<toolitem action='New'/>"
90 "<toolitem action='Open'/>"
91 "<toolitem action='Save'/>"
92 "<toolitem action='SaveAll'/>"
93 "<separator/>"
94 "<toolitem action='Reload'/>"
95 "<toolitem action='Close'/>"
96 "<separator/>"
97 "<toolitem action='NavBack'/>"
98 "<toolitem action='NavFor'/>"
99 "<separator/>"
100 "<toolitem action='Compile'/>"
101 "<toolitem action='Build'/>"
102 "<toolitem action='Run'/>"
103 "<separator/>"
104 "<toolitem action='Color'/>"
105 "<separator/>"
106 "<toolitem action='SearchEntry'/>"
107 "<toolitem action='Search'/>"
108 "<separator/>"
109 "<toolitem action='GotoEntry'/>"
110 "<toolitem action='Goto'/>"
111 "<separator/>"
112 "<toolitem action='Quit'/>"
113 "</toolbar>"
114 "</ui>";
117 /* Note: The returned widget pointer is only valid until the toolbar is reloaded. So, either
118 * update the widget pointer in this case (i.e. request it again) or better use
119 * toolbar_get_action_by_name() instead. The action objects will remain the same even when the
120 * toolbar is reloaded. */
121 GtkWidget *toolbar_get_widget_by_name(const gchar *name)
123 GtkWidget *widget;
124 gchar *path;
126 g_return_val_if_fail(name != NULL, NULL);
128 path = g_strconcat("/ui/GeanyToolbar/", name, NULL);
129 widget = gtk_ui_manager_get_widget(uim, path);
131 g_free(path);
132 return widget;
136 /* Note: The returned widget pointer is only valid until the toolbar is reloaded. See
137 * toolbar_get_widget_by_name for details(). */
138 GtkWidget *toolbar_get_widget_child_by_name(const gchar *name)
140 GtkWidget *widget = toolbar_get_widget_by_name(name);
142 if (G_LIKELY(widget != NULL))
143 return gtk_bin_get_child(GTK_BIN(widget));
144 else
145 return NULL;
149 GtkAction *toolbar_get_action_by_name(const gchar *name)
151 g_return_val_if_fail(name != NULL, NULL);
153 return gtk_action_group_get_action(group, name);
157 static void toolbar_item_destroy_cb(GtkWidget *widget, G_GNUC_UNUSED gpointer data)
159 plugin_items = g_slist_remove(plugin_items, widget);
163 void toolbar_item_ref(GtkToolItem *item)
165 g_return_if_fail(item != NULL);
167 plugin_items = g_slist_append(plugin_items, item);
168 g_signal_connect(item, "destroy", G_CALLBACK(toolbar_item_destroy_cb), NULL);
172 static GtkWidget *toolbar_reload(const gchar *markup)
174 gint i;
175 GSList *l;
176 GtkWidget *entry;
177 GError *error = NULL;
178 gchar *filename;
179 static guint merge_id = 0;
180 GtkWidget *toolbar_new_file_menu = NULL;
181 GtkWidget *toolbar_recent_files_menu = NULL;
182 GtkWidget *toolbar_build_menu = NULL;
184 /* Cleanup old toolbar */
185 if (merge_id > 0)
187 /* ref plugins toolbar items to keep them after we destroyed the toolbar */
188 foreach_slist(l, plugin_items)
190 g_object_ref(l->data);
191 gtk_container_remove(GTK_CONTAINER(main_widgets.toolbar), GTK_WIDGET(l->data));
193 /* ref and hold the submenus of the New, Open and Build toolbar items */
194 toolbar_new_file_menu = geany_menu_button_action_get_menu(
195 GEANY_MENU_BUTTON_ACTION(gtk_action_group_get_action(group, "New")));
196 g_object_ref(toolbar_new_file_menu);
197 toolbar_recent_files_menu = geany_menu_button_action_get_menu(
198 GEANY_MENU_BUTTON_ACTION(gtk_action_group_get_action(group, "Open")));
199 g_object_ref(toolbar_recent_files_menu);
200 toolbar_build_menu = geany_menu_button_action_get_menu(
201 GEANY_MENU_BUTTON_ACTION(gtk_action_group_get_action(group, "Build")));
202 g_object_ref(toolbar_build_menu);
204 /* Get rid of it! */
205 gtk_widget_destroy(main_widgets.toolbar);
207 gtk_ui_manager_remove_ui(uim, merge_id);
208 gtk_ui_manager_ensure_update(uim);
211 if (markup != NULL)
213 merge_id = gtk_ui_manager_add_ui_from_string(uim, markup, -1, &error);
215 else
217 /* Load the toolbar UI XML file from disk (first from config dir, then try data dir) */
218 filename = g_build_filename(app->configdir, "ui_toolbar.xml", NULL);
219 merge_id = gtk_ui_manager_add_ui_from_file(uim, filename, &error);
220 if (merge_id == 0)
222 if (! g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
223 geany_debug("Loading user toolbar UI definition failed (%s).", error->message);
224 g_error_free(error);
225 error = NULL;
227 SETPTR(filename, g_build_filename(app->datadir, "ui_toolbar.xml", NULL));
228 merge_id = gtk_ui_manager_add_ui_from_file(uim, filename, &error);
230 g_free(filename);
232 if (error != NULL)
234 geany_debug("UI creation failed, using internal fallback definition. Error message: %s",
235 error->message);
236 g_error_free(error);
237 /* finally load the internally defined markup as fallback */
238 merge_id = gtk_ui_manager_add_ui_from_string(uim, toolbar_markup, -1, NULL);
240 main_widgets.toolbar = gtk_ui_manager_get_widget(uim, "/ui/GeanyToolbar");
241 ui_init_toolbar_widgets();
243 /* add the toolbar again to the main window */
244 if (toolbar_prefs.append_to_menu)
246 GtkWidget *hbox_menubar = ui_lookup_widget(main_widgets.window, "hbox_menubar");
247 gtk_box_pack_start(GTK_BOX(hbox_menubar), main_widgets.toolbar, TRUE, TRUE, 0);
248 gtk_box_reorder_child(GTK_BOX(hbox_menubar), main_widgets.toolbar, 1);
250 else
252 GtkWidget *box = ui_lookup_widget(main_widgets.window, "vbox1");
254 gtk_box_pack_start(GTK_BOX(box), main_widgets.toolbar, FALSE, FALSE, 0);
255 gtk_box_reorder_child(GTK_BOX(box), main_widgets.toolbar, 1);
257 gtk_widget_show(main_widgets.toolbar);
259 /* re-add und unref the plugin toolbar items */
260 i = toolbar_get_insert_position();
261 foreach_slist(l, plugin_items)
263 gtk_toolbar_insert(GTK_TOOLBAR(main_widgets.toolbar), l->data, i);
264 g_object_unref(l->data);
265 i++;
267 /* re-add und unref the submenus of menu toolbar items */
268 if (toolbar_new_file_menu != NULL)
270 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
271 gtk_action_group_get_action(group, "New")), toolbar_new_file_menu);
272 g_object_unref(toolbar_new_file_menu);
274 if (toolbar_recent_files_menu != NULL)
276 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
277 gtk_action_group_get_action(group, "Open")), toolbar_recent_files_menu);
278 g_object_unref(toolbar_recent_files_menu);
280 if (toolbar_build_menu != NULL)
282 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
283 gtk_action_group_get_action(group, "Build")), toolbar_build_menu);
284 g_object_unref(toolbar_build_menu);
287 /* update button states */
288 if (main_status.main_window_realized)
290 GeanyDocument *doc = document_get_current();
291 gboolean doc_changed = (doc != NULL) ? doc->changed : FALSE;
293 ui_document_buttons_update();
294 ui_save_buttons_toggle(doc_changed); /* update save all */
295 ui_update_popup_reundo_items(doc);
297 toolbar_apply_settings();
300 /* Signals */
301 g_signal_connect(main_widgets.toolbar, "button-press-event",
302 G_CALLBACK(toolbar_popup_menu), NULL);
303 g_signal_connect(main_widgets.toolbar, "key-press-event",
304 G_CALLBACK(on_escape_key_press_event), NULL);
306 /* We don't need to disconnect those signals as this is done automatically when the entry
307 * widgets are destroyed, happens when the toolbar itself is destroyed. */
308 entry = toolbar_get_widget_child_by_name("SearchEntry");
309 if (entry != NULL)
310 g_signal_connect(entry, "motion-notify-event", G_CALLBACK(on_motion_event), NULL);
311 entry = toolbar_get_widget_child_by_name("GotoEntry");
312 if (entry != NULL)
313 g_signal_connect(entry, "motion-notify-event", G_CALLBACK(on_motion_event), NULL);
315 return main_widgets.toolbar;
319 static void toolbar_notify_style_cb(GObject *object, GParamSpec *arg1, gpointer data)
321 const gchar *arg_name = g_param_spec_get_name(arg1);
322 gint value;
324 if (toolbar_prefs.use_gtk_default_style && utils_str_equal(arg_name, "gtk-toolbar-style"))
326 value = ui_get_gtk_settings_integer(arg_name, toolbar_prefs.icon_style);
327 gtk_toolbar_set_style(GTK_TOOLBAR(main_widgets.toolbar), value);
329 else if (toolbar_prefs.use_gtk_default_icon && utils_str_equal(arg_name, "gtk-toolbar-size"))
331 value = ui_get_gtk_settings_integer(arg_name, toolbar_prefs.icon_size);
332 gtk_toolbar_set_icon_size(GTK_TOOLBAR(main_widgets.toolbar), value);
337 GtkWidget *toolbar_init(void)
339 GtkWidget *toolbar;
340 GtkAction *action_new;
341 GtkAction *action_open;
342 GtkAction *action_build;
343 GtkAction *action_searchentry;
344 GtkAction *action_gotoentry;
345 GtkSettings *gtk_settings;
347 uim = gtk_ui_manager_new();
348 group = gtk_action_group_new("GeanyToolbar");
350 gtk_action_group_set_translation_domain(group, GETTEXT_PACKAGE);
351 gtk_action_group_add_actions(group, ui_entries, ui_entries_n, NULL);
353 /* Create our custom actions */
354 action_new = geany_menu_button_action_new(
355 "New", NULL,
356 _("Create a new file"),
357 _("Create a new file from a template"),
358 GTK_STOCK_NEW);
359 g_signal_connect(action_new, "button-clicked", G_CALLBACK(on_toolbutton_new_clicked), NULL);
360 gtk_action_group_add_action(group, action_new);
362 action_open = geany_menu_button_action_new(
363 "Open", NULL,
364 _("Open an existing file"),
365 _("Open a recent file"),
366 GTK_STOCK_OPEN);
367 g_signal_connect(action_open, "button-clicked", G_CALLBACK(on_toolbutton_open_clicked), NULL);
368 gtk_action_group_add_action(group, action_open);
370 action_build = geany_menu_button_action_new(
371 "Build", NULL,
372 _("Build the current file"),
373 _("Choose more build actions"),
374 GEANY_STOCK_BUILD);
375 g_signal_connect(action_build, "button-clicked",
376 G_CALLBACK(build_toolbutton_build_clicked), NULL);
377 gtk_action_group_add_action(group, action_build);
379 action_searchentry = geany_entry_action_new(
380 "SearchEntry", _("Search Field"), _("Find the entered text in the current file"), FALSE);
381 g_signal_connect(action_searchentry, "entry-activate",
382 G_CALLBACK(on_toolbar_search_entry_activate), GINT_TO_POINTER(FALSE));
383 g_signal_connect(action_searchentry, "entry-activate-backward",
384 G_CALLBACK(on_toolbar_search_entry_activate), GINT_TO_POINTER(TRUE));
385 g_signal_connect(action_searchentry, "entry-changed",
386 G_CALLBACK(on_toolbar_search_entry_changed), NULL);
387 gtk_action_group_add_action(group, action_searchentry);
389 action_gotoentry = geany_entry_action_new(
390 "GotoEntry", _("Goto Field"), _("Jump to the entered line number"), TRUE);
391 g_signal_connect(action_gotoentry, "entry-activate",
392 G_CALLBACK(on_toolbutton_goto_entry_activate), NULL);
393 gtk_action_group_add_action(group, action_gotoentry);
395 gtk_ui_manager_insert_action_group(uim, group, 0);
397 toolbar = toolbar_reload(NULL);
398 #if GTK_CHECK_VERSION(3, 0, 0)
399 gtk_style_context_add_class(gtk_widget_get_style_context(toolbar), "primary-toolbar");
400 #endif
402 gtk_settings = gtk_widget_get_settings(GTK_WIDGET(toolbar));
403 if (gtk_settings != NULL)
405 g_signal_connect(gtk_settings, "notify::gtk-toolbar-style",
406 G_CALLBACK(toolbar_notify_style_cb), NULL);
409 return toolbar;
413 void toolbar_update_ui(void)
415 static GtkWidget *hbox_menubar = NULL;
416 static GtkWidget *menubar = NULL;
417 GtkWidget *menubar_toolbar_separator = NULL;
418 GtkWidget *parent;
419 GtkToolItem *first_item;
421 if (menubar == NULL)
422 { /* cache widget pointers */
423 hbox_menubar = ui_lookup_widget(main_widgets.window, "hbox_menubar");
424 menubar = ui_lookup_widget(main_widgets.window, "menubar1");
426 /* the separator between the menubar and the toolbar */
427 first_item = gtk_toolbar_get_nth_item(GTK_TOOLBAR(main_widgets.toolbar), 0);
428 if (first_item != NULL && GTK_IS_SEPARATOR_TOOL_ITEM(first_item))
430 gtk_widget_destroy(GTK_WIDGET(first_item));
433 parent = gtk_widget_get_parent(main_widgets.toolbar);
435 if (toolbar_prefs.append_to_menu)
437 if (parent != NULL)
439 if (parent != hbox_menubar)
440 { /* here we manually 'reparent' the toolbar, gtk_widget_reparent() doesn't
441 * like to do it */
442 g_object_ref(main_widgets.toolbar);
444 gtk_container_remove(GTK_CONTAINER(parent), main_widgets.toolbar);
445 gtk_box_pack_start(GTK_BOX(hbox_menubar), main_widgets.toolbar, TRUE, TRUE, 0);
446 gtk_box_reorder_child(GTK_BOX(hbox_menubar), main_widgets.toolbar, 1);
448 g_object_unref(main_widgets.toolbar);
451 else
452 gtk_box_pack_start(GTK_BOX(hbox_menubar), main_widgets.toolbar, TRUE, TRUE, 0);
454 /* the separator between the menubar and the toolbar */
455 menubar_toolbar_separator = GTK_WIDGET(gtk_separator_tool_item_new());
456 gtk_widget_show(menubar_toolbar_separator);
457 gtk_toolbar_insert(GTK_TOOLBAR(main_widgets.toolbar),
458 GTK_TOOL_ITEM(menubar_toolbar_separator), 0);
460 else
462 GtkWidget *box = ui_lookup_widget(main_widgets.window, "vbox1");
464 if (parent != NULL)
466 if (parent != box)
468 g_object_ref(main_widgets.toolbar);
470 gtk_container_remove(GTK_CONTAINER(parent), main_widgets.toolbar);
471 gtk_box_pack_start(GTK_BOX(box), main_widgets.toolbar, FALSE, FALSE, 0);
472 gtk_box_reorder_child(GTK_BOX(box), main_widgets.toolbar, 1);
474 g_object_unref(main_widgets.toolbar);
477 else
479 gtk_box_pack_start(GTK_BOX(box), main_widgets.toolbar, FALSE, FALSE, 0);
480 gtk_box_reorder_child(GTK_BOX(box), main_widgets.toolbar, 1);
483 /* we need to adjust the packing flags for the menubar to expand it if it is alone in the
484 * hbox and not expand it if the toolbar is appended */
485 gtk_box_set_child_packing(GTK_BOX(hbox_menubar), menubar,
486 ! (toolbar_prefs.visible && toolbar_prefs.append_to_menu), TRUE, 0, GTK_PACK_START);
490 /* Returns the position for adding new toolbar items. The returned position can be used
491 * to add new toolbar items with @c gtk_toolbar_insert(). The toolbar object can be accessed
492 * with @a geany->main_widgets->toolbar.
493 * The position is always the last one before the Quit button or the very last position if the
494 * Quit button is not the last toolbar item.
496 * @return The position for new toolbar items.
498 gint toolbar_get_insert_position(void)
500 GtkWidget *quit = toolbar_get_widget_by_name("Quit");
501 gint quit_pos = -1, pos;
503 if (quit != NULL)
504 quit_pos = gtk_toolbar_get_item_index(GTK_TOOLBAR(main_widgets.toolbar), GTK_TOOL_ITEM(quit));
506 pos = gtk_toolbar_get_n_items(GTK_TOOLBAR(main_widgets.toolbar));
507 if (quit_pos == (pos - 1))
509 /* if the toolbar item before the quit button is a separator, insert new items before */
510 if (GTK_IS_SEPARATOR_TOOL_ITEM(gtk_toolbar_get_nth_item(
511 GTK_TOOLBAR(main_widgets.toolbar), quit_pos - 1)))
513 return quit_pos - 1;
515 /* else return the position of the quit button to insert new items before */
516 return quit_pos;
519 return pos;
523 void toolbar_finalize(void)
525 GeanyMenubuttonAction *open_action = GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("Open"));
526 g_object_unref(geany_menu_button_action_get_menu(open_action));
527 geany_menu_button_action_set_menu(open_action, NULL);
529 /* unref'ing the GtkUIManager object will destroy all its widgets unless they were ref'ed */
530 g_object_unref(uim);
531 g_object_unref(group);
533 g_slist_free(plugin_items);
537 void toolbar_show_hide(void)
539 ignore_callback = TRUE;
540 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
541 ui_lookup_widget(main_widgets.window, "menu_show_toolbar1")), toolbar_prefs.visible);
542 ui_widget_show_hide(main_widgets.toolbar, toolbar_prefs.visible);
543 ignore_callback = FALSE;
547 /* sets the icon style of the toolbar */
548 static void toolbar_set_icon_style(void)
550 gint icon_style;
552 icon_style = toolbar_prefs.icon_style;
554 if (toolbar_prefs.use_gtk_default_style)
555 icon_style = ui_get_gtk_settings_integer("gtk-toolbar-style", toolbar_prefs.icon_style);
557 gtk_toolbar_set_style(GTK_TOOLBAR(main_widgets.toolbar), icon_style);
561 /* sets the icon size of the toolbar */
562 static void toolbar_set_icon_size(void)
564 gint icon_size;
566 icon_size = toolbar_prefs.icon_size;
568 if (toolbar_prefs.use_gtk_default_icon)
569 icon_size = ui_get_gtk_settings_integer("gtk-toolbar-icon-size", toolbar_prefs.icon_size);
571 gtk_toolbar_set_icon_size(GTK_TOOLBAR(main_widgets.toolbar), icon_size);
575 void toolbar_apply_settings(void)
577 toolbar_set_icon_style();
578 toolbar_set_icon_size();
582 #define TB_EDITOR_SEPARATOR _("Separator")
583 #define TB_EDITOR_SEPARATOR_LABEL _("--- Separator ---")
584 typedef struct
586 GtkWidget *dialog;
588 GtkTreeView *tree_available;
589 GtkTreeView *tree_used;
591 GtkListStore *store_available;
592 GtkListStore *store_used;
594 GtkTreePath *last_drag_path;
595 GtkTreeViewDropPosition last_drag_pos;
597 GtkWidget *drag_source;
598 } TBEditorWidget;
600 static const GtkTargetEntry tb_editor_dnd_targets[] =
602 { "GEANY_TB_EDITOR_ROW", 0, 0 }
604 static const gint tb_editor_dnd_targets_len = G_N_ELEMENTS(tb_editor_dnd_targets);
606 enum
608 TB_EDITOR_COL_ACTION,
609 TB_EDITOR_COL_LABEL,
610 TB_EDITOR_COL_ICON,
611 TB_EDITOR_COLS_MAX
614 static void tb_editor_handler_start_element(GMarkupParseContext *context, const gchar *element_name,
615 const gchar **attribute_names,
616 const gchar **attribute_values, gpointer data,
617 GError **error)
619 gint i;
620 GSList **actions = data;
622 /* This is very basic parsing, stripped down any error checking, requires a valid UI markup. */
623 if (utils_str_equal(element_name, "separator"))
624 *actions = g_slist_append(*actions, g_strdup(TB_EDITOR_SEPARATOR));
626 for (i = 0; attribute_names[i] != NULL; i++)
628 if (utils_str_equal(attribute_names[i], "action"))
630 *actions = g_slist_append(*actions, g_strdup(attribute_values[i]));
636 static const GMarkupParser tb_editor_xml_parser =
638 tb_editor_handler_start_element, NULL, NULL, NULL, NULL
642 static GSList *tb_editor_parse_ui(const gchar *buffer, gssize length, GError **error)
644 GMarkupParseContext *context;
645 GSList *list = NULL;
647 context = g_markup_parse_context_new(&tb_editor_xml_parser, 0, &list, NULL);
648 g_markup_parse_context_parse(context, buffer, length, error);
649 g_markup_parse_context_free(context);
651 return list;
655 static void tb_editor_set_item_values(const gchar *name, GtkListStore *store, GtkTreeIter *iter)
657 gchar *icon = NULL;
658 gchar *label = NULL;
659 gchar *label_clean = NULL;
660 GtkAction *action;
662 action = gtk_action_group_get_action(group, name);
663 if (action == NULL)
665 if (utils_str_equal(name, TB_EDITOR_SEPARATOR))
666 label_clean = g_strdup(TB_EDITOR_SEPARATOR_LABEL);
667 else
668 return;
670 else
672 g_object_get(action, "icon-name", &icon, NULL);
673 if (icon == NULL)
674 g_object_get(action, "stock-id", &icon, NULL);
676 g_object_get(action, "label", &label, NULL);
677 if (label != NULL)
678 label_clean = utils_str_remove_chars(g_strdup(label), "_");
681 gtk_list_store_set(store, iter,
682 TB_EDITOR_COL_ACTION, name,
683 TB_EDITOR_COL_LABEL, label_clean,
684 TB_EDITOR_COL_ICON, icon,
685 -1);
687 g_free(icon);
688 g_free(label);
689 g_free(label_clean);
693 static void tb_editor_scroll_to_iter(GtkTreeView *treeview, GtkTreeIter *iter)
695 GtkTreePath *path = gtk_tree_model_get_path(gtk_tree_view_get_model(treeview), iter);
696 gtk_tree_view_scroll_to_cell(treeview, path, NULL, TRUE, 0.5, 0.0);
697 gtk_tree_path_free(path);
701 static void tb_editor_free_path(TBEditorWidget *tbw)
703 if (tbw->last_drag_path != NULL)
705 gtk_tree_path_free(tbw->last_drag_path);
706 tbw->last_drag_path = NULL;
711 static void tb_editor_btn_remove_clicked_cb(GtkWidget *button, TBEditorWidget *tbw)
713 GtkTreeModel *model_used;
714 GtkTreeSelection *selection_used;
715 GtkTreeIter iter_used, iter_new;
716 gchar *action_name;
718 selection_used = gtk_tree_view_get_selection(tbw->tree_used);
719 if (gtk_tree_selection_get_selected(selection_used, &model_used, &iter_used))
721 gtk_tree_model_get(model_used, &iter_used, TB_EDITOR_COL_ACTION, &action_name, -1);
722 if (gtk_list_store_remove(tbw->store_used, &iter_used))
723 gtk_tree_selection_select_iter(selection_used, &iter_used);
725 if (! utils_str_equal(action_name, TB_EDITOR_SEPARATOR))
727 gtk_list_store_append(tbw->store_available, &iter_new);
728 tb_editor_set_item_values(action_name, tbw->store_available, &iter_new);
729 tb_editor_scroll_to_iter(tbw->tree_available, &iter_new);
732 g_free(action_name);
737 static void tb_editor_btn_add_clicked_cb(GtkWidget *button, TBEditorWidget *tbw)
739 GtkTreeModel *model_available;
740 GtkTreeSelection *selection_available, *selection_used;
741 GtkTreeIter iter_available, iter_new, iter_selected;
742 gchar *action_name;
744 selection_available = gtk_tree_view_get_selection(tbw->tree_available);
745 if (gtk_tree_selection_get_selected(selection_available, &model_available, &iter_available))
747 gtk_tree_model_get(model_available, &iter_available,
748 TB_EDITOR_COL_ACTION, &action_name, -1);
749 if (! utils_str_equal(action_name, TB_EDITOR_SEPARATOR))
751 if (gtk_list_store_remove(tbw->store_available, &iter_available))
752 gtk_tree_selection_select_iter(selection_available, &iter_available);
755 selection_used = gtk_tree_view_get_selection(tbw->tree_used);
756 if (gtk_tree_selection_get_selected(selection_used, NULL, &iter_selected))
757 gtk_list_store_insert_before(tbw->store_used, &iter_new, &iter_selected);
758 else
759 gtk_list_store_append(tbw->store_used, &iter_new);
761 tb_editor_set_item_values(action_name, tbw->store_used, &iter_new);
762 tb_editor_scroll_to_iter(tbw->tree_used, &iter_new);
764 g_free(action_name);
769 static gboolean tb_editor_drag_motion_cb(GtkWidget *widget, GdkDragContext *drag_context,
770 gint x, gint y, guint ltime, TBEditorWidget *tbw)
772 if (tbw->last_drag_path != NULL)
773 gtk_tree_path_free(tbw->last_drag_path);
774 gtk_tree_view_get_drag_dest_row(GTK_TREE_VIEW(widget),
775 &(tbw->last_drag_path), &(tbw->last_drag_pos));
777 return FALSE;
781 static void tb_editor_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context,
782 GtkSelectionData *data, guint info, guint ltime,
783 TBEditorWidget *tbw)
785 GtkTreeIter iter;
786 GtkTreeSelection *selection;
787 GtkTreeModel *model;
788 GdkAtom atom;
789 gchar *name;
791 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
792 if (! gtk_tree_selection_get_selected(selection, &model, &iter))
793 return;
795 gtk_tree_model_get(model, &iter, TB_EDITOR_COL_ACTION, &name, -1);
796 if (G_UNLIKELY(EMPTY(name)))
797 return;
799 atom = gdk_atom_intern(tb_editor_dnd_targets[0].target, FALSE);
800 gtk_selection_data_set(data, atom, 8, (guchar*) name, strlen(name));
802 g_free(name);
804 tbw->drag_source = widget;
808 static void tb_editor_drag_data_rcvd_cb(GtkWidget *widget, GdkDragContext *context,
809 gint x, gint y, GtkSelectionData *data, guint info,
810 guint ltime, TBEditorWidget *tbw)
812 GtkTreeView *tree = GTK_TREE_VIEW(widget);
813 gboolean del = FALSE;
815 if (gtk_selection_data_get_length(data) >= 0 && gtk_selection_data_get_format(data) == 8)
817 gboolean is_sep;
818 gchar *text = NULL;
820 text = (gchar*) gtk_selection_data_get_data(data);
821 is_sep = utils_str_equal(text, TB_EDITOR_SEPARATOR);
822 /* If the source of the action is equal to the target, we do just re-order and so need
823 * to delete the separator to get it moved, not just copied. */
824 if (is_sep && widget == tbw->drag_source)
825 is_sep = FALSE;
827 if (tree != tbw->tree_available || ! is_sep)
829 GtkTreeIter iter, iter_before, *iter_before_ptr;
830 GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree));
832 if (tbw->last_drag_path != NULL)
834 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter_before, tbw->last_drag_path);
836 if (gtk_list_store_iter_is_valid(store, &iter_before))
837 iter_before_ptr = &iter_before;
838 else
839 iter_before_ptr = NULL;
841 if (tbw->last_drag_pos == GTK_TREE_VIEW_DROP_BEFORE ||
842 tbw->last_drag_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
843 gtk_list_store_insert_before(store, &iter, iter_before_ptr);
844 else
845 gtk_list_store_insert_after(store, &iter, iter_before_ptr);
847 else
848 gtk_list_store_append(store, &iter);
850 tb_editor_set_item_values(text, store, &iter);
851 tb_editor_scroll_to_iter(tree, &iter);
853 if (tree != tbw->tree_used || ! is_sep)
854 del = TRUE;
857 tbw->drag_source = NULL; /* reset the value just to be sure */
858 tb_editor_free_path(tbw);
859 gtk_drag_finish(context, TRUE, del, ltime);
863 static gboolean tb_editor_foreach_used(GtkTreeModel *model, GtkTreePath *path,
864 GtkTreeIter *iter, gpointer data)
866 gchar *action_name;
868 gtk_tree_model_get(model, iter, TB_EDITOR_COL_ACTION, &action_name, -1);
870 if (utils_str_equal(action_name, TB_EDITOR_SEPARATOR))
871 g_string_append_printf(data, "\t\t<separator/>\n");
872 else if (G_LIKELY(!EMPTY(action_name)))
873 g_string_append_printf(data, "\t\t<toolitem action='%s' />\n", action_name);
875 g_free(action_name);
876 return FALSE;
880 static void tb_editor_write_markup(TBEditorWidget *tbw)
882 /* <ui> must be the first tag, otherwise gtk_ui_manager_add_ui_from_string() will fail. */
883 const gchar *template = "<ui>\n<!--\n\
884 This is Geany's toolbar UI definition.\nThe DTD can be found at \n\
885 http://library.gnome.org/devel/gtk/stable/GtkUIManager.html#GtkUIManager.description.\n\n\
886 You can re-order all items and freely add and remove available actions.\n\
887 You cannot add new actions which are not listed in the documentation.\n\
888 Everything you add or change must be inside the /ui/toolbar/ path.\n\n\
889 For changes to take effect, you need to restart Geany. Alternatively you can use the toolbar\n\
890 editor in Geany.\n\n\
891 A list of available actions can be found in the documentation included with Geany or\n\
892 at http://www.geany.org/manual/current/index.html#customizing-the-toolbar.\n-->\n\
893 \t<toolbar name='GeanyToolbar'>\n";
894 gchar *filename;
895 GString *str = g_string_new(template);
897 gtk_tree_model_foreach(GTK_TREE_MODEL(tbw->store_used), tb_editor_foreach_used, str);
899 g_string_append(str, "\n\t</toolbar>\n</ui>\n");
901 toolbar_reload(str->str);
903 filename = g_build_filename(app->configdir, "ui_toolbar.xml", NULL);
904 utils_write_file(filename, str->str);
905 g_free(filename);
907 g_string_free(str, TRUE);
911 static void tb_editor_available_items_changed_cb(GtkTreeModel *model, GtkTreePath *arg1,
912 GtkTreeIter *arg2, TBEditorWidget *tbw)
914 tb_editor_write_markup(tbw);
918 static void tb_editor_available_items_deleted_cb(GtkTreeModel *model, GtkTreePath *arg1,
919 TBEditorWidget *tbw)
921 tb_editor_write_markup(tbw);
925 static TBEditorWidget *tb_editor_create_dialog(GtkWindow *parent)
927 GtkWidget *dialog, *vbox, *hbox, *vbox_buttons, *button_add, *button_remove;
928 GtkWidget *swin_available, *swin_used, *tree_available, *tree_used, *label;
929 GtkCellRenderer *text_renderer, *icon_renderer;
930 GtkTreeViewColumn *column;
931 TBEditorWidget *tbw = g_new(TBEditorWidget, 1);
933 if (parent == NULL)
934 parent = GTK_WINDOW(main_widgets.window);
936 dialog = gtk_dialog_new_with_buttons(_("Customize Toolbar"),
937 parent,
938 GTK_DIALOG_DESTROY_WITH_PARENT,
939 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
940 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
941 gtk_box_set_spacing(GTK_BOX(vbox), 6);
942 gtk_widget_set_name(dialog, "GeanyDialog");
943 gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 400);
944 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
946 tbw->store_available = gtk_list_store_new(TB_EDITOR_COLS_MAX,
947 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
948 tbw->store_used = gtk_list_store_new(TB_EDITOR_COLS_MAX,
949 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
951 label = gtk_label_new(
952 _("Select items to be displayed on the toolbar. Items can be reordered by drag and drop."));
953 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
955 tree_available = gtk_tree_view_new();
956 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_available), GTK_TREE_MODEL(tbw->store_available));
957 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_available), TRUE);
958 gtk_tree_sortable_set_sort_column_id(
959 GTK_TREE_SORTABLE(tbw->store_available), TB_EDITOR_COL_LABEL, GTK_SORT_ASCENDING);
961 icon_renderer = gtk_cell_renderer_pixbuf_new();
962 column = gtk_tree_view_column_new_with_attributes(
963 NULL, icon_renderer, "stock-id", TB_EDITOR_COL_ICON, NULL);
964 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_available), column);
966 text_renderer = gtk_cell_renderer_text_new();
967 column = gtk_tree_view_column_new_with_attributes(
968 _("Available Items"), text_renderer, "text", TB_EDITOR_COL_LABEL, NULL);
969 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_available), column);
971 swin_available = gtk_scrolled_window_new(NULL, NULL);
972 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin_available),
973 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
974 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin_available), GTK_SHADOW_ETCHED_IN);
975 gtk_container_add(GTK_CONTAINER(swin_available), tree_available);
977 tree_used = gtk_tree_view_new();
978 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_used), GTK_TREE_MODEL(tbw->store_used));
979 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_used), TRUE);
980 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tree_used), TRUE);
982 icon_renderer = gtk_cell_renderer_pixbuf_new();
983 column = gtk_tree_view_column_new_with_attributes(
984 NULL, icon_renderer, "stock-id", TB_EDITOR_COL_ICON, NULL);
985 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_used), column);
987 text_renderer = gtk_cell_renderer_text_new();
988 column = gtk_tree_view_column_new_with_attributes(
989 _("Displayed Items"), text_renderer, "text", TB_EDITOR_COL_LABEL, NULL);
990 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_used), column);
992 swin_used = gtk_scrolled_window_new(NULL, NULL);
993 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin_used),
994 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
995 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin_used), GTK_SHADOW_ETCHED_IN);
996 gtk_container_add(GTK_CONTAINER(swin_used), tree_used);
998 /* drag'n'drop */
999 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(tree_available), GDK_BUTTON1_MASK,
1000 tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE);
1001 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree_available),
1002 tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE);
1003 g_signal_connect(tree_available, "drag-data-get",
1004 G_CALLBACK(tb_editor_drag_data_get_cb), tbw);
1005 g_signal_connect(tree_available, "drag-data-received",
1006 G_CALLBACK(tb_editor_drag_data_rcvd_cb), tbw);
1007 g_signal_connect(tree_available, "drag-motion",
1008 G_CALLBACK(tb_editor_drag_motion_cb), tbw);
1010 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(tree_used), GDK_BUTTON1_MASK,
1011 tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE);
1012 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree_used),
1013 tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE);
1014 g_signal_connect(tree_used, "drag-data-get",
1015 G_CALLBACK(tb_editor_drag_data_get_cb), tbw);
1016 g_signal_connect(tree_used, "drag-data-received",
1017 G_CALLBACK(tb_editor_drag_data_rcvd_cb), tbw);
1018 g_signal_connect(tree_used, "drag-motion",
1019 G_CALLBACK(tb_editor_drag_motion_cb), tbw);
1022 button_add = ui_button_new_with_image(GTK_STOCK_GO_FORWARD, NULL);
1023 button_remove = ui_button_new_with_image(GTK_STOCK_GO_BACK, NULL);
1024 g_signal_connect(button_add, "clicked", G_CALLBACK(tb_editor_btn_add_clicked_cb), tbw);
1025 g_signal_connect(button_remove, "clicked", G_CALLBACK(tb_editor_btn_remove_clicked_cb), tbw);
1027 vbox_buttons = gtk_vbox_new(FALSE, 6);
1028 /* FIXME this is a little hack'ish, any better ideas? */
1029 gtk_box_pack_start(GTK_BOX(vbox_buttons), gtk_label_new(""), TRUE, TRUE, 0);
1030 gtk_box_pack_start(GTK_BOX(vbox_buttons), button_add, FALSE, FALSE, 0);
1031 gtk_box_pack_start(GTK_BOX(vbox_buttons), button_remove, FALSE, FALSE, 0);
1032 gtk_box_pack_start(GTK_BOX(vbox_buttons), gtk_label_new(""), TRUE, TRUE, 0);
1034 hbox = gtk_hbox_new(FALSE, 6);
1035 gtk_box_pack_start(GTK_BOX(hbox), swin_available, TRUE, TRUE, 0);
1036 gtk_box_pack_start(GTK_BOX(hbox), vbox_buttons, FALSE, FALSE, 0);
1037 gtk_box_pack_start(GTK_BOX(hbox), swin_used, TRUE, TRUE, 0);
1039 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 6);
1040 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
1042 gtk_widget_show_all(vbox);
1044 g_object_unref(tbw->store_available);
1045 g_object_unref(tbw->store_used);
1047 tbw->dialog = dialog;
1048 tbw->tree_available = GTK_TREE_VIEW(tree_available);
1049 tbw->tree_used = GTK_TREE_VIEW(tree_used);
1051 tbw->last_drag_path = NULL;
1053 return tbw;
1057 void toolbar_configure(GtkWindow *parent)
1059 gchar *markup;
1060 const gchar *name;
1061 GSList *sl, *used_items;
1062 GList *l, *all_items;
1063 GtkTreeIter iter;
1064 GtkTreePath *path;
1065 TBEditorWidget *tbw;
1067 /* read the current active toolbar items */
1068 markup = gtk_ui_manager_get_ui(uim);
1069 used_items = tb_editor_parse_ui(markup, -1, NULL);
1070 g_free(markup);
1072 /* get all available actions */
1073 all_items = gtk_action_group_list_actions(group);
1075 /* create the GUI */
1076 tbw = tb_editor_create_dialog(parent);
1078 /* fill the stores */
1079 gtk_list_store_insert_with_values(tbw->store_available, NULL, -1,
1080 TB_EDITOR_COL_ACTION, TB_EDITOR_SEPARATOR,
1081 TB_EDITOR_COL_LABEL, TB_EDITOR_SEPARATOR_LABEL,
1082 -1);
1083 foreach_list(l, all_items)
1085 name = gtk_action_get_name(l->data);
1086 if (g_slist_find_custom(used_items, name, (GCompareFunc) strcmp) == NULL)
1088 gtk_list_store_append(tbw->store_available, &iter);
1089 tb_editor_set_item_values(name, tbw->store_available, &iter);
1092 foreach_slist(sl, used_items)
1094 gtk_list_store_append(tbw->store_used, &iter);
1095 tb_editor_set_item_values(sl->data, tbw->store_used, &iter);
1097 /* select first item */
1098 path = gtk_tree_path_new_from_string("0");
1099 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tbw->tree_used), path);
1100 gtk_tree_path_free(path);
1102 /* connect the changed signals after populating the store */
1103 g_signal_connect(tbw->store_used, "row-changed",
1104 G_CALLBACK(tb_editor_available_items_changed_cb), tbw);
1105 g_signal_connect(tbw->store_used, "row-deleted",
1106 G_CALLBACK(tb_editor_available_items_deleted_cb), tbw);
1108 /* run it */
1109 gtk_dialog_run(GTK_DIALOG(tbw->dialog));
1111 gtk_widget_destroy(tbw->dialog);
1113 g_slist_foreach(used_items, (GFunc) g_free, NULL);
1114 g_slist_free(used_items);
1115 g_list_free(all_items);
1116 tb_editor_free_path(tbw);
1117 g_free(tbw);