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.
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 /* does some preparing things to the open files list widget */
239 static void prepare_openfiles(void)
241 GtkCellRenderer
*icon_renderer
;
242 GtkCellRenderer
*text_renderer
;
243 GtkTreeViewColumn
*column
;
244 GtkTreeSelection
*selection
;
245 GtkTreeSortable
*sortable
;
247 tv
.tree_openfiles
= ui_lookup_widget(main_widgets
.window
, "treeview6");
249 /* store the icon and the short filename to show, and the index as reference,
250 * the colour (black/red/green) and the full name for the tooltip */
251 store_openfiles
= gtk_tree_store_new(5, G_TYPE_STRING
, G_TYPE_STRING
,
252 G_TYPE_POINTER
, GDK_TYPE_COLOR
, G_TYPE_STRING
);
253 gtk_tree_view_set_model(GTK_TREE_VIEW(tv
.tree_openfiles
), GTK_TREE_MODEL(store_openfiles
));
255 /* set policy settings for the scolledwindow around the treeview again, because glade
256 * doesn't keep the settings */
257 gtk_scrolled_window_set_policy(
258 GTK_SCROLLED_WINDOW(ui_lookup_widget(main_widgets
.window
, "scrolledwindow7")),
259 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
261 icon_renderer
= gtk_cell_renderer_pixbuf_new();
262 text_renderer
= gtk_cell_renderer_text_new();
263 g_object_set(text_renderer
, "ellipsize", PANGO_ELLIPSIZE_MIDDLE
, NULL
);
264 column
= gtk_tree_view_column_new();
265 gtk_tree_view_column_pack_start(column
, icon_renderer
, FALSE
);
266 gtk_tree_view_column_set_attributes(column
, icon_renderer
, "stock-id", DOCUMENTS_ICON
, NULL
);
267 gtk_tree_view_column_pack_start(column
, text_renderer
, TRUE
);
268 gtk_tree_view_column_set_attributes(column
, text_renderer
, "text", DOCUMENTS_SHORTNAME
,
269 "foreground-gdk", DOCUMENTS_COLOR
, NULL
);
270 gtk_tree_view_append_column(GTK_TREE_VIEW(tv
.tree_openfiles
), column
);
271 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tv
.tree_openfiles
), FALSE
);
273 gtk_tree_view_set_search_column(GTK_TREE_VIEW(tv
.tree_openfiles
),
274 DOCUMENTS_SHORTNAME
);
276 /* sort opened filenames in the store_openfiles treeview */
277 sortable
= GTK_TREE_SORTABLE(GTK_TREE_MODEL(store_openfiles
));
278 gtk_tree_sortable_set_sort_column_id(sortable
, DOCUMENTS_SHORTNAME
, GTK_SORT_ASCENDING
);
280 ui_widget_modify_font_from_string(tv
.tree_openfiles
, interface_prefs
.tagbar_font
);
282 /* GTK 2.12 tooltips */
283 if (gtk_check_version(2, 12, 0) == NULL
)
284 g_object_set(tv
.tree_openfiles
, "has-tooltip", TRUE
, "tooltip-column", DOCUMENTS_FILENAME
, NULL
);
286 /* selection handling */
287 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(tv
.tree_openfiles
));
288 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
289 g_object_unref(store_openfiles
);
291 g_signal_connect(GTK_TREE_VIEW(tv
.tree_openfiles
), "button-press-event",
292 G_CALLBACK(sidebar_button_press_cb
), NULL
);
293 g_signal_connect(GTK_TREE_VIEW(tv
.tree_openfiles
), "key-press-event",
294 G_CALLBACK(sidebar_key_press_cb
), NULL
);
298 /* iter should be toplevel */
299 static gboolean
find_tree_iter_dir(GtkTreeIter
*iter
, const gchar
*dir
)
305 if (utils_str_equal(dir
, "."))
306 dir
= GEANY_STRING_UNTITLED
;
308 gtk_tree_model_get(GTK_TREE_MODEL(store_openfiles
), iter
, DOCUMENTS_DOCUMENT
, &doc
, -1);
309 g_return_val_if_fail(!doc
, FALSE
);
311 gtk_tree_model_get(GTK_TREE_MODEL(store_openfiles
), iter
, DOCUMENTS_SHORTNAME
, &name
, -1);
313 result
= utils_str_equal(name
, dir
);
320 static GtkTreeIter
*get_doc_parent(GeanyDocument
*doc
)
323 gchar
*project_base_path
;
324 gchar
*dirname
= NULL
;
325 static GtkTreeIter parent
;
326 GtkTreeModel
*model
= GTK_TREE_MODEL(store_openfiles
);
328 if (!documents_show_paths
)
331 tmp_dirname
= g_path_get_dirname(DOC_FILENAME(doc
));
332 /* replace the project base path with the project name */
333 project_base_path
= project_get_base_path();
334 if (project_base_path
!= NULL
)
336 gsize len
= strlen(project_base_path
);
339 /* check whether the dir name matches or uses the project base path */
340 if (!utils_str_equal(project_base_path
, tmp_dirname
))
341 setptr(project_base_path
, g_strconcat(project_base_path
, G_DIR_SEPARATOR_S
, NULL
));
342 if (g_str_has_prefix(tmp_dirname
, project_base_path
))
344 rest
= tmp_dirname
+ len
;
345 dirname
= g_strdup_printf("%s%s%s",
347 (*rest
!= G_DIR_SEPARATOR
&& *rest
!= '\0') ? G_DIR_SEPARATOR_S
: "",
350 g_free(project_base_path
);
353 dirname
= tmp_dirname
;
357 if (gtk_tree_model_get_iter_first(model
, &parent
))
361 if (find_tree_iter_dir(&parent
, dirname
))
367 while (gtk_tree_model_iter_next(model
, &parent
));
369 /* no match, add dir parent */
370 gtk_tree_store_append(store_openfiles
, &parent
, NULL
);
371 gtk_tree_store_set(store_openfiles
, &parent
, DOCUMENTS_ICON
, GTK_STOCK_DIRECTORY
,
372 DOCUMENTS_SHORTNAME
, doc
->file_name
? dirname
: GEANY_STRING_UNTITLED
, -1);
379 /* Also sets doc->priv->iter.
380 * This is called recursively in sidebar_openfiles_update_all(). */
381 void sidebar_openfiles_add(GeanyDocument
*doc
)
383 GtkTreeIter
*iter
= &doc
->priv
->iter
;
384 GtkTreeIter
*parent
= get_doc_parent(doc
);
386 const GdkColor
*color
= document_get_status_color(doc
);
388 gtk_tree_store_append(store_openfiles
, iter
, parent
);
390 /* check if new parent */
391 if (parent
&& gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store_openfiles
), parent
) == 1)
396 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(store_openfiles
), parent
);
397 gtk_tree_view_expand_row(GTK_TREE_VIEW(tv
.tree_openfiles
), path
, TRUE
);
398 gtk_tree_path_free(path
);
400 basename
= g_path_get_basename(DOC_FILENAME(doc
));
401 gtk_tree_store_set(store_openfiles
, iter
, DOCUMENTS_ICON
, GTK_STOCK_FILE
,
402 DOCUMENTS_SHORTNAME
, basename
, DOCUMENTS_DOCUMENT
, doc
, DOCUMENTS_COLOR
, color
,
403 DOCUMENTS_FILENAME
, DOC_FILENAME(doc
), -1);
408 static void openfiles_remove(GeanyDocument
*doc
)
410 GtkTreeIter
*iter
= &doc
->priv
->iter
;
413 if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(store_openfiles
), &parent
, iter
) &&
414 gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store_openfiles
), &parent
) == 1)
415 gtk_tree_store_remove(store_openfiles
, &parent
);
417 gtk_tree_store_remove(store_openfiles
, iter
);
421 void sidebar_openfiles_update(GeanyDocument
*doc
)
423 GtkTreeIter
*iter
= &doc
->priv
->iter
;
426 gtk_tree_model_get(GTK_TREE_MODEL(store_openfiles
), iter
, DOCUMENTS_FILENAME
, &fname
, -1);
428 if (utils_str_equal(fname
, DOC_FILENAME(doc
)))
430 /* just update color */
431 const GdkColor
*color
= document_get_status_color(doc
);
433 gtk_tree_store_set(store_openfiles
, iter
, DOCUMENTS_COLOR
, color
, -1);
437 /* path has changed, so remove and re-add */
438 GtkTreeSelection
*treesel
;
441 treesel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(tv
.tree_openfiles
));
442 sel
= gtk_tree_selection_iter_is_selected(treesel
, &doc
->priv
->iter
);
443 openfiles_remove(doc
);
445 sidebar_openfiles_add(doc
);
447 gtk_tree_selection_select_iter(treesel
, &doc
->priv
->iter
);
453 void sidebar_openfiles_update_all()
458 gtk_tree_store_clear(store_openfiles
);
459 page_count
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
));
460 for (i
= 0; i
< page_count
; i
++)
462 doc
= document_get_from_page(i
);
463 if (G_UNLIKELY(doc
== NULL
))
466 sidebar_openfiles_add(doc
);
471 void sidebar_remove_document(GeanyDocument
*doc
)
473 openfiles_remove(doc
);
475 if (GTK_IS_WIDGET(doc
->priv
->tag_tree
))
477 gtk_widget_destroy(doc
->priv
->tag_tree
);
478 if (GTK_IS_TREE_VIEW(doc
->priv
->tag_tree
))
480 /* Because it was ref'd in sidebar_update_tag_list, it needs unref'ing */
481 g_object_unref((gpointer
)doc
->priv
->tag_tree
);
483 doc
->priv
->tag_tree
= NULL
;
488 static void on_hide_sidebar(void)
490 ui_prefs
.sidebar_visible
= FALSE
;
491 ui_sidebar_show_hide();
495 static gboolean
on_sidebar_display_symbol_list_show(GtkWidget
*item
)
497 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
),
498 interface_prefs
.sidebar_symbol_visible
);
503 static gboolean
on_sidebar_display_open_files_show(GtkWidget
*item
)
505 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
),
506 interface_prefs
.sidebar_openfiles_visible
);
511 void sidebar_add_common_menu_items(GtkMenu
*menu
)
515 item
= gtk_separator_menu_item_new();
516 gtk_widget_show(item
);
517 gtk_container_add(GTK_CONTAINER(menu
), item
);
519 item
= gtk_check_menu_item_new_with_mnemonic(_("Show S_ymbol List"));
520 gtk_container_add(GTK_CONTAINER(menu
), item
);
521 g_signal_connect(item
, "expose-event",
522 G_CALLBACK(on_sidebar_display_symbol_list_show
), NULL
);
523 gtk_widget_show(item
);
524 g_signal_connect(item
, "activate",
525 G_CALLBACK(on_list_symbol_activate
), NULL
);
527 item
= gtk_check_menu_item_new_with_mnemonic(_("Show _Document List"));
528 gtk_container_add(GTK_CONTAINER(menu
), item
);
529 g_signal_connect(item
, "expose-event",
530 G_CALLBACK(on_sidebar_display_open_files_show
), NULL
);
531 gtk_widget_show(item
);
532 g_signal_connect(item
, "activate",
533 G_CALLBACK(on_list_document_activate
), NULL
);
535 item
= gtk_image_menu_item_new_with_mnemonic(_("H_ide Sidebar"));
536 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
537 gtk_image_new_from_stock("gtk-close", GTK_ICON_SIZE_MENU
));
538 gtk_widget_show(item
);
539 gtk_container_add(GTK_CONTAINER(menu
), item
);
540 g_signal_connect(item
, "activate", G_CALLBACK(on_hide_sidebar
), NULL
);
544 static void on_openfiles_show_paths_activate(GtkCheckMenuItem
*item
, gpointer user_data
)
546 documents_show_paths
= gtk_check_menu_item_get_active(item
);
547 sidebar_openfiles_update_all();
551 static void on_list_document_activate(GtkCheckMenuItem
*item
, gpointer user_data
)
553 interface_prefs
.sidebar_openfiles_visible
= gtk_check_menu_item_get_active(item
);
554 ui_sidebar_show_hide();
555 sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), NULL
, 0, NULL
);
559 static void on_list_symbol_activate(GtkCheckMenuItem
*item
, gpointer user_data
)
561 interface_prefs
.sidebar_symbol_visible
= gtk_check_menu_item_get_active(item
);
562 ui_sidebar_show_hide();
563 sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), NULL
, 0, NULL
);
567 static void create_openfiles_popup_menu(void)
571 openfiles_popup_menu
= gtk_menu_new();
573 item
= gtk_image_menu_item_new_from_stock("gtk-close", NULL
);
574 gtk_widget_show(item
);
575 gtk_container_add(GTK_CONTAINER(openfiles_popup_menu
), item
);
576 g_signal_connect(item
, "activate",
577 G_CALLBACK(on_openfiles_document_action
), GINT_TO_POINTER(OPENFILES_ACTION_REMOVE
));
578 doc_items
.close
= item
;
580 item
= gtk_separator_menu_item_new();
581 gtk_widget_show(item
);
582 gtk_container_add(GTK_CONTAINER(openfiles_popup_menu
), item
);
584 item
= gtk_image_menu_item_new_from_stock("gtk-save", NULL
);
585 gtk_widget_show(item
);
586 gtk_container_add(GTK_CONTAINER(openfiles_popup_menu
), item
);
587 g_signal_connect(item
, "activate",
588 G_CALLBACK(on_openfiles_document_action
), GINT_TO_POINTER(OPENFILES_ACTION_SAVE
));
589 doc_items
.save
= item
;
591 item
= gtk_image_menu_item_new_with_mnemonic(_("_Reload"));
592 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
593 gtk_image_new_from_stock("gtk-revert-to-saved", GTK_ICON_SIZE_MENU
));
594 gtk_widget_show(item
);
595 gtk_container_add(GTK_CONTAINER(openfiles_popup_menu
), item
);
596 g_signal_connect(item
, "activate",
597 G_CALLBACK(on_openfiles_document_action
), GINT_TO_POINTER(OPENFILES_ACTION_RELOAD
));
598 doc_items
.reload
= item
;
600 item
= gtk_separator_menu_item_new();
601 gtk_widget_show(item
);
602 gtk_container_add(GTK_CONTAINER(openfiles_popup_menu
), item
);
604 doc_items
.show_paths
= gtk_check_menu_item_new_with_mnemonic(_("Show _Paths"));
605 gtk_widget_show(doc_items
.show_paths
);
606 gtk_container_add(GTK_CONTAINER(openfiles_popup_menu
), doc_items
.show_paths
);
607 g_signal_connect(doc_items
.show_paths
, "activate",
608 G_CALLBACK(on_openfiles_show_paths_activate
), NULL
);
610 sidebar_add_common_menu_items(GTK_MENU(openfiles_popup_menu
));
614 static void unfold_parent(GtkTreeIter
*iter
)
619 if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(store_openfiles
), &parent
, iter
))
621 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(store_openfiles
), &parent
);
622 gtk_tree_view_expand_row(GTK_TREE_VIEW(tv
.tree_openfiles
), path
, TRUE
);
623 gtk_tree_path_free(path
);
628 /* compares the given data with the doc pointer from the selected row of openfiles
629 * treeview, in case of a match the row is selected and TRUE is returned
630 * (called indirectly from gtk_tree_model_foreach()) */
631 static gboolean
tree_model_find_node(GtkTreeModel
*model
, GtkTreePath
*path
,
632 GtkTreeIter
*iter
, gpointer data
)
636 gtk_tree_model_get(GTK_TREE_MODEL(store_openfiles
), iter
, DOCUMENTS_DOCUMENT
, &doc
, -1);
640 /* unfolding also prevents a strange bug where the selection gets stuck on the parent
641 * when it is collapsed and then switching documents */
643 gtk_tree_view_set_cursor(GTK_TREE_VIEW(tv
.tree_openfiles
), path
, NULL
, FALSE
);
650 void sidebar_select_openfiles_item(GeanyDocument
*doc
)
652 gtk_tree_model_foreach(GTK_TREE_MODEL(store_openfiles
), tree_model_find_node
, doc
);
658 static void document_action(GeanyDocument
*doc
, gint action
)
660 if (! DOC_VALID(doc
))
665 case OPENFILES_ACTION_REMOVE
:
670 case OPENFILES_ACTION_SAVE
:
672 document_save_file(doc
, FALSE
);
675 case OPENFILES_ACTION_RELOAD
:
677 on_toolbutton_reload_clicked(NULL
, NULL
);
684 static void on_openfiles_document_action(GtkMenuItem
*menuitem
, gpointer user_data
)
687 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(tv
.tree_openfiles
));
690 gint action
= GPOINTER_TO_INT(user_data
);
692 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
694 gtk_tree_model_get(model
, &iter
, DOCUMENTS_DOCUMENT
, &doc
, -1);
697 document_action(doc
, action
);
701 /* parent item selected */
703 gint i
= gtk_tree_model_iter_n_children(model
, &iter
) - 1;
705 while (i
>= 0 && gtk_tree_model_iter_nth_child(model
, &child
, &iter
, i
))
707 gtk_tree_model_get(model
, &child
, DOCUMENTS_DOCUMENT
, &doc
, -1);
709 document_action(doc
, action
);
717 static void change_focus_to_editor(GeanyDocument
*doc
, GtkWidget
*source_widget
)
720 document_try_focus(doc
, source_widget
);
721 may_steal_focus
= FALSE
;
725 static gboolean
on_openfiles_tree_selection_changed(gpointer data
)
729 GeanyDocument
*doc
= NULL
;
731 /* use switch_notebook_page to ignore changing the notebook page because it is already done */
732 if (gtk_tree_selection_get_selected(selection_change
.selection
, &model
, &iter
) && ! ignore_callback
)
734 gtk_tree_model_get(model
, &iter
, DOCUMENTS_DOCUMENT
, &doc
, -1);
736 return FALSE
; /* parent */
738 /* switch to the doc and grab the focus */
739 gtk_notebook_set_current_page(GTK_NOTEBOOK(main_widgets
.notebook
),
740 gtk_notebook_page_num(GTK_NOTEBOOK(main_widgets
.notebook
),
741 (GtkWidget
*) doc
->editor
->sci
));
742 if (selection_change
.keyval
!= GDK_space
)
743 change_focus_to_editor(doc
, tv
.tree_openfiles
);
749 static gboolean
on_taglist_tree_selection_changed(gpointer data
)
755 if (gtk_tree_selection_get_selected(selection_change
.selection
, &model
, &iter
))
759 gtk_tree_model_get(model
, &iter
, SYMBOLS_COLUMN_TAG
, &tag
, -1);
763 line
= tag
->atts
.entry
.line
;
766 GeanyDocument
*doc
= document_get_current();
770 navqueue_goto_line(doc
, doc
, line
);
771 if (selection_change
.keyval
!= GDK_space
)
772 change_focus_to_editor(doc
, NULL
);
780 static void update_selection_change(GtkTreeSelection
*selection
, guint keyval
)
782 selection_change
.selection
= selection
;
783 selection_change
.keyval
= keyval
;
787 static gboolean
sidebar_key_press_cb(GtkWidget
*widget
, GdkEventKey
*event
,
790 may_steal_focus
= FALSE
;
791 if (ui_is_keyval_enter_or_return(event
->keyval
) || event
->keyval
== GDK_space
)
793 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
794 may_steal_focus
= TRUE
;
795 /* delay the query of selection state because this callback is executed before GTK
796 * changes the selection (g_signal_connect_after would be better but it doesn't work) */
797 update_selection_change(selection
, event
->keyval
);
799 if (widget
== tv
.tree_openfiles
) /* tag and doc list have separate handlers */
800 g_idle_add(on_openfiles_tree_selection_changed
, NULL
);
802 g_idle_add(on_taglist_tree_selection_changed
, NULL
);
808 static gboolean
sidebar_button_press_cb(GtkWidget
*widget
, GdkEventButton
*event
,
809 G_GNUC_UNUSED gpointer user_data
)
811 GtkTreeSelection
*selection
;
813 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
814 may_steal_focus
= TRUE
;
816 if (event
->type
== GDK_2BUTTON_PRESS
)
817 { /* double click on parent node(section) expands/collapses it */
821 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
823 if (gtk_tree_model_iter_has_child(model
, &iter
))
825 GtkTreePath
*path
= gtk_tree_model_get_path(model
, &iter
);
827 if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget
), path
))
828 gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget
), path
);
830 gtk_tree_view_expand_row(GTK_TREE_VIEW(widget
), path
, FALSE
);
832 gtk_tree_path_free(path
);
837 else if (event
->button
== 1)
838 { /* allow reclicking of taglist treeview item */
839 /* delay the query of selection state because this callback is executed before GTK
840 * changes the selection (g_signal_connect_after would be better but it doesn't work) */
841 update_selection_change(selection
, 0);
843 if (widget
== tv
.tree_openfiles
)
844 g_idle_add(on_openfiles_tree_selection_changed
, NULL
);
846 g_idle_add(on_taglist_tree_selection_changed
, NULL
);
848 else if (event
->button
== 3)
850 if (widget
== tv
.tree_openfiles
)
852 if (!openfiles_popup_menu
)
853 create_openfiles_popup_menu();
855 /* update menu item sensitivity */
856 documents_menu_update(selection
);
857 gtk_menu_popup(GTK_MENU(openfiles_popup_menu
), NULL
, NULL
, NULL
, NULL
,
858 event
->button
, event
->time
);
862 gtk_menu_popup(GTK_MENU(tv
.popup_taglist
), NULL
, NULL
, NULL
, NULL
,
863 event
->button
, event
->time
);
870 static void documents_menu_update(GtkTreeSelection
*selection
)
875 gchar
*shortname
= NULL
;
876 GeanyDocument
*doc
= NULL
;
878 /* maybe no selection e.g. if ctrl-click deselected */
879 sel
= gtk_tree_selection_get_selected(selection
, &model
, &iter
);
882 gtk_tree_model_get(model
, &iter
, DOCUMENTS_DOCUMENT
, &doc
,
883 DOCUMENTS_SHORTNAME
, &shortname
, -1);
885 path
= NZV(shortname
) &&
886 (g_path_is_absolute(shortname
) ||
887 (app
->project
&& g_str_has_prefix(shortname
, app
->project
->name
)));
889 /* can close all, save all (except shortname), but only reload individually ATM */
890 gtk_widget_set_sensitive(doc_items
.close
, sel
);
891 gtk_widget_set_sensitive(doc_items
.save
, (doc
&& doc
->real_path
) || path
);
892 gtk_widget_set_sensitive(doc_items
.reload
, doc
&& doc
->real_path
);
895 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(doc_items
.show_paths
),
896 documents_show_paths
);
900 StashGroup
*stash_group
= NULL
;
902 static void on_load_settings(void)
904 tag_window
= ui_lookup_widget(main_widgets
.window
, "scrolledwindow2");
907 /* note: ui_prefs.sidebar_page is reapplied after plugins are loaded */
908 stash_group_display(stash_group
, NULL
);
909 sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), NULL
, 0, NULL
);
913 static void on_save_settings(void)
915 stash_group_update(stash_group
, NULL
);
916 sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), NULL
, 0, NULL
);
920 void sidebar_init(void)
924 group
= stash_group_new(PACKAGE
);
925 stash_group_add_boolean(group
, &documents_show_paths
, "documents_show_paths", TRUE
);
926 stash_group_add_widget_property(group
, &ui_prefs
.sidebar_page
, "sidebar_page", GINT_TO_POINTER(0),
927 main_widgets
.sidebar_notebook
, "page", 0);
928 configuration_add_pref_group(group
, FALSE
);
931 /* delay building documents treeview until sidebar font has been read */
932 g_signal_connect(geany_object
, "load-settings", on_load_settings
, NULL
);
933 g_signal_connect(geany_object
, "save-settings", on_save_settings
, NULL
);
936 if (gtk_check_version(2, 10, 0) == NULL
)
938 g_signal_connect(main_widgets
.sidebar_notebook
, "page-added",
939 G_CALLBACK(sidebar_tabs_show_hide
), NULL
);
941 g_signal_connect(main_widgets
.sidebar_notebook
, "page-removed",
942 G_CALLBACK(sidebar_tabs_show_hide
), NULL
);
944 sidebar_tabs_show_hide(GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), NULL
, 0, NULL
);
947 #define WIDGET(w) w && GTK_IS_WIDGET(w)
949 void sidebar_finalize(void)
951 if (WIDGET(tv
.default_tag_tree
))
953 g_object_unref(tv
.default_tag_tree
);
954 gtk_widget_destroy(tv
.default_tag_tree
);
956 if (WIDGET(tv
.popup_taglist
))
957 gtk_widget_destroy(tv
.popup_taglist
);
958 if (WIDGET(openfiles_popup_menu
))
959 gtk_widget_destroy(openfiles_popup_menu
);
963 void sidebar_focus_openfiles_tab(void)
965 if (ui_prefs
.sidebar_visible
&& interface_prefs
.sidebar_openfiles_visible
)
967 GtkNotebook
*notebook
= GTK_NOTEBOOK(main_widgets
.sidebar_notebook
);
969 gtk_notebook_set_current_page(notebook
, TREEVIEW_OPENFILES
);
970 gtk_widget_grab_focus(tv
.tree_openfiles
);
975 void sidebar_focus_symbols_tab(void)
977 if (ui_prefs
.sidebar_visible
&& interface_prefs
.sidebar_symbol_visible
)
979 GtkNotebook
*notebook
= GTK_NOTEBOOK(main_widgets
.sidebar_notebook
);
980 GtkWidget
*symbol_list_scrollwin
= gtk_notebook_get_nth_page(notebook
, TREEVIEW_SYMBOL
);
982 gtk_notebook_set_current_page(notebook
, TREEVIEW_SYMBOL
);
983 gtk_widget_grab_focus(gtk_bin_get_child(GTK_BIN(symbol_list_scrollwin
)));
988 static void sidebar_tabs_show_hide(GtkNotebook
*notebook
, GtkWidget
*child
,
989 guint page_num
, gpointer data
)
991 if (gtk_check_version(2, 10, 0) == NULL
)
993 gint tabs
= gtk_notebook_get_n_pages(notebook
);
995 if (interface_prefs
.sidebar_symbol_visible
== FALSE
)
997 if (interface_prefs
.sidebar_openfiles_visible
== FALSE
)
1000 gtk_notebook_set_show_tabs(notebook
, (tabs
> 1));