2
* sidebar.c - this file is part of Geany, a fast and lightweight IDE
4
* Copyright 2005-2010 Enrico Trƶger <enrico(dot)troeger(at)uvena(dot)de>
5
* Copyright 2006-2010 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
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
* $Id: sidebar.c 5003 2010-06-06 17:21:52Z eht16 $
25
* Sidebar related code for the Symbol list and Open files GtkTreeViews.
32
#include "callbacks.h"
36
#include "documentprivate.h"
37
#include "filetypes.h"
45
#include "sciwrappers.h"
47
#include <gdk/gdkkeysyms.h>
50
SidebarTreeviews tv = {NULL, NULL, NULL};
51
/* while typeahead searching, editor should not get focus */
52
static gboolean may_steal_focus = FALSE;
59
GtkWidget *show_paths;
61
doc_items = {NULL, NULL, NULL, NULL};
65
GtkTreeSelection *selection;
67
} selection_change = {NULL, 0};
77
OPENFILES_ACTION_REMOVE = 0,
78
OPENFILES_ACTION_SAVE,
79
OPENFILES_ACTION_RELOAD
82
/* documents tree model columns */
86
DOCUMENTS_SHORTNAME, /* dirname for parents, basename for children */
89
DOCUMENTS_FILENAME /* full filename */
92
static GtkTreeStore *store_openfiles;
93
static GtkWidget *openfiles_popup_menu;
94
static gboolean documents_show_paths;
95
static GtkWidget *tag_window; /* scrolled window that holds the symbol list GtkTreeView */
97
/* callback prototypes */
98
static gboolean on_openfiles_tree_selection_changed(gpointer data);
99
static void on_openfiles_document_action(GtkMenuItem *menuitem, gpointer user_data);
100
static gboolean on_taglist_tree_selection_changed(gpointer data);
101
static gboolean sidebar_button_press_cb(GtkWidget *widget, GdkEventButton *event,
103
static gboolean sidebar_key_press_cb(GtkWidget *widget, GdkEventKey *event,
105
static void on_list_document_activate(GtkCheckMenuItem *item, gpointer user_data);
106
static void on_list_symbol_activate(GtkCheckMenuItem *item, gpointer user_data);
107
static void documents_menu_update(GtkTreeSelection *selection);
108
static void sidebar_tabs_show_hide(GtkNotebook *notebook, GtkWidget *child,
109
guint page_num, gpointer data);
112
/* the prepare_* functions are document-related, but I think they fit better here than in document.c */
113
static void prepare_taglist(GtkWidget *tree, GtkTreeStore *store)
115
GtkCellRenderer *text_renderer, *icon_renderer;
116
GtkTreeViewColumn *column;
117
GtkTreeSelection *selection;
119
text_renderer = gtk_cell_renderer_text_new();
120
icon_renderer = gtk_cell_renderer_pixbuf_new();
121
column = gtk_tree_view_column_new();
123
gtk_tree_view_column_pack_start(column, icon_renderer, FALSE);
124
gtk_tree_view_column_set_attributes(column, icon_renderer, "pixbuf", SYMBOLS_COLUMN_ICON, NULL);
125
g_object_set(icon_renderer, "xalign", 0.0, NULL);
127
gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
128
gtk_tree_view_column_set_attributes(column, text_renderer, "text", SYMBOLS_COLUMN_NAME, NULL);
129
g_object_set(text_renderer, "yalign", 0.5, NULL);
130
gtk_tree_view_column_set_title(column, _("Symbols"));
132
gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
133
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
135
ui_widget_modify_font_from_string(tree, interface_prefs.tagbar_font);
137
gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(store));
138
g_object_unref(store);
140
g_signal_connect(tree, "button-press-event",
141
G_CALLBACK(sidebar_button_press_cb), NULL);
142
g_signal_connect(tree, "key-press-event",
143
G_CALLBACK(sidebar_key_press_cb), NULL);
145
if (gtk_check_version(2, 12, 0) == NULL)
147
g_object_set(tree, "show-expanders", interface_prefs.show_symbol_list_expanders, NULL);
148
if (! interface_prefs.show_symbol_list_expanders)
149
g_object_set(tree, "level-indentation", 10, NULL);
153
"tooltip-column", SYMBOLS_COLUMN_TOOLTIP, NULL);
156
/* selection handling */
157
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
158
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
159
/* callback for changed selection not necessary, will be handled by button-press-event */
164
on_default_tag_tree_button_press_event(GtkWidget *widget, GdkEventButton *event,
167
if (event->button == 3)
169
gtk_menu_popup(GTK_MENU(tv.popup_taglist), NULL, NULL, NULL, NULL,
170
event->button, event->time);
177
static void create_default_tag_tree(void)
179
GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW(tag_window);
182
/* default_tag_tree is a GtkViewPort with a GtkLabel inside it */
183
tv.default_tag_tree = gtk_viewport_new(
184
gtk_scrolled_window_get_hadjustment(scrolled_window),
185
gtk_scrolled_window_get_vadjustment(scrolled_window));
186
label = gtk_label_new(_("No tags found"));
187
gtk_misc_set_alignment(GTK_MISC(label), 0.1f, 0.01f);
188
gtk_container_add(GTK_CONTAINER(tv.default_tag_tree), label);
189
gtk_widget_show_all(tv.default_tag_tree);
190
g_signal_connect(tv.default_tag_tree, "button-press-event",
191
G_CALLBACK(on_default_tag_tree_button_press_event), NULL);
192
g_object_ref((gpointer)tv.default_tag_tree); /* to hold it after removing */
196
/* update = rescan the tags for doc->filename */
197
void sidebar_update_tag_list(GeanyDocument *doc, gboolean update)
199
if (gtk_bin_get_child(GTK_BIN(tag_window)))
200
gtk_container_remove(GTK_CONTAINER(tag_window), gtk_bin_get_child(GTK_BIN(tag_window)));
202
if (tv.default_tag_tree == NULL)
203
create_default_tag_tree();
205
/* show default empty tag tree if there are no tags */
206
if (doc == NULL || doc->file_type == NULL || ! filetype_has_tags(doc->file_type))
208
gtk_container_add(GTK_CONTAINER(tag_window), tv.default_tag_tree);
213
{ /* updating the tag list in the left tag window */
214
if (doc->priv->tag_tree == NULL)
216
doc->priv->tag_store = gtk_tree_store_new(
217
SYMBOLS_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
218
doc->priv->tag_tree = gtk_tree_view_new();
219
prepare_taglist(doc->priv->tag_tree, doc->priv->tag_store);
220
gtk_widget_show(doc->priv->tag_tree);
221
g_object_ref((gpointer)doc->priv->tag_tree); /* to hold it after removing */
224
doc->has_tags = symbols_recreate_tag_list(doc, SYMBOLS_SORT_USE_PREVIOUS);
229
gtk_container_add(GTK_CONTAINER(tag_window), doc->priv->tag_tree);
233
gtk_container_add(GTK_CONTAINER(tag_window), tv.default_tag_tree);
238
/* cleverly sorts documents by their short name */
239
static gint documents_sort_func(GtkTreeModel *model, GtkTreeIter *iter_a,
240
GtkTreeIter *iter_b, gpointer data)
242
gchar *key_a, *key_b;
243
gchar *name_a, *name_b;
246
gtk_tree_model_get(model, iter_a, DOCUMENTS_SHORTNAME, &name_a, -1);
247
key_a = g_utf8_collate_key_for_filename(name_a, -1);
249
gtk_tree_model_get(model, iter_b, DOCUMENTS_SHORTNAME, &name_b, -1);
250
key_b = g_utf8_collate_key_for_filename(name_b, -1);
252
cmp = strcmp(key_a, key_b);
260
/* does some preparing things to the open files list widget */
261
static void prepare_openfiles(void)
263
GtkCellRenderer *icon_renderer;
264
GtkCellRenderer *text_renderer;
265
GtkTreeViewColumn *column;
266
GtkTreeSelection *selection;
267
GtkTreeSortable *sortable;
269
tv.tree_openfiles = ui_lookup_widget(main_widgets.window, "treeview6");
271
/* store the icon and the short filename to show, and the index as reference,
272
* the colour (black/red/green) and the full name for the tooltip */
273
store_openfiles = gtk_tree_store_new(5, G_TYPE_STRING, G_TYPE_STRING,
274
G_TYPE_POINTER, GDK_TYPE_COLOR, G_TYPE_STRING);
275
gtk_tree_view_set_model(GTK_TREE_VIEW(tv.tree_openfiles), GTK_TREE_MODEL(store_openfiles));
277
/* set policy settings for the scolledwindow around the treeview again, because glade
278
* doesn't keep the settings */
279
gtk_scrolled_window_set_policy(
280
GTK_SCROLLED_WINDOW(ui_lookup_widget(main_widgets.window, "scrolledwindow7")),
281
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
283
icon_renderer = gtk_cell_renderer_pixbuf_new();
284
text_renderer = gtk_cell_renderer_text_new();
285
g_object_set(text_renderer, "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
286
column = gtk_tree_view_column_new();
287
gtk_tree_view_column_pack_start(column, icon_renderer, FALSE);
288
gtk_tree_view_column_set_attributes(column, icon_renderer, "stock-id", DOCUMENTS_ICON, NULL);
289
gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
290
gtk_tree_view_column_set_attributes(column, text_renderer, "text", DOCUMENTS_SHORTNAME,
291
"foreground-gdk", DOCUMENTS_COLOR, NULL);
292
gtk_tree_view_append_column(GTK_TREE_VIEW(tv.tree_openfiles), column);
293
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tv.tree_openfiles), FALSE);
295
gtk_tree_view_set_search_column(GTK_TREE_VIEW(tv.tree_openfiles),
296
DOCUMENTS_SHORTNAME);
298
/* sort opened filenames in the store_openfiles treeview */
299
sortable = GTK_TREE_SORTABLE(GTK_TREE_MODEL(store_openfiles));
300
gtk_tree_sortable_set_sort_func(sortable, DOCUMENTS_SHORTNAME, documents_sort_func, NULL, NULL);
301
gtk_tree_sortable_set_sort_column_id(sortable, DOCUMENTS_SHORTNAME, GTK_SORT_ASCENDING);
303
ui_widget_modify_font_from_string(tv.tree_openfiles, interface_prefs.tagbar_font);
305
/* GTK 2.12 tooltips */
306
if (gtk_check_version(2, 12, 0) == NULL)
307
g_object_set(tv.tree_openfiles, "has-tooltip", TRUE, "tooltip-column", DOCUMENTS_FILENAME, NULL);
309
/* selection handling */
310
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv.tree_openfiles));
311
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
312
g_object_unref(store_openfiles);
314
g_signal_connect(GTK_TREE_VIEW(tv.tree_openfiles), "button-press-event",
315
G_CALLBACK(sidebar_button_press_cb), NULL);
316
g_signal_connect(GTK_TREE_VIEW(tv.tree_openfiles), "key-press-event",
317
G_CALLBACK(sidebar_key_press_cb), NULL);
321
/* iter should be toplevel */
322
static gboolean find_tree_iter_dir(GtkTreeIter *iter, const gchar *dir)
328
if (utils_str_equal(dir, "."))
329
dir = GEANY_STRING_UNTITLED;
331
gtk_tree_model_get(GTK_TREE_MODEL(store_openfiles), iter, DOCUMENTS_DOCUMENT, &doc, -1);
332
g_return_val_if_fail(!doc, FALSE);
334
gtk_tree_model_get(GTK_TREE_MODEL(store_openfiles), iter, DOCUMENTS_SHORTNAME, &name, -1);
336
result = utils_str_equal(name, dir);
343
static GtkTreeIter *get_doc_parent(GeanyDocument *doc)
346
gchar *project_base_path;
347
gchar *dirname = NULL;
348
static GtkTreeIter parent;
349
GtkTreeModel *model = GTK_TREE_MODEL(store_openfiles);
351
if (!documents_show_paths)
354
tmp_dirname = g_path_get_dirname(DOC_FILENAME(doc));
355
/* replace the project base path with the project name */
356
project_base_path = project_get_base_path();
357
if (project_base_path != NULL)
359
gsize len = strlen(project_base_path);
362
if (project_base_path[len-1] == G_DIR_SEPARATOR)
363
project_base_path[len-1] = '\0';
365
/* check whether the dir name matches or uses the project base path */
366
if (g_str_has_prefix(tmp_dirname, project_base_path))
368
rest = tmp_dirname + len;
369
if (*rest == G_DIR_SEPARATOR || *rest == '\0')
370
dirname = g_strdup_printf("%s%s", app->project->name, rest);
372
g_free(project_base_path);
375
dirname = tmp_dirname;
379
if (gtk_tree_model_get_iter_first(model, &parent))
383
if (find_tree_iter_dir(&parent, dirname))
389
while (gtk_tree_model_iter_next(model, &parent));
391
/* no match, add dir parent */
392
gtk_tree_store_append(store_openfiles, &parent, NULL);
393
gtk_tree_store_set(store_openfiles, &parent, DOCUMENTS_ICON, GTK_STOCK_DIRECTORY,
394
DOCUMENTS_SHORTNAME, doc->file_name ? dirname : GEANY_STRING_UNTITLED, -1);
401
/* Also sets doc->priv->iter.
402
* This is called recursively in sidebar_openfiles_update_all(). */
403
void sidebar_openfiles_add(GeanyDocument *doc)
405
GtkTreeIter *iter = &doc->priv->iter;
406
GtkTreeIter *parent = get_doc_parent(doc);
408
const GdkColor *color = document_get_status_color(doc);
410
gtk_tree_store_append(store_openfiles, iter, parent);
412
/* check if new parent */
413
if (parent && gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store_openfiles), parent) == 1)
418
path = gtk_tree_model_get_path(GTK_TREE_MODEL(store_openfiles), parent);
419
gtk_tree_view_expand_row(GTK_TREE_VIEW(tv.tree_openfiles), path, TRUE);
420
gtk_tree_path_free(path);
422
basename = g_path_get_basename(DOC_FILENAME(doc));
423
gtk_tree_store_set(store_openfiles, iter, DOCUMENTS_ICON, GTK_STOCK_FILE,
424
DOCUMENTS_SHORTNAME, basename, DOCUMENTS_DOCUMENT, doc, DOCUMENTS_COLOR, color,
425
DOCUMENTS_FILENAME, DOC_FILENAME(doc), -1);
430
static void openfiles_remove(GeanyDocument *doc)
432
GtkTreeIter *iter = &doc->priv->iter;
435
if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(store_openfiles), &parent, iter) &&
436
gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store_openfiles), &parent) == 1)
437
gtk_tree_store_remove(store_openfiles, &parent);
439
gtk_tree_store_remove(store_openfiles, iter);
443
void sidebar_openfiles_update(GeanyDocument *doc)
445
GtkTreeIter *iter = &doc->priv->iter;
448
gtk_tree_model_get(GTK_TREE_MODEL(store_openfiles), iter, DOCUMENTS_FILENAME, &fname, -1);
450
if (utils_str_equal(fname, DOC_FILENAME(doc)))
452
/* just update color */
453
const GdkColor *color = document_get_status_color(doc);
455
gtk_tree_store_set(store_openfiles, iter, DOCUMENTS_COLOR, color, -1);
459
/* path has changed, so remove and re-add */
460
GtkTreeSelection *treesel;
463
treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv.tree_openfiles));
464
sel = gtk_tree_selection_iter_is_selected(treesel, &doc->priv->iter);
465
openfiles_remove(doc);
467
sidebar_openfiles_add(doc);
469
gtk_tree_selection_select_iter(treesel, &doc->priv->iter);
475
void sidebar_openfiles_update_all()
480
gtk_tree_store_clear(store_openfiles);
481
page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook));
482
for (i = 0; i < page_count; i++)
484
doc = document_get_from_page(i);
485
if (G_UNLIKELY(doc == NULL))
488
sidebar_openfiles_add(doc);
493
void sidebar_remove_document(GeanyDocument *doc)
495
openfiles_remove(doc);
497
if (GTK_IS_WIDGET(doc->priv->tag_tree))
499
gtk_widget_destroy(doc->priv->tag_tree);
500
if (GTK_IS_TREE_VIEW(doc->priv->tag_tree))
502
/* Because it was ref'd in sidebar_update_tag_list, it needs unref'ing */
503
g_object_unref((gpointer)doc->priv->tag_tree);
505
doc->priv->tag_tree = NULL;
510
static void on_hide_sidebar(void)
512
ui_prefs.sidebar_visible = FALSE;
513
ui_sidebar_show_hide();
517
static gboolean on_sidebar_display_symbol_list_show(GtkWidget *item)
519
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),
520
interface_prefs.sidebar_symbol_visible);
525
static gboolean on_sidebar_display_open_files_show(GtkWidget *item)
527
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),
528
interface_prefs.sidebar_openfiles_visible);
533
void sidebar_add_common_menu_items(GtkMenu *menu)
537
item = gtk_separator_menu_item_new();
538
gtk_widget_show(item);
539
gtk_container_add(GTK_CONTAINER(menu), item);
541
item = gtk_check_menu_item_new_with_mnemonic(_("Show S_ymbol List"));
542
gtk_container_add(GTK_CONTAINER(menu), item);
543
g_signal_connect(item, "expose-event",
544
G_CALLBACK(on_sidebar_display_symbol_list_show), NULL);
545
gtk_widget_show(item);
546
g_signal_connect(item, "activate",
547
G_CALLBACK(on_list_symbol_activate), NULL);
549
item = gtk_check_menu_item_new_with_mnemonic(_("Show _Document List"));
550
gtk_container_add(GTK_CONTAINER(menu), item);
551
g_signal_connect(item, "expose-event",
552
G_CALLBACK(on_sidebar_display_open_files_show), NULL);
553
gtk_widget_show(item);
554
g_signal_connect(item, "activate",
555
G_CALLBACK(on_list_document_activate), NULL);
557
item = gtk_image_menu_item_new_with_mnemonic(_("H_ide Sidebar"));
558
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
559
gtk_image_new_from_stock("gtk-close", GTK_ICON_SIZE_MENU));
560
gtk_widget_show(item);
561
gtk_container_add(GTK_CONTAINER(menu), item);
562
g_signal_connect(item, "activate", G_CALLBACK(on_hide_sidebar), NULL);
566
static void on_openfiles_show_paths_activate(GtkCheckMenuItem *item, gpointer user_data)
568
documents_show_paths = gtk_check_menu_item_get_active(item);
569
sidebar_openfiles_update_all();
573
static void on_list_document_activate(GtkCheckMenuItem *item, gpointer user_data)
575
interface_prefs.sidebar_openfiles_visible = gtk_check_menu_item_get_active(item);
576
ui_sidebar_show_hide();
577
sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets.sidebar_notebook), NULL, 0, NULL);
581
static void on_list_symbol_activate(GtkCheckMenuItem *item, gpointer user_data)
583
interface_prefs.sidebar_symbol_visible = gtk_check_menu_item_get_active(item);
584
ui_sidebar_show_hide();
585
sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets.sidebar_notebook), NULL, 0, NULL);
589
static void create_openfiles_popup_menu(void)
593
openfiles_popup_menu = gtk_menu_new();
595
item = gtk_image_menu_item_new_from_stock("gtk-close", NULL);
596
gtk_widget_show(item);
597
gtk_container_add(GTK_CONTAINER(openfiles_popup_menu), item);
598
g_signal_connect(item, "activate",
599
G_CALLBACK(on_openfiles_document_action), GINT_TO_POINTER(OPENFILES_ACTION_REMOVE));
600
doc_items.close = item;
602
item = gtk_separator_menu_item_new();
603
gtk_widget_show(item);
604
gtk_container_add(GTK_CONTAINER(openfiles_popup_menu), item);
606
item = gtk_image_menu_item_new_from_stock("gtk-save", NULL);
607
gtk_widget_show(item);
608
gtk_container_add(GTK_CONTAINER(openfiles_popup_menu), item);
609
g_signal_connect(item, "activate",
610
G_CALLBACK(on_openfiles_document_action), GINT_TO_POINTER(OPENFILES_ACTION_SAVE));
611
doc_items.save = item;
613
item = gtk_image_menu_item_new_with_mnemonic(_("_Reload"));
614
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
615
gtk_image_new_from_stock("gtk-revert-to-saved", GTK_ICON_SIZE_MENU));
616
gtk_widget_show(item);
617
gtk_container_add(GTK_CONTAINER(openfiles_popup_menu), item);
618
g_signal_connect(item, "activate",
619
G_CALLBACK(on_openfiles_document_action), GINT_TO_POINTER(OPENFILES_ACTION_RELOAD));
620
doc_items.reload = item;
622
item = gtk_separator_menu_item_new();
623
gtk_widget_show(item);
624
gtk_container_add(GTK_CONTAINER(openfiles_popup_menu), item);
626
doc_items.show_paths = gtk_check_menu_item_new_with_mnemonic(_("Show _Paths"));
627
gtk_widget_show(doc_items.show_paths);
628
gtk_container_add(GTK_CONTAINER(openfiles_popup_menu), doc_items.show_paths);
629
g_signal_connect(doc_items.show_paths, "activate",
630
G_CALLBACK(on_openfiles_show_paths_activate), NULL);
632
sidebar_add_common_menu_items(GTK_MENU(openfiles_popup_menu));
636
static void unfold_parent(GtkTreeIter *iter)
641
if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(store_openfiles), &parent, iter))
643
path = gtk_tree_model_get_path(GTK_TREE_MODEL(store_openfiles), &parent);
644
gtk_tree_view_expand_row(GTK_TREE_VIEW(tv.tree_openfiles), path, TRUE);
645
gtk_tree_path_free(path);
650
/* compares the given data with the doc pointer from the selected row of openfiles
651
* treeview, in case of a match the row is selected and TRUE is returned
652
* (called indirectly from gtk_tree_model_foreach()) */
653
static gboolean tree_model_find_node(GtkTreeModel *model, GtkTreePath *path,
654
GtkTreeIter *iter, gpointer data)
658
gtk_tree_model_get(GTK_TREE_MODEL(store_openfiles), iter, DOCUMENTS_DOCUMENT, &doc, -1);
662
/* unfolding also prevents a strange bug where the selection gets stuck on the parent
663
* when it is collapsed and then switching documents */
665
gtk_tree_view_set_cursor(GTK_TREE_VIEW(tv.tree_openfiles), path, NULL, FALSE);
672
void sidebar_select_openfiles_item(GeanyDocument *doc)
674
gtk_tree_model_foreach(GTK_TREE_MODEL(store_openfiles), tree_model_find_node, doc);
680
static void document_action(GeanyDocument *doc, gint action)
682
if (! DOC_VALID(doc))
687
case OPENFILES_ACTION_REMOVE:
692
case OPENFILES_ACTION_SAVE:
694
document_save_file(doc, FALSE);
697
case OPENFILES_ACTION_RELOAD:
699
on_toolbutton_reload_clicked(NULL, NULL);
706
static void on_openfiles_document_action(GtkMenuItem *menuitem, gpointer user_data)
709
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv.tree_openfiles));
712
gint action = GPOINTER_TO_INT(user_data);
714
if (gtk_tree_selection_get_selected(selection, &model, &iter))
716
gtk_tree_model_get(model, &iter, DOCUMENTS_DOCUMENT, &doc, -1);
719
document_action(doc, action);
723
/* parent item selected */
725
gint i = gtk_tree_model_iter_n_children(model, &iter) - 1;
727
while (i >= 0 && gtk_tree_model_iter_nth_child(model, &child, &iter, i))
729
gtk_tree_model_get(model, &child, DOCUMENTS_DOCUMENT, &doc, -1);
731
document_action(doc, action);
739
static void change_focus_to_editor(GeanyDocument *doc, GtkWidget *source_widget)
742
document_try_focus(doc, source_widget);
743
may_steal_focus = FALSE;
747
static gboolean on_openfiles_tree_selection_changed(gpointer data)
751
GeanyDocument *doc = NULL;
753
/* use switch_notebook_page to ignore changing the notebook page because it is already done */
754
if (gtk_tree_selection_get_selected(selection_change.selection, &model, &iter) && ! ignore_callback)
756
gtk_tree_model_get(model, &iter, DOCUMENTS_DOCUMENT, &doc, -1);
758
return FALSE; /* parent */
760
/* switch to the doc and grab the focus */
761
gtk_notebook_set_current_page(GTK_NOTEBOOK(main_widgets.notebook),
762
gtk_notebook_page_num(GTK_NOTEBOOK(main_widgets.notebook),
763
(GtkWidget*) doc->editor->sci));
764
if (selection_change.keyval != GDK_space)
765
change_focus_to_editor(doc, tv.tree_openfiles);
771
static gboolean on_taglist_tree_selection_changed(gpointer data)
777
if (gtk_tree_selection_get_selected(selection_change.selection, &model, &iter))
781
gtk_tree_model_get(model, &iter, SYMBOLS_COLUMN_TAG, &tag, -1);
785
line = tag->atts.entry.line;
788
GeanyDocument *doc = document_get_current();
792
navqueue_goto_line(doc, doc, line);
793
if (selection_change.keyval != GDK_space)
794
change_focus_to_editor(doc, NULL);
802
static void update_selection_change(GtkTreeSelection *selection, guint keyval)
804
selection_change.selection = selection;
805
selection_change.keyval = keyval;
809
static gboolean sidebar_key_press_cb(GtkWidget *widget, GdkEventKey *event,
812
may_steal_focus = FALSE;
813
if (ui_is_keyval_enter_or_return(event->keyval) || event->keyval == GDK_space)
815
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
816
may_steal_focus = TRUE;
817
/* delay the query of selection state because this callback is executed before GTK
818
* changes the selection (g_signal_connect_after would be better but it doesn't work) */
819
update_selection_change(selection, event->keyval);
821
if (widget == tv.tree_openfiles) /* tag and doc list have separate handlers */
822
g_idle_add(on_openfiles_tree_selection_changed, NULL);
824
g_idle_add(on_taglist_tree_selection_changed, NULL);
830
static gboolean sidebar_button_press_cb(GtkWidget *widget, GdkEventButton *event,
831
G_GNUC_UNUSED gpointer user_data)
833
GtkTreeSelection *selection;
835
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
836
may_steal_focus = TRUE;
838
if (event->type == GDK_2BUTTON_PRESS)
839
{ /* double click on parent node(section) expands/collapses it */
843
if (gtk_tree_selection_get_selected(selection, &model, &iter))
845
if (gtk_tree_model_iter_has_child(model, &iter))
847
GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
849
if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path))
850
gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget), path);
852
gtk_tree_view_expand_row(GTK_TREE_VIEW(widget), path, FALSE);
854
gtk_tree_path_free(path);
859
else if (event->button == 1)
860
{ /* allow reclicking of taglist treeview item */
861
/* delay the query of selection state because this callback is executed before GTK
862
* changes the selection (g_signal_connect_after would be better but it doesn't work) */
863
update_selection_change(selection, 0);
865
if (widget == tv.tree_openfiles)
866
g_idle_add(on_openfiles_tree_selection_changed, NULL);
868
g_idle_add(on_taglist_tree_selection_changed, NULL);
870
else if (event->button == 3)
872
if (widget == tv.tree_openfiles)
874
if (!openfiles_popup_menu)
875
create_openfiles_popup_menu();
877
/* update menu item sensitivity */
878
documents_menu_update(selection);
879
gtk_menu_popup(GTK_MENU(openfiles_popup_menu), NULL, NULL, NULL, NULL,
880
event->button, event->time);
884
gtk_menu_popup(GTK_MENU(tv.popup_taglist), NULL, NULL, NULL, NULL,
885
event->button, event->time);
892
static void documents_menu_update(GtkTreeSelection *selection)
897
gchar *shortname = NULL;
898
GeanyDocument *doc = NULL;
900
/* maybe no selection e.g. if ctrl-click deselected */
901
sel = gtk_tree_selection_get_selected(selection, &model, &iter);
904
gtk_tree_model_get(model, &iter, DOCUMENTS_DOCUMENT, &doc,
905
DOCUMENTS_SHORTNAME, &shortname, -1);
907
path = NZV(shortname) &&
908
(g_path_is_absolute(shortname) ||
909
(app->project && g_str_has_prefix(shortname, app->project->name)));
911
/* can close all, save all (except shortname), but only reload individually ATM */
912
gtk_widget_set_sensitive(doc_items.close, sel);
913
gtk_widget_set_sensitive(doc_items.save, (doc && doc->real_path) || path);
914
gtk_widget_set_sensitive(doc_items.reload, doc && doc->real_path);
917
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(doc_items.show_paths),
918
documents_show_paths);
922
StashGroup *stash_group = NULL;
924
static void on_load_settings(void)
926
tag_window = ui_lookup_widget(main_widgets.window, "scrolledwindow2");
929
/* note: ui_prefs.sidebar_page is reapplied after plugins are loaded */
930
stash_group_display(stash_group, NULL);
931
sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets.sidebar_notebook), NULL, 0, NULL);
935
static void on_save_settings(void)
937
stash_group_update(stash_group, NULL);
938
sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets.sidebar_notebook), NULL, 0, NULL);
942
void sidebar_init(void)
946
group = stash_group_new(PACKAGE);
947
stash_group_add_boolean(group, &documents_show_paths, "documents_show_paths", TRUE);
948
stash_group_add_widget_property(group, &ui_prefs.sidebar_page, "sidebar_page", GINT_TO_POINTER(0),
949
main_widgets.sidebar_notebook, "page", 0);
950
configuration_add_pref_group(group, FALSE);
953
/* delay building documents treeview until sidebar font has been read */
954
g_signal_connect(geany_object, "load-settings", on_load_settings, NULL);
955
g_signal_connect(geany_object, "save-settings", on_save_settings, NULL);
958
if (gtk_check_version(2, 10, 0) == NULL)
960
g_signal_connect(main_widgets.sidebar_notebook, "page-added",
961
G_CALLBACK(sidebar_tabs_show_hide), NULL);
963
g_signal_connect(main_widgets.sidebar_notebook, "page-removed",
964
G_CALLBACK(sidebar_tabs_show_hide), NULL);
966
sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets.sidebar_notebook), NULL, 0, NULL);
969
#define WIDGET(w) w && GTK_IS_WIDGET(w)
971
void sidebar_finalize(void)
973
if (WIDGET(tv.default_tag_tree))
975
g_object_unref(tv.default_tag_tree);
976
/* This is not exactly clean, default_tag_tree's ref_count is 2 when it is shown,
977
* 1 oherwise. We should probably handle the ref_count more accurate. */
978
if (WIDGET(tv.default_tag_tree))
979
gtk_widget_destroy(tv.default_tag_tree);
981
if (WIDGET(tv.popup_taglist))
982
gtk_widget_destroy(tv.popup_taglist);
983
if (WIDGET(openfiles_popup_menu))
984
gtk_widget_destroy(openfiles_popup_menu);
988
void sidebar_focus_openfiles_tab(void)
990
if (ui_prefs.sidebar_visible && interface_prefs.sidebar_openfiles_visible)
992
GtkNotebook *notebook = GTK_NOTEBOOK(main_widgets.sidebar_notebook);
994
gtk_notebook_set_current_page(notebook, TREEVIEW_OPENFILES);
995
gtk_widget_grab_focus(tv.tree_openfiles);
1000
void sidebar_focus_symbols_tab(void)
1002
if (ui_prefs.sidebar_visible && interface_prefs.sidebar_symbol_visible)
1004
GtkNotebook *notebook = GTK_NOTEBOOK(main_widgets.sidebar_notebook);
1005
GtkWidget *symbol_list_scrollwin = gtk_notebook_get_nth_page(notebook, TREEVIEW_SYMBOL);
1007
gtk_notebook_set_current_page(notebook, TREEVIEW_SYMBOL);
1008
gtk_widget_grab_focus(gtk_bin_get_child(GTK_BIN(symbol_list_scrollwin)));
1013
static void sidebar_tabs_show_hide(GtkNotebook *notebook, GtkWidget *child,
1014
guint page_num, gpointer data)
1016
if (gtk_check_version(2, 10, 0) == NULL)
1018
gint tabs = gtk_notebook_get_n_pages(notebook);
1020
if (interface_prefs.sidebar_symbol_visible == FALSE)
1022
if (interface_prefs.sidebar_openfiles_visible == FALSE)
1025
gtk_notebook_set_show_tabs(notebook, (tabs > 1));