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.
21 #include <gdk/gdkkeysyms.h>
26 #include "plugindata.h"
28 #include "filetypes.h"
29 #include "keybindings.h"
31 #include "pluginmacros.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
;
47 FILEVIEW_COLUMN_NAME
= 0,
53 GtkWidget
*new_project
;
54 GtkWidget
*delete_project
;
57 GtkWidget
*remove_files
;
59 GtkWidget
*preferences
;
61 GtkWidget
*find_in_files
;
65 /* Returns: the full filename in locale encoding. */
67 get_tree_path_filename(GtkTreePath
* treepath
)
69 GtkTreeModel
*model
= GTK_TREE_MODEL(file_store
);
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
));
79 /* We use documents->open_files() as it's more efficient. */
81 open_selected_files(GList
* list
)
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
99 on_open_clicked(GtkMenuItem
* menuitem
, gpointer user_data
)
101 GtkTreeSelection
*treesel
;
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
);
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
);
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
);
134 // simple struct to keep references to the elements of the properties dialog
135 typedef struct _PropertyDialogElements
139 GtkWidget
*description
;
140 GtkWidget
*file_name
;
141 GtkWidget
*base_path
;
142 GtkWidget
*make_in_base_path
;
145 } PropertyDialogElements
;
147 static gboolean entries_modified
;
150 on_entries_changed(GtkEditable
* editable
, PropertyDialogElements
* e
)
152 entries_modified
= TRUE
;
156 on_new_project(GtkMenuItem
* menuitem
, gpointer user_data
)
164 GtkTooltips
*tooltips
= GTK_TOOLTIPS(p_support
->lookup_widget(app
->window
, "tooltips"));
165 PropertyDialogElements
*e
;
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
);
241 response
= gtk_dialog_run(GTK_DIALOG(e
->dialog
));
242 if (response
== GTK_RESPONSE_OK
)
244 // if (! update_config(e))
248 gtk_widget_destroy(e
->dialog
);
254 on_delete_project(GtkMenuItem
* menuitem
, gpointer user_data
)
257 if (!g_current_project
)
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
);
271 on_add_file(GtkMenuItem
* menuitem
, gpointer user_data
)
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
)
283 xproject_add_file(doc_list
[idx
].file_name
);
287 remove_selected_files(GList
* list
)
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
);
300 on_remove_files(GtkMenuItem
* menuitem
, gpointer user_data
)
302 GtkTreeSelection
*treesel
;
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
);
316 on_preferences(GtkMenuItem
* menuitem
, gpointer user_data
)
321 on_find_in_project(GtkMenuItem
* menuitem
, gpointer user_data
)
324 if (!g_current_project
)
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
);
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
);
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
));
433 update_popup_menu(GtkWidget
* popup_menu
)
436 gboolean cur_file_exists
;
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
) &&
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
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
);
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
,
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
);
529 gtk_list_store_clear(file_store
);
533 add_item(gpointer name
, gpointer value
, gpointer user_data
)
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.
548 if (!g_current_project
)
551 g_hash_table_foreach(g_current_project
->tags
, add_item
, NULL
);
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();
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")));
580 gtk_widget_destroy(file_view_vbox
);