compilation fixes for geany svn
[geanyprj.git] / src / sidebar.c
blobd63b211e8d4a544bb56fd00bf1933e164ad6afbb
1 /*
2 * geanyprj - Alternative project support for geany light IDE.
4 * Copyright 2008 Yura Siamashka <yurand2@gmail.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <sys/time.h>
22 #include <gdk/gdkkeysyms.h>
24 #include "geany.h"
25 #include "support.h"
26 #include "prefs.h"
27 #include "plugindata.h"
28 #include "document.h"
29 #include "filetypes.h"
30 #include "keybindings.h"
31 #include "utils.h"
32 #include "ui_utils.h"
33 #include "pluginmacros.h"
35 #include "project.h"
37 #include "geanyprj.h"
39 extern GeanyData *geany_data;
40 extern GeanyFunctions *geany_functions;
42 static GtkWidget *file_view_vbox;
43 static GtkWidget *file_view;
44 static GtkListStore *file_store;
46 enum
48 FILEVIEW_COLUMN_NAME = 0,
49 FILEVIEW_N_COLUMNS,
52 static struct
54 GtkWidget *new_project;
55 GtkWidget *delete_project;
57 GtkWidget *add_file;
58 GtkWidget *remove_files;
60 GtkWidget *preferences;
62 GtkWidget *find_in_files;
63 } popup_items;
66 /* Returns: the full filename in locale encoding. */
67 static gchar *
68 get_tree_path_filename(GtkTreePath * treepath)
70 GtkTreeModel *model = GTK_TREE_MODEL(file_store);
71 GtkTreeIter iter;
72 gchar *name;
74 gtk_tree_model_get_iter(model, &iter, treepath);
75 gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_NAME, &name, -1);
76 setptr(name, p_utils->get_locale_from_utf8(name));
77 setptr(name, get_full_path(g_current_project->path, name));
78 return name;
81 /* We use documents->open_files() as it's more efficient. */
82 static void
83 open_selected_files(GList * list)
85 GSList *files = NULL;
86 GList *item;
88 for (item = list; item != NULL; item = g_list_next(item))
90 GtkTreePath *treepath = item->data;
91 gchar *fname = get_tree_path_filename(treepath);
92 files = g_slist_append(files, fname);
94 p_document->open_files(files, FALSE, NULL, NULL);
95 g_slist_foreach(files, (GFunc) g_free, NULL); // free filenames
96 g_slist_free(files);
99 static void
100 on_open_clicked(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
102 GtkTreeSelection *treesel;
103 GtkTreeModel *model;
104 GList *list;
106 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
108 list = gtk_tree_selection_get_selected_rows(treesel, &model);
109 open_selected_files(list);
110 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
111 g_list_free(list);
114 static gboolean
115 on_button_press(G_GNUC_UNUSED GtkWidget * widget, GdkEventButton * event,
116 G_GNUC_UNUSED gpointer user_data)
118 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
119 on_open_clicked(NULL, NULL);
120 return FALSE;
123 static GtkWidget *
124 make_toolbar()
126 GtkWidget *toolbar;
128 toolbar = gtk_toolbar_new();
129 gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_MENU);
130 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
132 return toolbar;
135 static void
136 remove_selected_files(GList * list)
138 GList *item;
139 for (item = list; item != NULL; item = g_list_next(item))
141 GtkTreePath *treepath = item->data;
142 gchar *fname = get_tree_path_filename(treepath);
143 xproject_remove_file(fname);
144 g_free(fname);
148 static void
149 on_remove_files(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
151 GtkTreeSelection *treesel;
152 GtkTreeModel *model;
153 GList *list;
155 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
157 list = gtk_tree_selection_get_selected_rows(treesel, &model);
158 remove_selected_files(list);
159 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
160 g_list_free(list);
163 static gboolean
164 on_key_press(G_GNUC_UNUSED GtkWidget * widget, GdkEventKey * event, G_GNUC_UNUSED gpointer data)
166 if (event->keyval == GDK_Return
167 || event->keyval == GDK_ISO_Enter
168 || event->keyval == GDK_KP_Enter || event->keyval == GDK_space)
169 on_open_clicked(NULL, NULL);
170 return FALSE;
173 static GtkWidget *
174 create_popup_menu()
176 GtkWidget *item, *menu, *image;
178 menu = gtk_menu_new();
180 image = gtk_image_new_from_stock(GTK_STOCK_NEW, GTK_ICON_SIZE_MENU);
181 gtk_widget_show(image);
182 item = gtk_image_menu_item_new_with_mnemonic(_("New Project"));
183 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
184 gtk_widget_show(item);
185 gtk_container_add(GTK_CONTAINER(menu), item);
186 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_new_project), NULL);
187 popup_items.new_project = item;
189 image = gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU);
190 gtk_widget_show(image);
191 item = gtk_image_menu_item_new_with_mnemonic(_("Delete Project"));
192 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
193 gtk_widget_show(item);
194 gtk_container_add(GTK_CONTAINER(menu), item);
195 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_delete_project), NULL);
196 popup_items.delete_project = item;
198 item = gtk_separator_menu_item_new();
199 gtk_widget_show(item);
200 gtk_container_add(GTK_CONTAINER(menu), item);
202 image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
203 gtk_widget_show(image);
204 item = gtk_image_menu_item_new_with_mnemonic(_("Add File"));
205 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
206 gtk_widget_show(item);
207 gtk_container_add(GTK_CONTAINER(menu), item);
208 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_add_file), NULL);
209 popup_items.add_file = item;
211 image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
212 gtk_widget_show(image);
213 item = gtk_image_menu_item_new_with_mnemonic(_("Remove File"));
214 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
215 gtk_widget_show(item);
216 gtk_container_add(GTK_CONTAINER(menu), item);
217 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_remove_files), NULL);
218 popup_items.remove_files = item;
220 item = gtk_separator_menu_item_new();
221 gtk_widget_show(item);
222 gtk_container_add(GTK_CONTAINER(menu), item);
224 image = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU);
225 gtk_widget_show(image);
226 item = gtk_image_menu_item_new_with_mnemonic(_("Preferences"));
227 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
228 gtk_widget_show(item);
229 gtk_container_add(GTK_CONTAINER(menu), item);
230 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_preferences), NULL);
231 popup_items.preferences = item;
233 item = gtk_separator_menu_item_new();
234 gtk_widget_show(item);
235 gtk_container_add(GTK_CONTAINER(menu), item);
237 image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
238 gtk_widget_show(image);
239 item = gtk_image_menu_item_new_with_mnemonic(_("Find in Project"));
240 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
241 gtk_widget_show(item);
242 gtk_container_add(GTK_CONTAINER(menu), item);
243 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_find_in_project), NULL);
244 popup_items.find_in_files = item;
246 item = gtk_separator_menu_item_new();
247 gtk_widget_show(item);
248 gtk_container_add(GTK_CONTAINER(menu), item);
250 item = gtk_image_menu_item_new_with_mnemonic(_("H_ide Sidebar"));
251 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
252 gtk_image_new_from_stock("gtk-close", GTK_ICON_SIZE_MENU));
253 gtk_widget_show(item);
254 gtk_container_add(GTK_CONTAINER(menu), item);
255 g_signal_connect_swapped((gpointer) item, "activate",
256 G_CALLBACK(p_keybindings->send_command),
257 GINT_TO_POINTER(GEANY_KEYS_VIEW_SIDEBAR));
259 return menu;
262 static void
263 update_popup_menu(G_GNUC_UNUSED GtkWidget * popup_menu)
265 gint idx;
266 gboolean cur_file_exists;
267 gboolean badd_file;
269 idx = p_document->get_cur_idx();
271 cur_file_exists = DOC_IDX_VALID(idx) &&
272 doc_list[idx].file_name != NULL && g_path_is_absolute(doc_list[idx].file_name);
274 badd_file = (g_current_project ? TRUE : FALSE) &&
275 !g_current_project->regenerate &&
276 cur_file_exists &&
277 !g_hash_table_lookup(g_current_project->tags, doc_list[idx].file_name);
279 GtkTreeSelection *treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
280 gboolean bremove_file = (g_current_project ? TRUE : FALSE) &&
281 !g_current_project->regenerate &&
282 (gtk_tree_selection_count_selected_rows(treesel) > 0);
284 gtk_widget_set_sensitive(popup_items.new_project, TRUE);
285 gtk_widget_set_sensitive(popup_items.delete_project, g_current_project ? TRUE : FALSE);
287 gtk_widget_set_sensitive(popup_items.add_file, badd_file);
288 gtk_widget_set_sensitive(popup_items.remove_files, bremove_file);
290 gtk_widget_set_sensitive(popup_items.preferences, g_current_project ? TRUE : FALSE);
292 gtk_widget_set_sensitive(popup_items.find_in_files, g_current_project ? TRUE : FALSE);
295 // delay updating popup menu until the selection has been set
296 static gboolean
297 on_button_release(G_GNUC_UNUSED GtkWidget * widget, GdkEventButton * event,
298 G_GNUC_UNUSED gpointer user_data)
300 if (event->button == 3)
302 static GtkWidget *popup_menu = NULL;
304 if (popup_menu == NULL)
305 popup_menu = create_popup_menu();
307 update_popup_menu(popup_menu);
309 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, NULL, NULL,
310 event->button, event->time);
312 return FALSE;
315 static void
316 prepare_file_view()
318 GtkCellRenderer *text_renderer;
319 GtkTreeViewColumn *column;
320 GtkTreeSelection *select;
321 PangoFontDescription *pfd;
323 file_store = gtk_list_store_new(FILEVIEW_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
325 gtk_tree_view_set_model(GTK_TREE_VIEW(file_view), GTK_TREE_MODEL(file_store));
327 text_renderer = gtk_cell_renderer_text_new();
328 column = gtk_tree_view_column_new();
329 gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
330 gtk_tree_view_column_set_attributes(column, text_renderer, "text", FILEVIEW_COLUMN_NAME,
331 NULL);
332 gtk_tree_view_append_column(GTK_TREE_VIEW(file_view), column);
333 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(file_view), FALSE);
335 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(file_view), TRUE);
336 gtk_tree_view_set_search_column(GTK_TREE_VIEW(file_view), FILEVIEW_COLUMN_NAME);
338 pfd = pango_font_description_from_string(geany_data->interface_prefs->tagbar_font);
339 gtk_widget_modify_font(file_view, pfd);
340 pango_font_description_free(pfd);
342 // selection handling
343 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
344 gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
346 g_signal_connect(G_OBJECT(file_view), "button-release-event",
347 G_CALLBACK(on_button_release), NULL);
348 g_signal_connect(G_OBJECT(file_view), "button-press-event",
349 G_CALLBACK(on_button_press), NULL);
351 g_signal_connect(G_OBJECT(file_view), "key-press-event", G_CALLBACK(on_key_press), NULL);
354 void
355 sidebar_clear()
357 gtk_list_store_clear(file_store);
360 gint
361 mycmp(const gchar * a, const gchar * b)
363 const gchar *p1 = a;
364 const gchar *p2 = b;
366 gint cnt1 = 0;
367 gint cnt2 = 0;
369 while (*p1)
371 if (*p1 == G_DIR_SEPARATOR_S[0])
372 cnt1++;
373 p1++;
376 while (*p2)
378 if (*p2 == G_DIR_SEPARATOR_S[0])
379 cnt2++;
380 p2++;
383 if (cnt1 != cnt2)
384 return cnt2 - cnt1;
386 p1 = a;
387 p2 = b;
389 while (*p1 && *p2)
391 if (*p1 != *p2)
393 if (*p1 == G_DIR_SEPARATOR_S[0])
394 return -1;
395 else if (*p2 == G_DIR_SEPARATOR_S[0])
396 return 1;
397 return *p1 - *p2;
399 p1++;
400 p2++;
402 if (*p1 == 0 && *p2 == 0)
403 return 0;
404 else if (*p1)
405 return 1;
406 return -1;
409 static void
410 add_item(gpointer name, G_GNUC_UNUSED gpointer value, gpointer user_data)
412 gchar *item;
413 GSList **lst = (GSList **) user_data;
415 item = get_relative_path(g_current_project->path, name);
416 *lst = g_slist_prepend(*lst, item);
419 // recreate the tree model from current_dir.
420 void
421 sidebar_refresh()
423 GtkTreeIter iter;
424 GSList *lst = NULL;
425 GSList *tmp;
427 sidebar_clear();
429 if (!g_current_project)
430 return;
432 g_hash_table_foreach(g_current_project->tags, add_item, &lst);
433 lst = g_slist_sort(lst, (GCompareFunc) strcmp);
434 for (tmp = lst; tmp != NULL; tmp = g_slist_next(tmp))
436 gtk_list_store_append(file_store, &iter);
437 gtk_list_store_set(file_store, &iter, FILEVIEW_COLUMN_NAME, tmp->data, -1);
439 g_slist_foreach(lst, (GFunc) g_free, NULL);
440 g_slist_free(lst);
443 void
444 create_sidebar()
446 GtkWidget *scrollwin, *toolbar;
448 file_view_vbox = gtk_vbox_new(FALSE, 0);
449 toolbar = make_toolbar();
450 gtk_box_pack_start(GTK_BOX(file_view_vbox), toolbar, FALSE, FALSE, 0);
452 file_view = gtk_tree_view_new();
453 prepare_file_view();
455 scrollwin = gtk_scrolled_window_new(NULL, NULL);
456 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
457 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
458 gtk_container_add(GTK_CONTAINER(scrollwin), file_view);
459 gtk_container_add(GTK_CONTAINER(file_view_vbox), scrollwin);
461 gtk_widget_show_all(file_view_vbox);
462 gtk_notebook_append_page(GTK_NOTEBOOK(app->treeview_notebook), file_view_vbox,
463 gtk_label_new(_("Project")));
466 void
467 destroy_sidebar()
469 gtk_widget_destroy(file_view_vbox);