added get_file_list func
[geanyprj.git] / sidebar.c
blobbe4069def9409f3636cd67efe20426221d41d45b
1 /*
2 * sidebar.c - 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 2 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, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <sys/time.h>
21 #include <gdk/gdkkeysyms.h>
23 #include "geany.h"
24 #include "support.h"
25 #include "prefs.h"
26 #include "plugindata.h"
27 #include "document.h"
28 #include "filetypes.h"
29 #include "keybindings.h"
30 #include "utils.h"
31 #include "pluginmacros.h"
33 #include "project.h"
35 #include "geanyprj.h"
37 #define MAX_NAME_LEN 50
39 extern GeanyData *geany_data;
41 static GtkWidget *file_view_vbox;
42 static GtkWidget *file_view;
43 static GtkListStore *file_store;
45 enum
47 FILEVIEW_COLUMN_NAME = 0,
48 FILEVIEW_N_COLUMNS,
51 static struct
53 GtkWidget *new_project;
54 GtkWidget *delete_project;
56 GtkWidget *add_file;
57 GtkWidget *remove_files;
59 GtkWidget *preferences;
61 GtkWidget *find_in_files;
62 } popup_items;
65 /* Returns: the full filename in locale encoding. */
66 static gchar *
67 get_tree_path_filename(GtkTreePath * treepath)
69 GtkTreeModel *model = GTK_TREE_MODEL(file_store);
70 GtkTreeIter iter;
71 gchar *name;
73 gtk_tree_model_get_iter(model, &iter, treepath);
74 gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_NAME, &name, -1);
75 setptr(name, p_utils->get_locale_from_utf8(name));
76 return name;
79 /* We use documents->open_files() as it's more efficient. */
80 static void
81 open_selected_files(GList * list)
83 GSList *files = NULL;
84 GList *item;
86 for (item = list; item != NULL; item = g_list_next(item))
88 GtkTreePath *treepath = item->data;
89 gchar *fname = get_tree_path_filename(treepath);
91 files = g_slist_append(files, fname);
93 p_document->open_files(files, FALSE, NULL, NULL);
94 g_slist_foreach(files, (GFunc) g_free, NULL); // free filenames
95 g_slist_free(files);
98 static void
99 on_open_clicked(GtkMenuItem * menuitem, gpointer user_data)
101 GtkTreeSelection *treesel;
102 GtkTreeModel *model;
103 GList *list;
104 gboolean dir_found;
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(GtkWidget * widget, GdkEventButton * event, gpointer user_data)
117 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
118 on_open_clicked(NULL, NULL);
119 return FALSE;
122 static GtkWidget *
123 make_toolbar()
125 GtkWidget *toolbar;
127 toolbar = gtk_toolbar_new();
128 gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_MENU);
129 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
131 return toolbar;
134 // simple struct to keep references to the elements of the properties dialog
135 typedef struct _PropertyDialogElements
137 GtkWidget *dialog;
138 GtkWidget *name;
139 GtkWidget *description;
140 GtkWidget *file_name;
141 GtkWidget *base_path;
142 GtkWidget *make_in_base_path;
143 GtkWidget *run_cmd;
144 GtkWidget *patterns;
145 } PropertyDialogElements;
147 static gboolean entries_modified;
149 static void
150 on_entries_changed(GtkEditable * editable, PropertyDialogElements * e)
152 entries_modified = TRUE;
155 static void
156 on_new_project(GtkMenuItem * menuitem, gpointer user_data)
158 GtkWidget *vbox;
159 GtkWidget *table;
160 GtkWidget *image;
161 GtkWidget *button;
162 GtkWidget *bbox;
163 GtkWidget *label;
164 GtkTooltips *tooltips = GTK_TOOLTIPS(p_support->lookup_widget(app->window, "tooltips"));
165 PropertyDialogElements *e;
166 gint response;
167 gchar *dir = NULL;
168 gint idx;
170 idx = p_document->get_cur_idx();
171 if (DOC_IDX_VALID(idx) &&
172 doc_list[idx].file_name != NULL && g_path_is_absolute(doc_list[idx].file_name))
174 dir = g_path_get_dirname(doc_list[idx].file_name);
177 e = g_new0(PropertyDialogElements, 1);
178 e->dialog = gtk_dialog_new_with_buttons(_("New Project"), GTK_WINDOW(app->window),
179 GTK_DIALOG_DESTROY_WITH_PARENT,
180 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
182 gtk_widget_set_name(e->dialog, "GeanyDialogProject");
183 bbox = gtk_hbox_new(FALSE, 0);
184 button = gtk_button_new();
185 image = gtk_image_new_from_stock("gtk-new", GTK_ICON_SIZE_BUTTON);
186 label = gtk_label_new_with_mnemonic(_("C_reate"));
187 gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 3);
188 gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 3);
189 gtk_container_add(GTK_CONTAINER(button), bbox);
190 gtk_dialog_add_action_widget(GTK_DIALOG(e->dialog), button, GTK_RESPONSE_OK);
192 vbox = p_ui->dialog_vbox_new(GTK_DIALOG(e->dialog));
194 entries_modified = FALSE;
196 table = gtk_table_new(3, 2, FALSE);
197 gtk_table_set_row_spacings(GTK_TABLE(table), 5);
198 gtk_table_set_col_spacings(GTK_TABLE(table), 10);
200 label = gtk_label_new(_("Name:"));
201 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
203 e->name = gtk_entry_new();
204 gtk_entry_set_max_length(GTK_ENTRY(e->name), MAX_NAME_LEN);
206 p_ui->table_add_row(GTK_TABLE(table), 0, label, e->name, NULL);
208 label = gtk_label_new(_("Location:"));
209 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
211 e->file_name = gtk_entry_new();
212 gtk_entry_set_width_chars(GTK_ENTRY(e->file_name), 30);
213 button = gtk_button_new();
214 image = gtk_image_new_from_stock("gtk-open", GTK_ICON_SIZE_BUTTON);
215 gtk_container_add(GTK_CONTAINER(button), image);
216 bbox = p_ui->path_box_new(_("Choose Project Location"),
217 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_ENTRY(e->file_name));
218 gtk_entry_set_text(GTK_ENTRY(e->file_name), dir);
220 p_ui->table_add_row(GTK_TABLE(table), 1, label, bbox, NULL);
222 label = gtk_label_new(_("Base path:"));
223 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
225 e->base_path = gtk_entry_new();
226 gtk_tooltips_set_tip(tooltips, e->base_path,
227 _("Base directory of all files that make up the project. "
228 "This can be a new path, or an existing directory tree. "
229 "You can use paths relative to the project filename."), NULL);
230 bbox = p_ui->path_box_new(_("Choose Project Base Path"),
231 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_ENTRY(e->base_path));
232 gtk_entry_set_text(GTK_ENTRY(e->base_path), dir);
234 p_ui->table_add_row(GTK_TABLE(table), 2, label, bbox, NULL);
236 gtk_container_add(GTK_CONTAINER(vbox), table);
238 gtk_widget_show_all(e->dialog);
240 retry:
241 response = gtk_dialog_run(GTK_DIALOG(e->dialog));
242 if (response == GTK_RESPONSE_OK)
244 // if (! update_config(e))
245 // goto retry;
248 gtk_widget_destroy(e->dialog);
249 g_free(e);
250 g_free(dir);
253 static void
254 on_delete_project(GtkMenuItem * menuitem, gpointer user_data)
256 gchar *path;
257 if (!g_current_project)
258 return;
260 if (p_dialogs->show_question("Do you really wish to delete current project:\n%s?",
261 g_current_project->name))
263 path = p_utils->get_locale_from_utf8(g_current_project->path);
264 xproject_close();
265 unlink(path);
266 g_free(path);
270 static void
271 on_add_file(GtkMenuItem * menuitem, gpointer user_data)
273 gint idx;
275 idx = p_document->get_cur_idx();
276 g_return_if_fail(DOC_IDX_VALID(idx) &&
277 doc_list[idx].file_name != NULL &&
278 g_path_is_absolute(doc_list[idx].file_name));
280 if (!g_current_project)
281 return;
283 xproject_add_file(doc_list[idx].file_name);
286 static void
287 remove_selected_files(GList * list)
289 GList *item;
290 for (item = list; item != NULL; item = g_list_next(item))
292 GtkTreePath *treepath = item->data;
293 gchar *fname = get_tree_path_filename(treepath);
294 xproject_remove_file(fname);
295 g_free(fname);
299 static void
300 on_remove_files(GtkMenuItem * menuitem, gpointer user_data)
302 GtkTreeSelection *treesel;
303 GtkTreeModel *model;
304 GList *list;
305 gboolean dir_found;
307 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
309 list = gtk_tree_selection_get_selected_rows(treesel, &model);
310 remove_selected_files(list);
311 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
312 g_list_free(list);
315 static void
316 on_preferences(GtkMenuItem * menuitem, gpointer user_data)
320 static void
321 on_find_in_project(GtkMenuItem * menuitem, gpointer user_data)
323 gchar *dir;
324 if (!g_current_project)
325 return;
327 dir = g_path_get_dirname(g_current_project->path);
328 setptr(dir, g_build_filename(dir, g_current_project->base_path, NULL));
329 p_search->show_find_in_files_dialog(dir);
330 g_free(dir);
333 static gboolean
334 on_key_press(GtkWidget * widget, GdkEventKey * event, gpointer data)
336 if (event->keyval == GDK_Return
337 || event->keyval == GDK_ISO_Enter
338 || event->keyval == GDK_KP_Enter || event->keyval == GDK_space)
339 on_open_clicked(NULL, NULL);
340 return FALSE;
343 static GtkWidget *
344 create_popup_menu()
346 GtkWidget *item, *menu, *image;
348 menu = gtk_menu_new();
350 image = gtk_image_new_from_stock(GTK_STOCK_NEW, GTK_ICON_SIZE_MENU);
351 gtk_widget_show(image);
352 item = gtk_image_menu_item_new_with_mnemonic(_("New Project"));
353 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
354 gtk_widget_show(item);
355 gtk_container_add(GTK_CONTAINER(menu), item);
356 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_new_project), NULL);
357 popup_items.new_project = item;
359 image = gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU);
360 gtk_widget_show(image);
361 item = gtk_image_menu_item_new_with_mnemonic(_("Delete Project"));
362 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
363 gtk_widget_show(item);
364 gtk_container_add(GTK_CONTAINER(menu), item);
365 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_delete_project), NULL);
366 popup_items.delete_project = item;
368 item = gtk_separator_menu_item_new();
369 gtk_widget_show(item);
370 gtk_container_add(GTK_CONTAINER(menu), item);
372 image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
373 gtk_widget_show(image);
374 item = gtk_image_menu_item_new_with_mnemonic(_("Add File"));
375 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
376 gtk_widget_show(item);
377 gtk_container_add(GTK_CONTAINER(menu), item);
378 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_add_file), NULL);
379 popup_items.add_file = item;
381 image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
382 gtk_widget_show(image);
383 item = gtk_image_menu_item_new_with_mnemonic(_("Remove File"));
384 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
385 gtk_widget_show(item);
386 gtk_container_add(GTK_CONTAINER(menu), item);
387 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_remove_files), NULL);
388 popup_items.remove_files = item;
390 item = gtk_separator_menu_item_new();
391 gtk_widget_show(item);
392 gtk_container_add(GTK_CONTAINER(menu), item);
394 image = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU);
395 gtk_widget_show(image);
396 item = gtk_image_menu_item_new_with_mnemonic(_("Preferences"));
397 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
398 gtk_widget_show(item);
399 gtk_container_add(GTK_CONTAINER(menu), item);
400 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_preferences), NULL);
401 popup_items.preferences = item;
403 item = gtk_separator_menu_item_new();
404 gtk_widget_show(item);
405 gtk_container_add(GTK_CONTAINER(menu), item);
407 image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
408 gtk_widget_show(image);
409 item = gtk_image_menu_item_new_with_mnemonic(_("Find in Project"));
410 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
411 gtk_widget_show(item);
412 gtk_container_add(GTK_CONTAINER(menu), item);
413 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_find_in_project), NULL);
414 popup_items.find_in_files = item;
416 item = gtk_separator_menu_item_new();
417 gtk_widget_show(item);
418 gtk_container_add(GTK_CONTAINER(menu), item);
420 item = gtk_image_menu_item_new_with_mnemonic(_("H_ide Sidebar"));
421 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
422 gtk_image_new_from_stock("gtk-close", GTK_ICON_SIZE_MENU));
423 gtk_widget_show(item);
424 gtk_container_add(GTK_CONTAINER(menu), item);
425 g_signal_connect_swapped((gpointer) item, "activate",
426 G_CALLBACK(p_keybindings->send_command),
427 GINT_TO_POINTER(GEANY_KEYS_MENU_SIDEBAR));
429 return menu;
432 static void
433 update_popup_menu(GtkWidget * popup_menu)
435 gint idx;
436 gboolean cur_file_exists;
437 gboolean badd_file;
439 idx = p_document->get_cur_idx();
441 cur_file_exists = DOC_IDX_VALID(idx) &&
442 doc_list[idx].file_name != NULL && g_path_is_absolute(doc_list[idx].file_name);
444 badd_file = (g_current_project ? TRUE : FALSE) &&
445 cur_file_exists &&
446 !g_hash_table_lookup(g_current_project->tags, doc_list[idx].file_name);
448 GtkTreeSelection *treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
449 gboolean have_sel = (gtk_tree_selection_count_selected_rows(treesel) > 0);
450 // gboolean multi_sel = (gtk_tree_selection_count_selected_rows(treesel) > 1);
453 gtk_widget_set_sensitive(popup_items.open, have_sel);
454 gtk_widget_set_sensitive(popup_items.open_external, have_sel);
455 gtk_widget_set_sensitive(popup_items.find_in_files, have_sel && ! multi_sel);
457 gtk_widget_set_sensitive(popup_items.new_project, TRUE);
458 gtk_widget_set_sensitive(popup_items.delete_project, g_current_project ? TRUE : FALSE);
460 gtk_widget_set_sensitive(popup_items.add_file, badd_file);
461 gtk_widget_set_sensitive(popup_items.remove_files, have_sel);
463 gtk_widget_set_sensitive(popup_items.preferences, FALSE);
465 gtk_widget_set_sensitive(popup_items.find_in_files, g_current_project ? TRUE : FALSE);
468 // delay updating popup menu until the selection has been set
469 static gboolean
470 on_button_release(GtkWidget * widget, GdkEventButton * event, gpointer user_data)
472 if (event->button == 3)
474 static GtkWidget *popup_menu = NULL;
476 if (popup_menu == NULL)
477 popup_menu = create_popup_menu();
479 update_popup_menu(popup_menu);
481 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, NULL, NULL,
482 event->button, event->time);
484 return FALSE;
487 static void
488 prepare_file_view()
490 GtkCellRenderer *text_renderer;
491 GtkTreeViewColumn *column;
492 GtkTreeSelection *select;
493 PangoFontDescription *pfd;
495 file_store = gtk_list_store_new(FILEVIEW_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
497 gtk_tree_view_set_model(GTK_TREE_VIEW(file_view), GTK_TREE_MODEL(file_store));
499 text_renderer = gtk_cell_renderer_text_new();
500 column = gtk_tree_view_column_new();
501 gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
502 gtk_tree_view_column_set_attributes(column, text_renderer, "text", FILEVIEW_COLUMN_NAME,
503 NULL);
504 gtk_tree_view_append_column(GTK_TREE_VIEW(file_view), column);
505 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(file_view), FALSE);
507 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(file_view), TRUE);
508 gtk_tree_view_set_search_column(GTK_TREE_VIEW(file_view), FILEVIEW_COLUMN_NAME);
510 pfd = pango_font_description_from_string(prefs->tagbar_font);
511 gtk_widget_modify_font(file_view, pfd);
512 pango_font_description_free(pfd);
514 // selection handling
515 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
516 gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
518 g_signal_connect(G_OBJECT(file_view), "button-release-event",
519 G_CALLBACK(on_button_release), NULL);
520 g_signal_connect(G_OBJECT(file_view), "button-press-event",
521 G_CALLBACK(on_button_press), NULL);
523 g_signal_connect(G_OBJECT(file_view), "key-press-event", G_CALLBACK(on_key_press), NULL);
526 void
527 sidebar_clear()
529 gtk_list_store_clear(file_store);
532 static void
533 add_item(gpointer name, gpointer value, gpointer user_data)
535 GtkTreeIter iter;
536 debug("%s name %s\n", __FUNCTION__, (gchar *) name);
538 gtk_list_store_append(file_store, &iter);
539 gtk_list_store_set(file_store, &iter, FILEVIEW_COLUMN_NAME, name, -1);
542 // recreate the tree model from current_dir.
543 void
544 sidebar_refresh()
546 sidebar_clear();
548 if (!g_current_project)
549 return;
551 g_hash_table_foreach(g_current_project->tags, add_item, NULL);
554 void
555 create_sidebar()
557 GtkWidget *scrollwin, *toolbar;
559 file_view_vbox = gtk_vbox_new(FALSE, 0);
560 toolbar = make_toolbar();
561 gtk_box_pack_start(GTK_BOX(file_view_vbox), toolbar, FALSE, FALSE, 0);
563 file_view = gtk_tree_view_new();
564 prepare_file_view();
566 scrollwin = gtk_scrolled_window_new(NULL, NULL);
567 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
568 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
569 gtk_container_add(GTK_CONTAINER(scrollwin), file_view);
570 gtk_container_add(GTK_CONTAINER(file_view_vbox), scrollwin);
572 gtk_widget_show_all(file_view_vbox);
573 gtk_notebook_append_page(GTK_NOTEBOOK(app->treeview_notebook), file_view_vbox,
574 gtk_label_new(_("Project")));
577 void
578 destroy_sidebar()
580 gtk_widget_destroy(file_view_vbox);