Compilation fix
[geanyprj.git] / src / sidebar.c
blob18d333ed6d4c27878b0c7affb677b0a786d6e2c8
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>
23 #include <string.h>
25 #include "geany.h"
26 #include "support.h"
27 #include "prefs.h"
28 #include "plugindata.h"
29 #include "document.h"
30 #include "filetypes.h"
31 #include "keybindings.h"
32 #include "utils.h"
33 #include "ui_utils.h"
34 #include "pluginmacros.h"
36 #include "project.h"
38 #include "geanyprj.h"
40 extern GeanyData *geany_data;
41 extern GeanyFunctions *geany_functions;
43 static GtkWidget *file_view_vbox;
44 static GtkWidget *file_view;
45 static GtkListStore *file_store;
47 enum
49 FILEVIEW_COLUMN_NAME = 0,
50 FILEVIEW_N_COLUMNS,
53 static struct
55 GtkWidget *new_project;
56 GtkWidget *delete_project;
58 GtkWidget *add_file;
59 GtkWidget *remove_files;
61 GtkWidget *preferences;
63 GtkWidget *find_in_files;
64 } popup_items;
67 /* Returns: the full filename in locale encoding. */
68 static gchar *
69 get_tree_path_filename(GtkTreePath * treepath)
71 GtkTreeModel *model = GTK_TREE_MODEL(file_store);
72 GtkTreeIter iter;
73 gchar *name;
75 gtk_tree_model_get_iter(model, &iter, treepath);
76 gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_NAME, &name, -1);
77 setptr(name, p_utils->get_locale_from_utf8(name));
78 setptr(name, get_full_path(g_current_project->path, name));
79 return name;
82 /* We use documents->open_files() as it's more efficient. */
83 static void
84 open_selected_files(GList * list)
86 GSList *files = NULL;
87 GList *item;
89 for (item = list; item != NULL; item = g_list_next(item))
91 GtkTreePath *treepath = item->data;
92 gchar *fname = get_tree_path_filename(treepath);
93 files = g_slist_append(files, fname);
95 p_document->open_files(files, FALSE, NULL, NULL);
96 g_slist_foreach(files, (GFunc) g_free, NULL); // free filenames
97 g_slist_free(files);
100 static void
101 on_open_clicked(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
103 GtkTreeSelection *treesel;
104 GtkTreeModel *model;
105 GList *list;
107 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
109 list = gtk_tree_selection_get_selected_rows(treesel, &model);
110 open_selected_files(list);
111 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
112 g_list_free(list);
115 static gboolean
116 on_button_press(G_GNUC_UNUSED GtkWidget * widget, GdkEventButton * event,
117 G_GNUC_UNUSED gpointer user_data)
119 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
120 on_open_clicked(NULL, NULL);
121 return FALSE;
124 static GtkWidget *
125 make_toolbar()
127 GtkWidget *toolbar;
129 toolbar = gtk_toolbar_new();
130 gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_MENU);
131 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
133 return toolbar;
136 static void
137 remove_selected_files(GList * list)
139 GList *item;
140 for (item = list; item != NULL; item = g_list_next(item))
142 GtkTreePath *treepath = item->data;
143 gchar *fname = get_tree_path_filename(treepath);
144 xproject_remove_file(fname);
145 g_free(fname);
149 static void
150 on_remove_files(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data)
152 GtkTreeSelection *treesel;
153 GtkTreeModel *model;
154 GList *list;
156 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
158 list = gtk_tree_selection_get_selected_rows(treesel, &model);
159 remove_selected_files(list);
160 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
161 g_list_free(list);
164 static gboolean
165 on_key_press(G_GNUC_UNUSED GtkWidget * widget, GdkEventKey * event, G_GNUC_UNUSED gpointer data)
167 if (event->keyval == GDK_Return
168 || event->keyval == GDK_ISO_Enter
169 || event->keyval == GDK_KP_Enter || event->keyval == GDK_space)
170 on_open_clicked(NULL, NULL);
171 return FALSE;
174 static GtkWidget *
175 create_popup_menu()
177 GtkWidget *item, *menu, *image;
179 menu = gtk_menu_new();
181 image = gtk_image_new_from_stock(GTK_STOCK_NEW, GTK_ICON_SIZE_MENU);
182 gtk_widget_show(image);
183 item = gtk_image_menu_item_new_with_mnemonic(_("New Project"));
184 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
185 gtk_widget_show(item);
186 gtk_container_add(GTK_CONTAINER(menu), item);
187 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_new_project), NULL);
188 popup_items.new_project = item;
190 image = gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU);
191 gtk_widget_show(image);
192 item = gtk_image_menu_item_new_with_mnemonic(_("Delete Project"));
193 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
194 gtk_widget_show(item);
195 gtk_container_add(GTK_CONTAINER(menu), item);
196 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_delete_project), NULL);
197 popup_items.delete_project = item;
199 item = gtk_separator_menu_item_new();
200 gtk_widget_show(item);
201 gtk_container_add(GTK_CONTAINER(menu), item);
203 image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
204 gtk_widget_show(image);
205 item = gtk_image_menu_item_new_with_mnemonic(_("Add File"));
206 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
207 gtk_widget_show(item);
208 gtk_container_add(GTK_CONTAINER(menu), item);
209 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_add_file), NULL);
210 popup_items.add_file = item;
212 image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
213 gtk_widget_show(image);
214 item = gtk_image_menu_item_new_with_mnemonic(_("Remove File"));
215 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
216 gtk_widget_show(item);
217 gtk_container_add(GTK_CONTAINER(menu), item);
218 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_remove_files), NULL);
219 popup_items.remove_files = item;
221 item = gtk_separator_menu_item_new();
222 gtk_widget_show(item);
223 gtk_container_add(GTK_CONTAINER(menu), item);
225 image = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU);
226 gtk_widget_show(image);
227 item = gtk_image_menu_item_new_with_mnemonic(_("Preferences"));
228 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
229 gtk_widget_show(item);
230 gtk_container_add(GTK_CONTAINER(menu), item);
231 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_preferences), NULL);
232 popup_items.preferences = item;
234 item = gtk_separator_menu_item_new();
235 gtk_widget_show(item);
236 gtk_container_add(GTK_CONTAINER(menu), item);
238 image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
239 gtk_widget_show(image);
240 item = gtk_image_menu_item_new_with_mnemonic(_("Find in Project"));
241 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
242 gtk_widget_show(item);
243 gtk_container_add(GTK_CONTAINER(menu), item);
244 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_find_in_project), NULL);
245 popup_items.find_in_files = item;
247 item = gtk_separator_menu_item_new();
248 gtk_widget_show(item);
249 gtk_container_add(GTK_CONTAINER(menu), item);
251 item = gtk_image_menu_item_new_with_mnemonic(_("H_ide Sidebar"));
252 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
253 gtk_image_new_from_stock("gtk-close", GTK_ICON_SIZE_MENU));
254 gtk_widget_show(item);
255 gtk_container_add(GTK_CONTAINER(menu), item);
256 g_signal_connect_swapped((gpointer) item, "activate",
257 G_CALLBACK(p_keybindings->send_command),
258 GINT_TO_POINTER(GEANY_KEYS_VIEW_SIDEBAR));
260 return menu;
263 static void
264 update_popup_menu(G_GNUC_UNUSED GtkWidget * popup_menu)
266 gint idx;
267 gboolean cur_file_exists;
268 gboolean badd_file;
270 idx = p_document->get_cur_idx();
272 cur_file_exists = DOC_IDX_VALID(idx) &&
273 documents[idx]->file_name != NULL && g_path_is_absolute(documents[idx]->file_name);
275 badd_file = (g_current_project ? TRUE : FALSE) &&
276 !g_current_project->regenerate &&
277 cur_file_exists &&
278 !g_hash_table_lookup(g_current_project->tags, documents[idx]->file_name);
280 GtkTreeSelection *treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
281 gboolean bremove_file = (g_current_project ? TRUE : FALSE) &&
282 !g_current_project->regenerate &&
283 (gtk_tree_selection_count_selected_rows(treesel) > 0);
285 gtk_widget_set_sensitive(popup_items.new_project, TRUE);
286 gtk_widget_set_sensitive(popup_items.delete_project, g_current_project ? TRUE : FALSE);
288 gtk_widget_set_sensitive(popup_items.add_file, badd_file);
289 gtk_widget_set_sensitive(popup_items.remove_files, bremove_file);
291 gtk_widget_set_sensitive(popup_items.preferences, g_current_project ? TRUE : FALSE);
293 gtk_widget_set_sensitive(popup_items.find_in_files, g_current_project ? TRUE : FALSE);
296 // delay updating popup menu until the selection has been set
297 static gboolean
298 on_button_release(G_GNUC_UNUSED GtkWidget * widget, GdkEventButton * event,
299 G_GNUC_UNUSED gpointer user_data)
301 if (event->button == 3)
303 static GtkWidget *popup_menu = NULL;
305 if (popup_menu == NULL)
306 popup_menu = create_popup_menu();
308 update_popup_menu(popup_menu);
310 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, NULL, NULL,
311 event->button, event->time);
313 return FALSE;
316 static void
317 prepare_file_view()
319 GtkCellRenderer *text_renderer;
320 GtkTreeViewColumn *column;
321 GtkTreeSelection *select;
322 PangoFontDescription *pfd;
324 file_store = gtk_list_store_new(FILEVIEW_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
326 gtk_tree_view_set_model(GTK_TREE_VIEW(file_view), GTK_TREE_MODEL(file_store));
328 text_renderer = gtk_cell_renderer_text_new();
329 column = gtk_tree_view_column_new();
330 gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
331 gtk_tree_view_column_set_attributes(column, text_renderer, "text", FILEVIEW_COLUMN_NAME,
332 NULL);
333 gtk_tree_view_append_column(GTK_TREE_VIEW(file_view), column);
334 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(file_view), FALSE);
336 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(file_view), TRUE);
337 gtk_tree_view_set_search_column(GTK_TREE_VIEW(file_view), FILEVIEW_COLUMN_NAME);
339 pfd = pango_font_description_from_string(geany_data->interface_prefs->tagbar_font);
340 gtk_widget_modify_font(file_view, pfd);
341 pango_font_description_free(pfd);
343 // selection handling
344 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
345 gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
347 g_signal_connect(G_OBJECT(file_view), "button-release-event",
348 G_CALLBACK(on_button_release), NULL);
349 g_signal_connect(G_OBJECT(file_view), "button-press-event",
350 G_CALLBACK(on_button_press), NULL);
352 g_signal_connect(G_OBJECT(file_view), "key-press-event", G_CALLBACK(on_key_press), NULL);
355 void
356 sidebar_clear()
358 gtk_list_store_clear(file_store);
361 gint
362 mycmp(const gchar * a, const gchar * b)
364 const gchar *p1 = a;
365 const gchar *p2 = b;
367 gint cnt1 = 0;
368 gint cnt2 = 0;
370 while (*p1)
372 if (*p1 == G_DIR_SEPARATOR_S[0])
373 cnt1++;
374 p1++;
377 while (*p2)
379 if (*p2 == G_DIR_SEPARATOR_S[0])
380 cnt2++;
381 p2++;
384 if (cnt1 != cnt2)
385 return cnt2 - cnt1;
387 p1 = a;
388 p2 = b;
390 while (*p1 && *p2)
392 if (*p1 != *p2)
394 if (*p1 == G_DIR_SEPARATOR_S[0])
395 return -1;
396 else if (*p2 == G_DIR_SEPARATOR_S[0])
397 return 1;
398 return *p1 - *p2;
400 p1++;
401 p2++;
403 if (*p1 == 0 && *p2 == 0)
404 return 0;
405 else if (*p1)
406 return 1;
407 return -1;
410 static void
411 add_item(gpointer name, G_GNUC_UNUSED gpointer value, gpointer user_data)
413 gchar *item;
414 GSList **lst = (GSList **) user_data;
416 item = get_relative_path(g_current_project->path, name);
417 *lst = g_slist_prepend(*lst, item);
420 // recreate the tree model from current_dir.
421 void
422 sidebar_refresh()
424 GtkTreeIter iter;
425 GSList *lst = NULL;
426 GSList *tmp;
428 sidebar_clear();
430 if (!g_current_project)
431 return;
433 g_hash_table_foreach(g_current_project->tags, add_item, &lst);
434 lst = g_slist_sort(lst, (GCompareFunc) strcmp);
435 for (tmp = lst; tmp != NULL; tmp = g_slist_next(tmp))
437 gtk_list_store_append(file_store, &iter);
438 gtk_list_store_set(file_store, &iter, FILEVIEW_COLUMN_NAME, tmp->data, -1);
440 g_slist_foreach(lst, (GFunc) g_free, NULL);
441 g_slist_free(lst);
444 void
445 create_sidebar()
447 GtkWidget *scrollwin, *toolbar;
449 file_view_vbox = gtk_vbox_new(FALSE, 0);
450 toolbar = make_toolbar();
451 gtk_box_pack_start(GTK_BOX(file_view_vbox), toolbar, FALSE, FALSE, 0);
453 file_view = gtk_tree_view_new();
454 prepare_file_view();
456 scrollwin = gtk_scrolled_window_new(NULL, NULL);
457 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
458 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
459 gtk_container_add(GTK_CONTAINER(scrollwin), file_view);
460 gtk_container_add(GTK_CONTAINER(file_view_vbox), scrollwin);
462 gtk_widget_show_all(file_view_vbox);
463 gtk_notebook_append_page(GTK_NOTEBOOK(main_widgets->sidebar_notebook), file_view_vbox,
464 gtk_label_new(_("Project")));
467 void
468 destroy_sidebar()
470 gtk_widget_destroy(file_view_vbox);