Re-enable SIGTERM handling
[geany-mirror.git] / src / toolbar.c
blobb74b7f2156c435ffb713fb62a088874a9bd1b99a
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 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include "toolbar.h"
34 #include "app.h"
35 #include "build.h"
36 #include "callbacks.h"
37 #include "document.h"
38 #include "geanyentryaction.h"
39 #include "geanymenubuttonaction.h"
40 #include "main.h"
41 #include "support.h"
42 #include "ui_utils.h"
43 #include "utils.h"
45 #include <string.h>
46 #include <glib/gstdio.h>
49 GeanyToolbarPrefs toolbar_prefs;
50 static GtkUIManager *uim;
51 static GtkActionGroup *group;
52 static GSList *plugin_items = NULL;
54 /* Available toolbar actions
55 * Fields: name, stock_id, label, accelerator, tooltip, callback */
56 static const GtkActionEntry ui_entries[] = {
57 /* custom actions defined in toolbar_init(): "New", "Open", "SearchEntry", "GotoEntry", "Build" */
58 { "Save", GTK_STOCK_SAVE, NULL, NULL, N_("Save the current file"), G_CALLBACK(on_save1_activate) },
59 { "SaveAs", GTK_STOCK_SAVE_AS, NULL, NULL, N_("Save as"), G_CALLBACK(on_save_as1_activate) },
60 { "SaveAll", GEANY_STOCK_SAVE_ALL, NULL, NULL, N_("Save all open files"), G_CALLBACK(on_save_all1_activate) },
61 { "Reload", GTK_STOCK_REVERT_TO_SAVED, NULL, NULL, N_("Reload the current file from disk"), G_CALLBACK(on_toolbutton_reload_clicked) },
62 { "Close", GTK_STOCK_CLOSE, NULL, NULL, N_("Close the current file"), G_CALLBACK(on_close1_activate) },
63 { "CloseAll", GEANY_STOCK_CLOSE_ALL, NULL, NULL, N_("Close all open files"), G_CALLBACK(on_close_all1_activate) },
64 { "Cut", GTK_STOCK_CUT, NULL, NULL, N_("Cut the current selection"), G_CALLBACK(on_cut1_activate) },
65 { "Copy", GTK_STOCK_COPY, NULL, NULL, N_("Copy the current selection"), G_CALLBACK(on_copy1_activate) },
66 { "Paste", GTK_STOCK_PASTE, NULL, NULL, N_("Paste the contents of the clipboard"), G_CALLBACK(on_paste1_activate) },
67 { "Delete", GTK_STOCK_DELETE, NULL, NULL, N_("Delete the current selection"), G_CALLBACK(on_delete1_activate) },
68 { "Undo", GTK_STOCK_UNDO, NULL, NULL, N_("Undo the last modification"), G_CALLBACK(on_undo1_activate) },
69 { "Redo", GTK_STOCK_REDO, NULL, NULL, N_("Redo the last modification"), G_CALLBACK(on_redo1_activate) },
70 { "NavBack", GTK_STOCK_GO_BACK, NULL, NULL, N_("Navigate back a location"), G_CALLBACK(on_toolbutton_back_activate) },
71 { "NavFor", GTK_STOCK_GO_FORWARD, NULL, NULL, N_("Navigate forward a location"), G_CALLBACK(on_toolbutton_forward_activate) },
72 { "Compile", GTK_STOCK_CONVERT, N_("Compile"), NULL, N_("Compile the current file"), G_CALLBACK(on_toolbutton_compile_clicked) },
73 { "Run", GTK_STOCK_EXECUTE, NULL, NULL, N_("Run or view the current file"), G_CALLBACK(on_toolbutton_run_clicked) },
74 { "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) },
75 { "ZoomIn", GTK_STOCK_ZOOM_IN, NULL, NULL, N_("Zoom in the text"), G_CALLBACK(on_zoom_in1_activate) },
76 { "ZoomOut", GTK_STOCK_ZOOM_OUT, NULL, NULL, N_("Zoom out the text"), G_CALLBACK(on_zoom_out1_activate) },
77 { "UnIndent", GTK_STOCK_UNINDENT, NULL, NULL, N_("Decrease indentation"), G_CALLBACK(on_menu_decrease_indent1_activate) },
78 { "Indent", GTK_STOCK_INDENT, NULL, NULL, N_("Increase indentation"), G_CALLBACK(on_menu_increase_indent1_activate) },
79 { "Search", GTK_STOCK_FIND, NULL, NULL, N_("Find the entered text in the current file"), G_CALLBACK(on_toolbutton_search_clicked) },
80 { "Goto", GTK_STOCK_JUMP_TO, NULL, NULL, N_("Jump to the entered line number"), G_CALLBACK(on_toolbutton_goto_clicked) },
81 { "Preferences", GTK_STOCK_PREFERENCES, NULL, NULL, N_("Show the preferences dialog"), G_CALLBACK(on_preferences1_activate) },
82 { "Quit", GTK_STOCK_QUIT, NULL, NULL, N_("Quit Geany"), G_CALLBACK(on_quit1_activate) },
83 { "Print", GTK_STOCK_PRINT, NULL, NULL, N_("Print document"), G_CALLBACK(on_print1_activate) },
84 { "Replace", GTK_STOCK_FIND_AND_REPLACE, NULL, NULL, N_("Replace text in the current document"), G_CALLBACK(on_replace1_activate) }
86 static const guint ui_entries_n = G_N_ELEMENTS(ui_entries);
89 /* fallback UI definition */
90 static const gchar *toolbar_markup =
91 "<ui>"
92 "<toolbar name='GeanyToolbar'>"
93 "<toolitem action='New'/>"
94 "<toolitem action='Open'/>"
95 "<toolitem action='Save'/>"
96 "<toolitem action='SaveAll'/>"
97 "<separator/>"
98 "<toolitem action='Reload'/>"
99 "<toolitem action='Close'/>"
100 "<separator/>"
101 "<toolitem action='NavBack'/>"
102 "<toolitem action='NavFor'/>"
103 "<separator/>"
104 "<toolitem action='Compile'/>"
105 "<toolitem action='Build'/>"
106 "<toolitem action='Run'/>"
107 "<separator/>"
108 "<toolitem action='Color'/>"
109 "<separator/>"
110 "<toolitem action='SearchEntry'/>"
111 "<toolitem action='Search'/>"
112 "<separator/>"
113 "<toolitem action='GotoEntry'/>"
114 "<toolitem action='Goto'/>"
115 "<separator/>"
116 "<toolitem action='Quit'/>"
117 "</toolbar>"
118 "</ui>";
121 /* Note: The returned widget pointer is only valid until the toolbar is reloaded. So, either
122 * update the widget pointer in this case (i.e. request it again) or better use
123 * toolbar_get_action_by_name() instead. The action objects will remain the same even when the
124 * toolbar is reloaded. */
125 GtkWidget *toolbar_get_widget_by_name(const gchar *name)
127 GtkWidget *widget;
128 gchar *path;
130 g_return_val_if_fail(name != NULL, NULL);
132 path = g_strconcat("/ui/GeanyToolbar/", name, NULL);
133 widget = gtk_ui_manager_get_widget(uim, path);
135 g_free(path);
136 return widget;
140 /* Note: The returned widget pointer is only valid until the toolbar is reloaded. See
141 * toolbar_get_widget_by_name for details(). */
142 GtkWidget *toolbar_get_widget_child_by_name(const gchar *name)
144 GtkWidget *widget = toolbar_get_widget_by_name(name);
146 if (G_LIKELY(widget != NULL))
147 return gtk_bin_get_child(GTK_BIN(widget));
148 else
149 return NULL;
153 GtkAction *toolbar_get_action_by_name(const gchar *name)
155 g_return_val_if_fail(name != NULL, NULL);
157 return gtk_action_group_get_action(group, name);
161 static void toolbar_item_destroy_cb(GtkWidget *widget, G_GNUC_UNUSED gpointer data)
163 plugin_items = g_slist_remove(plugin_items, widget);
167 void toolbar_item_ref(GtkToolItem *item)
169 g_return_if_fail(item != NULL);
171 plugin_items = g_slist_append(plugin_items, item);
172 g_signal_connect(item, "destroy", G_CALLBACK(toolbar_item_destroy_cb), NULL);
176 static GtkWidget *toolbar_reload(const gchar *markup)
178 gint i;
179 GSList *l;
180 GtkWidget *entry;
181 GError *error = NULL;
182 gchar *filename;
183 static guint merge_id = 0;
184 GtkWidget *toolbar_new_file_menu = NULL;
185 GtkWidget *toolbar_recent_files_menu = NULL;
186 GtkWidget *toolbar_build_menu = NULL;
188 /* Cleanup old toolbar */
189 if (merge_id > 0)
191 /* ref plugins toolbar items to keep them after we destroyed the toolbar */
192 foreach_slist(l, plugin_items)
194 g_object_ref(l->data);
195 gtk_container_remove(GTK_CONTAINER(main_widgets.toolbar), GTK_WIDGET(l->data));
197 /* ref and hold the submenus of the New, Open and Build toolbar items */
198 toolbar_new_file_menu = geany_menu_button_action_get_menu(
199 GEANY_MENU_BUTTON_ACTION(gtk_action_group_get_action(group, "New")));
200 g_object_ref(toolbar_new_file_menu);
201 toolbar_recent_files_menu = geany_menu_button_action_get_menu(
202 GEANY_MENU_BUTTON_ACTION(gtk_action_group_get_action(group, "Open")));
203 g_object_ref(toolbar_recent_files_menu);
204 toolbar_build_menu = geany_menu_button_action_get_menu(
205 GEANY_MENU_BUTTON_ACTION(gtk_action_group_get_action(group, "Build")));
206 g_object_ref(toolbar_build_menu);
208 /* Get rid of it! */
209 gtk_widget_destroy(main_widgets.toolbar);
211 gtk_ui_manager_remove_ui(uim, merge_id);
212 gtk_ui_manager_ensure_update(uim);
215 if (markup != NULL)
217 merge_id = gtk_ui_manager_add_ui_from_string(uim, markup, -1, &error);
219 else
221 /* Load the toolbar UI XML file from disk (first from config dir, then try data dir) */
222 filename = g_build_filename(app->configdir, "ui_toolbar.xml", NULL);
223 merge_id = gtk_ui_manager_add_ui_from_file(uim, filename, &error);
224 if (merge_id == 0)
226 if (! g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
227 geany_debug("Loading user toolbar UI definition failed (%s).", error->message);
228 g_error_free(error);
229 error = NULL;
231 SETPTR(filename, g_build_filename(app->datadir, "ui_toolbar.xml", NULL));
232 merge_id = gtk_ui_manager_add_ui_from_file(uim, filename, &error);
234 g_free(filename);
236 if (error != NULL)
238 geany_debug("UI creation failed, using internal fallback definition. Error message: %s",
239 error->message);
240 g_error_free(error);
241 /* finally load the internally defined markup as fallback */
242 merge_id = gtk_ui_manager_add_ui_from_string(uim, toolbar_markup, -1, NULL);
244 main_widgets.toolbar = gtk_ui_manager_get_widget(uim, "/ui/GeanyToolbar");
245 ui_init_toolbar_widgets();
247 /* add the toolbar again to the main window */
248 if (toolbar_prefs.append_to_menu)
250 GtkWidget *hbox_menubar = ui_lookup_widget(main_widgets.window, "hbox_menubar");
251 gtk_box_pack_start(GTK_BOX(hbox_menubar), main_widgets.toolbar, TRUE, TRUE, 0);
252 gtk_box_reorder_child(GTK_BOX(hbox_menubar), main_widgets.toolbar, 1);
254 else
256 GtkWidget *box = ui_lookup_widget(main_widgets.window, "vbox1");
258 gtk_box_pack_start(GTK_BOX(box), main_widgets.toolbar, FALSE, FALSE, 0);
259 gtk_box_reorder_child(GTK_BOX(box), main_widgets.toolbar, 1);
261 gtk_widget_show(main_widgets.toolbar);
263 /* re-add und unref the plugin toolbar items */
264 i = toolbar_get_insert_position();
265 foreach_slist(l, plugin_items)
267 gtk_toolbar_insert(GTK_TOOLBAR(main_widgets.toolbar), l->data, i);
268 g_object_unref(l->data);
269 i++;
271 /* re-add und unref the submenus of menu toolbar items */
272 if (toolbar_new_file_menu != NULL)
274 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
275 gtk_action_group_get_action(group, "New")), toolbar_new_file_menu);
276 g_object_unref(toolbar_new_file_menu);
278 if (toolbar_recent_files_menu != NULL)
280 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
281 gtk_action_group_get_action(group, "Open")), toolbar_recent_files_menu);
282 g_object_unref(toolbar_recent_files_menu);
284 if (toolbar_build_menu != NULL)
286 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
287 gtk_action_group_get_action(group, "Build")), toolbar_build_menu);
288 g_object_unref(toolbar_build_menu);
291 /* update button states */
292 if (main_status.main_window_realized)
294 GeanyDocument *doc = document_get_current();
295 gboolean doc_changed = (doc != NULL) ? doc->changed : FALSE;
297 ui_document_buttons_update();
298 ui_save_buttons_toggle(doc_changed); /* update save all */
299 ui_update_popup_reundo_items(doc);
301 toolbar_apply_settings();
304 /* Signals */
305 g_signal_connect(main_widgets.toolbar, "button-press-event",
306 G_CALLBACK(toolbar_popup_menu), NULL);
307 g_signal_connect(main_widgets.toolbar, "key-press-event",
308 G_CALLBACK(on_escape_key_press_event), NULL);
310 /* We don't need to disconnect those signals as this is done automatically when the entry
311 * widgets are destroyed, happens when the toolbar itself is destroyed. */
312 entry = toolbar_get_widget_child_by_name("SearchEntry");
313 if (entry != NULL)
314 g_signal_connect(entry, "motion-notify-event", G_CALLBACK(on_motion_event), NULL);
315 entry = toolbar_get_widget_child_by_name("GotoEntry");
316 if (entry != NULL)
317 g_signal_connect(entry, "motion-notify-event", G_CALLBACK(on_motion_event), NULL);
319 return main_widgets.toolbar;
323 static void toolbar_notify_style_cb(GObject *object, GParamSpec *arg1, gpointer data)
325 const gchar *arg_name = g_param_spec_get_name(arg1);
326 gint value;
328 if (toolbar_prefs.use_gtk_default_style && utils_str_equal(arg_name, "gtk-toolbar-style"))
330 value = ui_get_gtk_settings_integer(arg_name, toolbar_prefs.icon_style);
331 gtk_toolbar_set_style(GTK_TOOLBAR(main_widgets.toolbar), value);
333 else if (toolbar_prefs.use_gtk_default_icon && utils_str_equal(arg_name, "gtk-toolbar-size"))
335 value = ui_get_gtk_settings_integer(arg_name, toolbar_prefs.icon_size);
336 gtk_toolbar_set_icon_size(GTK_TOOLBAR(main_widgets.toolbar), value);
341 GtkWidget *toolbar_init(void)
343 GtkWidget *toolbar;
344 GtkAction *action_new;
345 GtkAction *action_open;
346 GtkAction *action_build;
347 GtkAction *action_searchentry;
348 GtkAction *action_gotoentry;
349 GtkSettings *gtk_settings;
351 uim = gtk_ui_manager_new();
352 group = gtk_action_group_new("GeanyToolbar");
354 gtk_action_group_set_translation_domain(group, GETTEXT_PACKAGE);
355 gtk_action_group_add_actions(group, ui_entries, ui_entries_n, NULL);
357 /* Create our custom actions */
358 action_new = geany_menu_button_action_new(
359 "New", NULL,
360 _("Create a new file"),
361 _("Create a new file from a template"),
362 GTK_STOCK_NEW);
363 g_signal_connect(action_new, "button-clicked", G_CALLBACK(on_new1_activate), NULL);
364 gtk_action_group_add_action(group, action_new);
366 action_open = geany_menu_button_action_new(
367 "Open", NULL,
368 _("Open an existing file"),
369 _("Open a recent file"),
370 GTK_STOCK_OPEN);
371 g_signal_connect(action_open, "button-clicked", G_CALLBACK(on_open1_activate), NULL);
372 gtk_action_group_add_action(group, action_open);
374 action_build = geany_menu_button_action_new(
375 "Build", NULL,
376 _("Build the current file"),
377 _("Choose more build actions"),
378 GEANY_STOCK_BUILD);
379 g_signal_connect(action_build, "button-clicked",
380 G_CALLBACK(build_toolbutton_build_clicked), NULL);
381 gtk_action_group_add_action(group, action_build);
383 action_searchentry = geany_entry_action_new(
384 "SearchEntry", _("Search Field"), _("Find the entered text in the current file"), FALSE);
385 g_signal_connect(action_searchentry, "entry-activate",
386 G_CALLBACK(on_toolbar_search_entry_activate), GINT_TO_POINTER(FALSE));
387 g_signal_connect(action_searchentry, "entry-activate-backward",
388 G_CALLBACK(on_toolbar_search_entry_activate), GINT_TO_POINTER(TRUE));
389 g_signal_connect(action_searchentry, "entry-changed",
390 G_CALLBACK(on_toolbar_search_entry_changed), NULL);
391 gtk_action_group_add_action(group, action_searchentry);
393 action_gotoentry = geany_entry_action_new(
394 "GotoEntry", _("Goto Field"), _("Jump to the entered line number"), TRUE);
395 g_signal_connect(action_gotoentry, "entry-activate",
396 G_CALLBACK(on_toolbutton_goto_entry_activate), NULL);
397 gtk_action_group_add_action(group, action_gotoentry);
399 gtk_ui_manager_insert_action_group(uim, group, 0);
401 toolbar = toolbar_reload(NULL);
402 #if GTK_CHECK_VERSION(3, 0, 0)
403 gtk_style_context_add_class(gtk_widget_get_style_context(toolbar), "primary-toolbar");
404 #endif
406 gtk_settings = gtk_widget_get_settings(GTK_WIDGET(toolbar));
407 if (gtk_settings != NULL)
409 g_signal_connect(gtk_settings, "notify::gtk-toolbar-style",
410 G_CALLBACK(toolbar_notify_style_cb), NULL);
413 return toolbar;
417 void toolbar_update_ui(void)
419 static GtkWidget *hbox_menubar = NULL;
420 static GtkWidget *menubar = NULL;
421 GtkWidget *parent;
422 GtkToolItem *first_item;
424 if (menubar == NULL)
425 { /* cache widget pointers */
426 hbox_menubar = ui_lookup_widget(main_widgets.window, "hbox_menubar");
427 menubar = ui_lookup_widget(main_widgets.window, "menubar1");
429 /* the separator between the menubar and the toolbar */
430 first_item = gtk_toolbar_get_nth_item(GTK_TOOLBAR(main_widgets.toolbar), 0);
431 if (first_item != NULL && GTK_IS_SEPARATOR_TOOL_ITEM(first_item))
433 gtk_widget_destroy(GTK_WIDGET(first_item));
436 parent = gtk_widget_get_parent(main_widgets.toolbar);
438 if (toolbar_prefs.append_to_menu)
440 GtkWidget *menubar_toolbar_separator;
442 if (parent != NULL)
444 if (parent != hbox_menubar)
445 { /* here we manually 'reparent' the toolbar, gtk_widget_reparent() doesn't
446 * like to do it */
447 g_object_ref(main_widgets.toolbar);
449 gtk_container_remove(GTK_CONTAINER(parent), main_widgets.toolbar);
450 gtk_box_pack_start(GTK_BOX(hbox_menubar), main_widgets.toolbar, TRUE, TRUE, 0);
451 gtk_box_reorder_child(GTK_BOX(hbox_menubar), main_widgets.toolbar, 1);
453 g_object_unref(main_widgets.toolbar);
456 else
457 gtk_box_pack_start(GTK_BOX(hbox_menubar), main_widgets.toolbar, TRUE, TRUE, 0);
459 /* the separator between the menubar and the toolbar */
460 menubar_toolbar_separator = GTK_WIDGET(gtk_separator_tool_item_new());
461 gtk_widget_show(menubar_toolbar_separator);
462 gtk_toolbar_insert(GTK_TOOLBAR(main_widgets.toolbar),
463 GTK_TOOL_ITEM(menubar_toolbar_separator), 0);
465 else
467 GtkWidget *box = ui_lookup_widget(main_widgets.window, "vbox1");
469 if (parent != NULL)
471 if (parent != box)
473 g_object_ref(main_widgets.toolbar);
475 gtk_container_remove(GTK_CONTAINER(parent), main_widgets.toolbar);
476 gtk_box_pack_start(GTK_BOX(box), main_widgets.toolbar, FALSE, FALSE, 0);
477 gtk_box_reorder_child(GTK_BOX(box), main_widgets.toolbar, 1);
479 g_object_unref(main_widgets.toolbar);
482 else
484 gtk_box_pack_start(GTK_BOX(box), main_widgets.toolbar, FALSE, FALSE, 0);
485 gtk_box_reorder_child(GTK_BOX(box), main_widgets.toolbar, 1);
488 /* we need to adjust the packing flags for the menubar to expand it if it is alone in the
489 * hbox and not expand it if the toolbar is appended */
490 gtk_box_set_child_packing(GTK_BOX(hbox_menubar), menubar,
491 ! (toolbar_prefs.visible && toolbar_prefs.append_to_menu), TRUE, 0, GTK_PACK_START);
495 /* Returns the position for adding new toolbar items. The returned position can be used
496 * to add new toolbar items with @c gtk_toolbar_insert(). The toolbar object can be accessed
497 * with @a geany->main_widgets->toolbar.
498 * The position is always the last one before the Quit button or the very last position if the
499 * Quit button is not the last toolbar item.
501 * @return The position for new toolbar items.
503 gint toolbar_get_insert_position(void)
505 GtkWidget *quit = toolbar_get_widget_by_name("Quit");
506 gint quit_pos = -1, pos;
508 if (quit != NULL)
509 quit_pos = gtk_toolbar_get_item_index(GTK_TOOLBAR(main_widgets.toolbar), GTK_TOOL_ITEM(quit));
511 pos = gtk_toolbar_get_n_items(GTK_TOOLBAR(main_widgets.toolbar));
512 if (quit_pos == (pos - 1))
514 /* if the toolbar item before the quit button is a separator, insert new items before */
515 if (GTK_IS_SEPARATOR_TOOL_ITEM(gtk_toolbar_get_nth_item(
516 GTK_TOOLBAR(main_widgets.toolbar), quit_pos - 1)))
518 return quit_pos - 1;
520 /* else return the position of the quit button to insert new items before */
521 return quit_pos;
524 return pos;
528 void toolbar_finalize(void)
530 GeanyMenubuttonAction *open_action = GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("Open"));
531 g_object_unref(geany_menu_button_action_get_menu(open_action));
532 geany_menu_button_action_set_menu(open_action, NULL);
534 /* unref'ing the GtkUIManager object will destroy all its widgets unless they were ref'ed */
535 g_object_unref(uim);
536 g_object_unref(group);
538 g_slist_free(plugin_items);
542 void toolbar_show_hide(void)
544 ignore_callback = TRUE;
545 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
546 ui_lookup_widget(main_widgets.window, "menu_show_toolbar1")), toolbar_prefs.visible);
547 ui_widget_show_hide(main_widgets.toolbar, toolbar_prefs.visible);
548 ignore_callback = FALSE;
552 /* sets the icon style of the toolbar */
553 static void toolbar_set_icon_style(void)
555 gint icon_style;
557 icon_style = toolbar_prefs.icon_style;
559 if (toolbar_prefs.use_gtk_default_style)
560 icon_style = ui_get_gtk_settings_integer("gtk-toolbar-style", toolbar_prefs.icon_style);
562 gtk_toolbar_set_style(GTK_TOOLBAR(main_widgets.toolbar), icon_style);
566 /* sets the icon size of the toolbar */
567 static void toolbar_set_icon_size(void)
569 gint icon_size;
571 icon_size = toolbar_prefs.icon_size;
573 if (toolbar_prefs.use_gtk_default_icon)
574 icon_size = ui_get_gtk_settings_integer("gtk-toolbar-icon-size", toolbar_prefs.icon_size);
576 gtk_toolbar_set_icon_size(GTK_TOOLBAR(main_widgets.toolbar), icon_size);
580 void toolbar_apply_settings(void)
582 toolbar_set_icon_style();
583 toolbar_set_icon_size();
587 #define TB_EDITOR_SEPARATOR _("Separator")
588 #define TB_EDITOR_SEPARATOR_LABEL _("--- Separator ---")
589 typedef struct
591 GtkWidget *dialog;
593 GtkTreeView *tree_available;
594 GtkTreeView *tree_used;
596 GtkListStore *store_available;
597 GtkListStore *store_used;
599 GtkTreePath *last_drag_path;
600 GtkTreeViewDropPosition last_drag_pos;
602 GtkWidget *drag_source;
603 } TBEditorWidget;
605 static const GtkTargetEntry tb_editor_dnd_targets[] =
607 { "GEANY_TB_EDITOR_ROW", 0, 0 }
609 static const gint tb_editor_dnd_targets_len = G_N_ELEMENTS(tb_editor_dnd_targets);
611 enum
613 TB_EDITOR_COL_ACTION,
614 TB_EDITOR_COL_LABEL,
615 TB_EDITOR_COL_ICON,
616 TB_EDITOR_COLS_MAX
619 static void tb_editor_handler_start_element(GMarkupParseContext *context, const gchar *element_name,
620 const gchar **attribute_names,
621 const gchar **attribute_values, gpointer data,
622 GError **error)
624 gint i;
625 GSList **actions = data;
627 /* This is very basic parsing, stripped down any error checking, requires a valid UI markup. */
628 if (utils_str_equal(element_name, "separator"))
629 *actions = g_slist_append(*actions, g_strdup(TB_EDITOR_SEPARATOR));
631 for (i = 0; attribute_names[i] != NULL; i++)
633 if (utils_str_equal(attribute_names[i], "action"))
635 *actions = g_slist_append(*actions, g_strdup(attribute_values[i]));
641 static const GMarkupParser tb_editor_xml_parser =
643 tb_editor_handler_start_element, NULL, NULL, NULL, NULL
647 static GSList *tb_editor_parse_ui(const gchar *buffer, gssize length, GError **error)
649 GMarkupParseContext *context;
650 GSList *list = NULL;
652 context = g_markup_parse_context_new(&tb_editor_xml_parser, 0, &list, NULL);
653 g_markup_parse_context_parse(context, buffer, length, error);
654 g_markup_parse_context_free(context);
656 return list;
660 static void tb_editor_set_item_values(const gchar *name, GtkListStore *store, GtkTreeIter *iter)
662 gchar *icon = NULL;
663 gchar *label = NULL;
664 gchar *label_clean = NULL;
665 GtkAction *action;
667 action = gtk_action_group_get_action(group, name);
668 if (action == NULL)
670 if (utils_str_equal(name, TB_EDITOR_SEPARATOR))
671 label_clean = g_strdup(TB_EDITOR_SEPARATOR_LABEL);
672 else
673 return;
675 else
677 g_object_get(action, "icon-name", &icon, NULL);
678 if (icon == NULL)
679 g_object_get(action, "stock-id", &icon, NULL);
681 g_object_get(action, "label", &label, NULL);
682 if (label != NULL)
683 label_clean = utils_str_remove_chars(g_strdup(label), "_");
686 gtk_list_store_set(store, iter,
687 TB_EDITOR_COL_ACTION, name,
688 TB_EDITOR_COL_LABEL, label_clean,
689 TB_EDITOR_COL_ICON, icon,
690 -1);
692 g_free(icon);
693 g_free(label);
694 g_free(label_clean);
698 static void tb_editor_scroll_to_iter(GtkTreeView *treeview, GtkTreeIter *iter)
700 GtkTreePath *path = gtk_tree_model_get_path(gtk_tree_view_get_model(treeview), iter);
701 gtk_tree_view_scroll_to_cell(treeview, path, NULL, TRUE, 0.5, 0.0);
702 gtk_tree_path_free(path);
706 static void tb_editor_free_path(TBEditorWidget *tbw)
708 if (tbw->last_drag_path != NULL)
710 gtk_tree_path_free(tbw->last_drag_path);
711 tbw->last_drag_path = NULL;
716 static void tb_editor_btn_remove_clicked_cb(GtkWidget *button, TBEditorWidget *tbw)
718 GtkTreeModel *model_used;
719 GtkTreeSelection *selection_used;
720 GtkTreeIter iter_used, iter_new;
721 gchar *action_name;
723 selection_used = gtk_tree_view_get_selection(tbw->tree_used);
724 if (gtk_tree_selection_get_selected(selection_used, &model_used, &iter_used))
726 gtk_tree_model_get(model_used, &iter_used, TB_EDITOR_COL_ACTION, &action_name, -1);
727 if (gtk_list_store_remove(tbw->store_used, &iter_used))
728 gtk_tree_selection_select_iter(selection_used, &iter_used);
730 if (! utils_str_equal(action_name, TB_EDITOR_SEPARATOR))
732 gtk_list_store_append(tbw->store_available, &iter_new);
733 tb_editor_set_item_values(action_name, tbw->store_available, &iter_new);
734 tb_editor_scroll_to_iter(tbw->tree_available, &iter_new);
737 g_free(action_name);
742 static void tb_editor_btn_add_clicked_cb(GtkWidget *button, TBEditorWidget *tbw)
744 GtkTreeModel *model_available;
745 GtkTreeSelection *selection_available, *selection_used;
746 GtkTreeIter iter_available, iter_new, iter_selected;
747 gchar *action_name;
749 selection_available = gtk_tree_view_get_selection(tbw->tree_available);
750 if (gtk_tree_selection_get_selected(selection_available, &model_available, &iter_available))
752 gtk_tree_model_get(model_available, &iter_available,
753 TB_EDITOR_COL_ACTION, &action_name, -1);
754 if (! utils_str_equal(action_name, TB_EDITOR_SEPARATOR))
756 if (gtk_list_store_remove(tbw->store_available, &iter_available))
757 gtk_tree_selection_select_iter(selection_available, &iter_available);
760 selection_used = gtk_tree_view_get_selection(tbw->tree_used);
761 if (gtk_tree_selection_get_selected(selection_used, NULL, &iter_selected))
762 gtk_list_store_insert_before(tbw->store_used, &iter_new, &iter_selected);
763 else
764 gtk_list_store_append(tbw->store_used, &iter_new);
766 tb_editor_set_item_values(action_name, tbw->store_used, &iter_new);
767 tb_editor_scroll_to_iter(tbw->tree_used, &iter_new);
769 g_free(action_name);
774 static gboolean tb_editor_drag_motion_cb(GtkWidget *widget, GdkDragContext *drag_context,
775 gint x, gint y, guint ltime, TBEditorWidget *tbw)
777 if (tbw->last_drag_path != NULL)
778 gtk_tree_path_free(tbw->last_drag_path);
779 gtk_tree_view_get_drag_dest_row(GTK_TREE_VIEW(widget),
780 &(tbw->last_drag_path), &(tbw->last_drag_pos));
782 return FALSE;
786 static void tb_editor_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context,
787 GtkSelectionData *data, guint info, guint ltime,
788 TBEditorWidget *tbw)
790 GtkTreeIter iter;
791 GtkTreeSelection *selection;
792 GtkTreeModel *model;
793 GdkAtom atom;
794 gchar *name;
796 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
797 if (! gtk_tree_selection_get_selected(selection, &model, &iter))
798 return;
800 gtk_tree_model_get(model, &iter, TB_EDITOR_COL_ACTION, &name, -1);
801 if (G_UNLIKELY(EMPTY(name)))
803 g_free(name);
804 return;
807 atom = gdk_atom_intern(tb_editor_dnd_targets[0].target, FALSE);
808 gtk_selection_data_set(data, atom, 8, (guchar*) name, strlen(name));
810 g_free(name);
812 tbw->drag_source = widget;
816 static void tb_editor_drag_data_rcvd_cb(GtkWidget *widget, GdkDragContext *context,
817 gint x, gint y, GtkSelectionData *data, guint info,
818 guint ltime, TBEditorWidget *tbw)
820 GtkTreeView *tree = GTK_TREE_VIEW(widget);
821 gboolean del = FALSE;
823 if (gtk_selection_data_get_length(data) >= 0 && gtk_selection_data_get_format(data) == 8)
825 gboolean is_sep;
826 gchar *text = NULL;
828 text = (gchar*) gtk_selection_data_get_data(data);
829 is_sep = utils_str_equal(text, TB_EDITOR_SEPARATOR);
830 /* If the source of the action is equal to the target, we do just re-order and so need
831 * to delete the separator to get it moved, not just copied. */
832 if (is_sep && widget == tbw->drag_source)
833 is_sep = FALSE;
835 if (tree != tbw->tree_available || ! is_sep)
837 GtkTreeIter iter, iter_before, *iter_before_ptr;
838 GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree));
840 if (tbw->last_drag_path != NULL)
842 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter_before, tbw->last_drag_path);
844 if (gtk_list_store_iter_is_valid(store, &iter_before))
845 iter_before_ptr = &iter_before;
846 else
847 iter_before_ptr = NULL;
849 if (tbw->last_drag_pos == GTK_TREE_VIEW_DROP_BEFORE ||
850 tbw->last_drag_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
851 gtk_list_store_insert_before(store, &iter, iter_before_ptr);
852 else
853 gtk_list_store_insert_after(store, &iter, iter_before_ptr);
855 else
856 gtk_list_store_append(store, &iter);
858 tb_editor_set_item_values(text, store, &iter);
859 tb_editor_scroll_to_iter(tree, &iter);
861 if (tree != tbw->tree_used || ! is_sep)
862 del = TRUE;
865 tbw->drag_source = NULL; /* reset the value just to be sure */
866 tb_editor_free_path(tbw);
867 gtk_drag_finish(context, TRUE, del, ltime);
871 static gboolean tb_editor_foreach_used(GtkTreeModel *model, GtkTreePath *path,
872 GtkTreeIter *iter, gpointer data)
874 gchar *action_name;
876 gtk_tree_model_get(model, iter, TB_EDITOR_COL_ACTION, &action_name, -1);
878 if (utils_str_equal(action_name, TB_EDITOR_SEPARATOR))
879 g_string_append_printf(data, "\t\t<separator/>\n");
880 else if (G_LIKELY(!EMPTY(action_name)))
881 g_string_append_printf(data, "\t\t<toolitem action='%s' />\n", action_name);
883 g_free(action_name);
884 return FALSE;
888 static void tb_editor_write_markup(TBEditorWidget *tbw)
890 /* <ui> must be the first tag, otherwise gtk_ui_manager_add_ui_from_string() will fail. */
891 const gchar *template = "<ui>\n<!--\n\
892 This is Geany's toolbar UI definition.\nThe DTD can be found at \n\
893 http://library.gnome.org/devel/gtk/stable/GtkUIManager.html#GtkUIManager.description.\n\n\
894 You can re-order all items and freely add and remove available actions.\n\
895 You cannot add new actions which are not listed in the documentation.\n\
896 Everything you add or change must be inside the /ui/toolbar/ path.\n\n\
897 For changes to take effect, you need to restart Geany. Alternatively you can use the toolbar\n\
898 editor in Geany.\n\n\
899 A list of available actions can be found in the documentation included with Geany or\n\
900 at http://www.geany.org/manual/current/index.html#customizing-the-toolbar.\n-->\n\
901 \t<toolbar name='GeanyToolbar'>\n";
902 gchar *filename;
903 GString *str = g_string_new(template);
905 gtk_tree_model_foreach(GTK_TREE_MODEL(tbw->store_used), tb_editor_foreach_used, str);
907 g_string_append(str, "\n\t</toolbar>\n</ui>\n");
909 toolbar_reload(str->str);
911 filename = g_build_filename(app->configdir, "ui_toolbar.xml", NULL);
912 utils_write_file(filename, str->str);
913 g_free(filename);
915 g_string_free(str, TRUE);
919 static void tb_editor_available_items_changed_cb(GtkTreeModel *model, GtkTreePath *arg1,
920 GtkTreeIter *arg2, TBEditorWidget *tbw)
922 tb_editor_write_markup(tbw);
926 static void tb_editor_available_items_deleted_cb(GtkTreeModel *model, GtkTreePath *arg1,
927 TBEditorWidget *tbw)
929 tb_editor_write_markup(tbw);
933 static TBEditorWidget *tb_editor_create_dialog(GtkWindow *parent)
935 GtkWidget *dialog, *vbox, *hbox, *vbox_buttons, *button_add, *button_remove;
936 GtkWidget *swin_available, *swin_used, *tree_available, *tree_used, *label;
937 GtkCellRenderer *text_renderer, *icon_renderer;
938 GtkTreeViewColumn *column;
939 TBEditorWidget *tbw = g_new(TBEditorWidget, 1);
941 if (parent == NULL)
942 parent = GTK_WINDOW(main_widgets.window);
944 dialog = gtk_dialog_new_with_buttons(_("Customize Toolbar"),
945 parent,
946 GTK_DIALOG_DESTROY_WITH_PARENT,
947 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
948 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
949 gtk_box_set_spacing(GTK_BOX(vbox), 6);
950 gtk_widget_set_name(dialog, "GeanyDialog");
951 gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 400);
952 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
954 tbw->store_available = gtk_list_store_new(TB_EDITOR_COLS_MAX,
955 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
956 tbw->store_used = gtk_list_store_new(TB_EDITOR_COLS_MAX,
957 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
959 label = gtk_label_new(
960 _("Select items to be displayed on the toolbar. Items can be reordered by drag and drop."));
961 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
963 tree_available = gtk_tree_view_new();
964 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_available), GTK_TREE_MODEL(tbw->store_available));
965 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_available), TRUE);
966 gtk_tree_sortable_set_sort_column_id(
967 GTK_TREE_SORTABLE(tbw->store_available), TB_EDITOR_COL_LABEL, GTK_SORT_ASCENDING);
969 icon_renderer = gtk_cell_renderer_pixbuf_new();
970 column = gtk_tree_view_column_new_with_attributes(
971 NULL, icon_renderer, "stock-id", TB_EDITOR_COL_ICON, NULL);
972 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_available), column);
974 text_renderer = gtk_cell_renderer_text_new();
975 column = gtk_tree_view_column_new_with_attributes(
976 _("Available Items"), text_renderer, "text", TB_EDITOR_COL_LABEL, NULL);
977 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_available), column);
979 swin_available = gtk_scrolled_window_new(NULL, NULL);
980 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin_available),
981 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
982 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin_available), GTK_SHADOW_IN);
983 gtk_container_add(GTK_CONTAINER(swin_available), tree_available);
985 tree_used = gtk_tree_view_new();
986 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_used), GTK_TREE_MODEL(tbw->store_used));
987 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_used), TRUE);
988 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tree_used), TRUE);
990 icon_renderer = gtk_cell_renderer_pixbuf_new();
991 column = gtk_tree_view_column_new_with_attributes(
992 NULL, icon_renderer, "stock-id", TB_EDITOR_COL_ICON, NULL);
993 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_used), column);
995 text_renderer = gtk_cell_renderer_text_new();
996 column = gtk_tree_view_column_new_with_attributes(
997 _("Displayed Items"), text_renderer, "text", TB_EDITOR_COL_LABEL, NULL);
998 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_used), column);
1000 swin_used = gtk_scrolled_window_new(NULL, NULL);
1001 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin_used),
1002 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1003 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin_used), GTK_SHADOW_IN);
1004 gtk_container_add(GTK_CONTAINER(swin_used), tree_used);
1006 /* drag'n'drop */
1007 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(tree_available), GDK_BUTTON1_MASK,
1008 tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE);
1009 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree_available),
1010 tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE);
1011 g_signal_connect(tree_available, "drag-data-get",
1012 G_CALLBACK(tb_editor_drag_data_get_cb), tbw);
1013 g_signal_connect(tree_available, "drag-data-received",
1014 G_CALLBACK(tb_editor_drag_data_rcvd_cb), tbw);
1015 g_signal_connect(tree_available, "drag-motion",
1016 G_CALLBACK(tb_editor_drag_motion_cb), tbw);
1018 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(tree_used), GDK_BUTTON1_MASK,
1019 tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE);
1020 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree_used),
1021 tb_editor_dnd_targets, tb_editor_dnd_targets_len, GDK_ACTION_MOVE);
1022 g_signal_connect(tree_used, "drag-data-get",
1023 G_CALLBACK(tb_editor_drag_data_get_cb), tbw);
1024 g_signal_connect(tree_used, "drag-data-received",
1025 G_CALLBACK(tb_editor_drag_data_rcvd_cb), tbw);
1026 g_signal_connect(tree_used, "drag-motion",
1027 G_CALLBACK(tb_editor_drag_motion_cb), tbw);
1030 button_add = ui_button_new_with_image(GTK_STOCK_GO_FORWARD, NULL);
1031 button_remove = ui_button_new_with_image(GTK_STOCK_GO_BACK, NULL);
1032 g_signal_connect(button_add, "clicked", G_CALLBACK(tb_editor_btn_add_clicked_cb), tbw);
1033 g_signal_connect(button_remove, "clicked", G_CALLBACK(tb_editor_btn_remove_clicked_cb), tbw);
1035 vbox_buttons = gtk_vbox_new(FALSE, 6);
1036 /* FIXME this is a little hack'ish, any better ideas? */
1037 gtk_box_pack_start(GTK_BOX(vbox_buttons), gtk_label_new(""), TRUE, TRUE, 0);
1038 gtk_box_pack_start(GTK_BOX(vbox_buttons), button_add, FALSE, FALSE, 0);
1039 gtk_box_pack_start(GTK_BOX(vbox_buttons), button_remove, FALSE, FALSE, 0);
1040 gtk_box_pack_start(GTK_BOX(vbox_buttons), gtk_label_new(""), TRUE, TRUE, 0);
1042 hbox = gtk_hbox_new(FALSE, 6);
1043 gtk_box_pack_start(GTK_BOX(hbox), swin_available, TRUE, TRUE, 0);
1044 gtk_box_pack_start(GTK_BOX(hbox), vbox_buttons, FALSE, FALSE, 0);
1045 gtk_box_pack_start(GTK_BOX(hbox), swin_used, TRUE, TRUE, 0);
1047 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 6);
1048 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
1050 gtk_widget_show_all(vbox);
1052 g_object_unref(tbw->store_available);
1053 g_object_unref(tbw->store_used);
1055 tbw->dialog = dialog;
1056 tbw->tree_available = GTK_TREE_VIEW(tree_available);
1057 tbw->tree_used = GTK_TREE_VIEW(tree_used);
1059 tbw->last_drag_path = NULL;
1061 return tbw;
1065 void toolbar_configure(GtkWindow *parent)
1067 gchar *markup;
1068 GSList *sl, *used_items;
1069 GList *l, *all_items;
1070 GtkTreePath *path;
1071 TBEditorWidget *tbw;
1073 /* read the current active toolbar items */
1074 markup = gtk_ui_manager_get_ui(uim);
1075 used_items = tb_editor_parse_ui(markup, -1, NULL);
1076 g_free(markup);
1078 /* get all available actions */
1079 all_items = gtk_action_group_list_actions(group);
1081 /* create the GUI */
1082 tbw = tb_editor_create_dialog(parent);
1084 /* fill the stores */
1085 gtk_list_store_insert_with_values(tbw->store_available, NULL, -1,
1086 TB_EDITOR_COL_ACTION, TB_EDITOR_SEPARATOR,
1087 TB_EDITOR_COL_LABEL, TB_EDITOR_SEPARATOR_LABEL,
1088 -1);
1089 foreach_list(l, all_items)
1091 const gchar *name = gtk_action_get_name(l->data);
1093 if (g_slist_find_custom(used_items, name, (GCompareFunc) strcmp) == NULL)
1095 GtkTreeIter iter;
1097 gtk_list_store_append(tbw->store_available, &iter);
1098 tb_editor_set_item_values(name, tbw->store_available, &iter);
1101 foreach_slist(sl, used_items)
1103 GtkTreeIter iter;
1105 gtk_list_store_append(tbw->store_used, &iter);
1106 tb_editor_set_item_values(sl->data, tbw->store_used, &iter);
1108 /* select first item */
1109 path = gtk_tree_path_new_from_string("0");
1110 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tbw->tree_used), path);
1111 gtk_tree_path_free(path);
1113 /* connect the changed signals after populating the store */
1114 g_signal_connect(tbw->store_used, "row-changed",
1115 G_CALLBACK(tb_editor_available_items_changed_cb), tbw);
1116 g_signal_connect(tbw->store_used, "row-deleted",
1117 G_CALLBACK(tb_editor_available_items_deleted_cb), tbw);
1119 /* run it */
1120 gtk_dialog_run(GTK_DIALOG(tbw->dialog));
1122 gtk_widget_destroy(tbw->dialog);
1124 g_slist_foreach(used_items, (GFunc) g_free, NULL);
1125 g_slist_free(used_items);
1126 g_list_free(all_items);
1127 tb_editor_free_path(tbw);
1128 g_free(tbw);