geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

filebrowser.c
Go to the documentation of this file.
1/*
2 * filebrowser.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2007 The Geany contributors
5 *
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.
10 *
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.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/* Sidebar file browser plugin. */
22
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include "geanyplugin.h"
28#include "gtkcompat.h"
29#include <string.h>
30
31#include <gdk/gdkkeysyms.h>
32
33#ifdef G_OS_WIN32
34# include <windows.h>
35
36# define OPEN_CMD "explorer \"%d\""
37#elif defined(__APPLE__)
38# define OPEN_CMD "open \"%d\""
39#else
40# define OPEN_CMD "nautilus \"%d\""
41#endif
42
45
46
48
49PLUGIN_SET_INFO(_("File Browser"), _("Adds a file browser tab to the sidebar."), VERSION,
50 _("The Geany developer team"))
51
52
53/* Keybinding(s) */
54enum
55{
59};
60
61
62enum
63{
66 FILEVIEW_COLUMN_FILENAME, /* the full filename, including path for display as tooltip */
69};
70
71static gboolean fb_set_project_base_path = FALSE;
72static gboolean fb_follow_path = FALSE;
73static gboolean show_hidden_files = FALSE;
74static gboolean hide_object_files = TRUE;
75
76static GtkWidget *file_view_vbox;
77static GtkWidget *file_view;
78static GtkListStore *file_store;
79static GtkTreeIter *last_dir_iter = NULL;
80static GtkEntryCompletion *entry_completion = NULL;
81
82static GtkWidget *filter_combo;
83static GtkWidget *filter_entry;
84static GtkWidget *path_combo;
85static GtkWidget *path_entry;
86static gchar *current_dir = NULL; /* in locale-encoding */
87static gchar *open_cmd; /* in locale-encoding */
88static gchar *config_file;
89static gchar **filter = NULL;
91
92static gint page_number = 0;
93
94static struct
95{
96 GtkWidget *open;
97 GtkWidget *open_external;
98 GtkWidget *find_in_files;
99 GtkWidget *show_hidden_files;
101
102
103static void project_open_cb(GObject *obj, GKeyFile *config, gpointer data);
104
105/* note: other callbacks connected in plugin_init */
107{
108 { "project-open", (GCallback) &project_open_cb, TRUE, NULL },
109 { NULL, NULL, FALSE, NULL }
110};
111
112
113#ifdef G_OS_WIN32
114static gboolean win32_check_hidden(const gchar *filename)
115{
116 DWORD attrs;
117 static wchar_t w_filename[MAX_PATH];
118 MultiByteToWideChar(CP_UTF8, 0, filename, -1, w_filename, sizeof(w_filename));
119 attrs = GetFileAttributesW(w_filename);
120 if (attrs != INVALID_FILE_ATTRIBUTES && attrs & FILE_ATTRIBUTE_HIDDEN)
121 return TRUE;
122 return FALSE;
123}
124#endif
125
126
127/* Returns: whether name should be hidden. */
128static gboolean check_hidden(const gchar *filename, const gchar *base_name)
129{
130 gsize len;
131
132#ifdef G_OS_WIN32
133 if (win32_check_hidden(filename))
134 return TRUE;
135#else
136 if (base_name[0] == '.')
137 return TRUE;
138#endif
139
140 len = strlen(base_name);
141 return base_name[len - 1] == '~';
142}
143
144
145static gboolean check_object(const gchar *base_name)
146{
147 gboolean ret = FALSE;
148 gchar **ptr;
149 gchar **exts = g_strsplit(hidden_file_extensions, " ", -1);
150
151 foreach_strv(ptr, exts)
152 {
153 if (g_str_has_suffix(base_name, *ptr))
154 {
155 ret = TRUE;
156 break;
157 }
158 }
159 g_strfreev(exts);
160 return ret;
161}
162
163
164/* Returns: whether filename should be removed. */
165static gboolean check_filtered(const gchar *base_name)
166{
167 gchar **filter_item;
168
169 if (filter == NULL)
170 return FALSE;
171
172 foreach_strv(filter_item, filter)
173 {
174 if (utils_str_equal(*filter_item, "*") || g_pattern_match_simple(*filter_item, base_name))
175 {
176 return FALSE;
177 }
178 }
179 return TRUE;
180}
181
182
183static GIcon *get_icon(const gchar *fname)
184{
185 GIcon *icon = NULL;
186 gchar *ctype;
187
188 ctype = g_content_type_guess(fname, NULL, 0, NULL);
189
190 if (ctype)
191 {
192 icon = g_content_type_get_icon(ctype);
193 if (icon)
194 {
195 GtkIconInfo *icon_info;
196
197 icon_info = gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(), icon, 16, 0);
198 if (!icon_info)
199 {
200 g_object_unref(icon);
201 icon = NULL;
202 }
203 else
204 gtk_icon_info_free(icon_info);
205 }
206 g_free(ctype);
207 }
208
209 if (!icon)
210 icon = g_themed_icon_new("text-x-generic");
211
212 return icon;
213}
214
215
216/* name is in locale encoding */
217static void add_item(const gchar *name)
218{
219 GtkTreeIter iter;
220 gchar *fname, *utf8_name, *utf8_fullname;
221 const gchar *sep;
222 gboolean dir;
223 GIcon *icon;
224
225 if (G_UNLIKELY(EMPTY(name)))
226 return;
227
228 /* root directory doesn't need separator */
229 sep = (utils_str_equal(current_dir, "/")) ? "" : G_DIR_SEPARATOR_S;
230 fname = g_strconcat(current_dir, sep, name, NULL);
231 dir = g_file_test(fname, G_FILE_TEST_IS_DIR);
232 utf8_fullname = utils_get_utf8_from_locale(fname);
233 utf8_name = utils_get_utf8_from_locale(name);
234 g_free(fname);
235
236 if (! show_hidden_files && check_hidden(utf8_fullname, utf8_name))
237 goto done;
238
239 if (dir)
240 {
241 if (last_dir_iter == NULL)
242 gtk_list_store_prepend(file_store, &iter);
243 else
244 {
245 gtk_list_store_insert_after(file_store, &iter, last_dir_iter);
246 gtk_tree_iter_free(last_dir_iter);
247 }
248 last_dir_iter = gtk_tree_iter_copy(&iter);
249 }
250 else
251 {
252 if (! show_hidden_files && hide_object_files && check_object(utf8_name))
253 goto done;
254 if (check_filtered(utf8_name))
255 goto done;
256
257 gtk_list_store_append(file_store, &iter);
258 }
259
260 icon = dir ? g_themed_icon_new("folder") : get_icon(utf8_name);
261 gtk_list_store_set(file_store, &iter,
263 FILEVIEW_COLUMN_NAME, utf8_name,
264 FILEVIEW_COLUMN_FILENAME, utf8_fullname,
266 -1);
267 g_object_unref(icon);
268done:
269 g_free(utf8_name);
270 g_free(utf8_fullname);
271}
272
273
274/* adds ".." to the start of the file list */
275static void add_top_level_entry(void)
276{
277 GtkTreeIter iter;
278 gchar *utf8_dir;
279 GIcon *icon;
280
281 if (EMPTY(g_path_skip_root(current_dir)))
282 return; /* ignore 'C:\' or '/' */
283
284 utf8_dir = g_path_get_dirname(current_dir);
285 SETPTR(utf8_dir, utils_get_utf8_from_locale(utf8_dir));
286
287 gtk_list_store_prepend(file_store, &iter);
288 last_dir_iter = gtk_tree_iter_copy(&iter);
289
290 icon = g_themed_icon_new("folder");
291 gtk_list_store_set(file_store, &iter,
294 FILEVIEW_COLUMN_FILENAME, utf8_dir,
296 -1);
297 g_object_unref(icon);
298 g_free(utf8_dir);
299}
300
301
302static void clear(void)
303{
304 gtk_list_store_clear(file_store);
305
306 /* reset the directory item pointer */
307 if (last_dir_iter != NULL)
308 gtk_tree_iter_free(last_dir_iter);
310}
311
312
313/* recreate the tree model from current_dir. */
314static void refresh(void)
315{
316 gchar *utf8_dir;
317 GSList *list, *node;
318
319 /* don't clear when the new path doesn't exist */
320 if (! g_file_test(current_dir, G_FILE_TEST_EXISTS))
321 return;
322
323 clear();
324
326 gtk_entry_set_text(GTK_ENTRY(path_entry), utf8_dir);
327 gtk_widget_set_tooltip_text(path_entry, utf8_dir);
328 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(path_combo), utf8_dir, 0);
329 g_free(utf8_dir);
330
331 add_top_level_entry(); /* ".." item */
332
334 if (list != NULL)
335 {
336 /* free filenames as we go through the list */
337 foreach_slist(node, list)
338 {
339 gchar *fname = node->data;
340
341 add_item(fname);
342 g_free(fname);
343 }
344 g_slist_free(list);
345 }
346 gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(file_store));
347}
348
349
350static void on_go_home(void)
351{
352 SETPTR(current_dir, g_strdup(g_get_home_dir()));
353 refresh();
354}
355
356
357/* TODO: use utils_get_default_dir_utf8() */
358static gchar *get_default_dir(void)
359{
360 const gchar *dir = NULL;
361 GeanyProject *project = geany->app->project;
362
363 if (project)
364 dir = project->base_path;
365 else
366 dir = geany->prefs->default_open_path;
367
368 if (!EMPTY(dir))
369 return utils_get_locale_from_utf8(dir);
370
371 return g_get_current_dir();
372}
373
374
375static void on_current_path(void)
376{
377 gchar *fname;
378 gchar *dir;
380
381 if (doc == NULL || doc->file_name == NULL || ! g_path_is_absolute(doc->file_name))
382 {
384 refresh();
385 return;
386 }
387 fname = doc->file_name;
388 fname = utils_get_locale_from_utf8(fname);
389 dir = g_path_get_dirname(fname);
390 g_free(fname);
391
392 SETPTR(current_dir, dir);
393 refresh();
394}
395
396
397static void on_realized(void)
398{
399 GeanyProject *project = geany->app->project;
400
401 /* if fb_set_project_base_path and project open, the path has already been set */
402 if (! fb_set_project_base_path || project == NULL || EMPTY(project->base_path))
404}
405
406
407static void on_go_up(void)
408{
409 gsize len = strlen(current_dir);
410 if (current_dir[len-1] == G_DIR_SEPARATOR)
411 current_dir[len-1] = '\0';
412 /* remove the highest directory part (which becomes the basename of current_dir) */
413 SETPTR(current_dir, g_path_get_dirname(current_dir));
414 refresh();
415}
416
417
418static gboolean check_single_selection(GtkTreeSelection *treesel)
419{
420 if (gtk_tree_selection_count_selected_rows(treesel) == 1)
421 return TRUE;
422
423 ui_set_statusbar(FALSE, _("Too many items selected!"));
424 return FALSE;
425}
426
427
428/* Returns: TRUE if at least one of selected_items is a folder. */
429static gboolean is_folder_selected(GList *selected_items)
430{
431 GList *item;
432 GtkTreeModel *model = GTK_TREE_MODEL(file_store);
433 gboolean dir_found = FALSE;
434
435 for (item = selected_items; item != NULL; item = g_list_next(item))
436 {
437 GtkTreeIter iter;
438 GtkTreePath *treepath;
439
440 treepath = (GtkTreePath*) item->data;
441 gtk_tree_model_get_iter(model, &iter, treepath);
442 gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_IS_DIR, &dir_found, -1);
443
444 if (dir_found)
445 break;
446 }
447 return dir_found;
448}
449
450
451/* Returns: the full filename in locale encoding. */
452static gchar *get_tree_path_filename(GtkTreePath *treepath)
453{
454 GtkTreeModel *model = GTK_TREE_MODEL(file_store);
455 GtkTreeIter iter;
456 gchar *name, *fname;
457
458 gtk_tree_model_get_iter(model, &iter, treepath);
459 gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_FILENAME, &name, -1);
460
462 g_free(name);
463
464 return fname;
465}
466
467
468static void open_external(const gchar *fname, gboolean dir_found)
469{
470 gchar *cmd;
471 gchar *locale_cmd;
472 gchar *dir;
473 GString *cmd_str = g_string_new(open_cmd);
474 GError *error = NULL;
475
476 if (! dir_found)
477 dir = g_path_get_dirname(fname);
478 else
479 dir = g_strdup(fname);
480
481 utils_string_replace_all(cmd_str, "%f", fname);
482 utils_string_replace_all(cmd_str, "%d", dir);
483
484 cmd = g_string_free(cmd_str, FALSE);
485 locale_cmd = utils_get_locale_from_utf8(cmd);
486 if (! spawn_async(NULL, locale_cmd, NULL, NULL, NULL, &error))
487 {
488 gchar *c = strchr(cmd, ' ');
489
490 if (c != NULL)
491 *c = '\0';
492 ui_set_statusbar(TRUE,
493 _("Could not execute configured external command '%s' (%s)."),
494 cmd, error->message);
495 g_error_free(error);
496 }
497 g_free(locale_cmd);
498 g_free(cmd);
499 g_free(dir);
500}
501
502
503static void on_external_open(GtkMenuItem *menuitem, gpointer user_data)
504{
505 GtkTreeSelection *treesel;
506 GtkTreeModel *model;
507 GList *list;
508 gboolean dir_found;
509
510 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
511
512 list = gtk_tree_selection_get_selected_rows(treesel, &model);
513 dir_found = is_folder_selected(list);
514
515 if (! dir_found || check_single_selection(treesel))
516 {
517 GList *item;
518
519 for (item = list; item != NULL; item = g_list_next(item))
520 {
521 GtkTreePath *treepath = item->data;
522 gchar *fname = get_tree_path_filename(treepath);
523
524 open_external(fname, dir_found);
525 g_free(fname);
526 }
527 }
528
529 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
530 g_list_free(list);
531}
532
533
534/* We use document_open_files() as it's more efficient. */
535static void open_selected_files(GList *list, gboolean do_not_focus)
536{
537 GSList *files = NULL;
538 GList *item;
539 GeanyDocument *doc;
540
541 for (item = list; item != NULL; item = g_list_next(item))
542 {
543 GtkTreePath *treepath = item->data;
544 gchar *fname = get_tree_path_filename(treepath);
545
546 files = g_slist_prepend(files, fname);
547 }
548 files = g_slist_reverse(files);
550 doc = document_get_current();
551 if (doc != NULL && ! do_not_focus)
553
554 g_slist_foreach(files, (GFunc) g_free, NULL); /* free filenames */
555 g_slist_free(files);
556}
557
558
559static void open_folder(GtkTreePath *treepath)
560{
561 gchar *fname = get_tree_path_filename(treepath);
562
563 SETPTR(current_dir, fname);
564 refresh();
565}
566
567
568static void on_open_clicked(GtkMenuItem *menuitem, gpointer user_data)
569{
570 GtkTreeSelection *treesel;
571 GtkTreeModel *model;
572 GList *list;
573 gboolean dir_found;
574
575 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
576
577 list = gtk_tree_selection_get_selected_rows(treesel, &model);
578 dir_found = is_folder_selected(list);
579
580 if (dir_found)
581 {
582 if (check_single_selection(treesel))
583 {
584 GtkTreePath *treepath = list->data; /* first selected item */
585
586 open_folder(treepath);
587 }
588 }
589 else
590 open_selected_files(list, GPOINTER_TO_INT(user_data));
591
592 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
593 g_list_free(list);
594}
595
596
597static void on_find_in_files(GtkMenuItem *menuitem, gpointer user_data)
598{
599 GtkTreeSelection *treesel;
600 GtkTreeModel *model;
601 GList *list;
602 gchar *dir;
603 gboolean is_dir = FALSE;
604
605 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
606 /* allow 0 or 1 selections */
607 if (gtk_tree_selection_count_selected_rows(treesel) > 0 &&
608 ! check_single_selection(treesel))
609 return;
610
611 list = gtk_tree_selection_get_selected_rows(treesel, &model);
612 is_dir = is_folder_selected(list);
613
614 if (is_dir)
615 {
616 GtkTreePath *treepath = list->data; /* first selected item */
617
618 dir = get_tree_path_filename(treepath);
619 }
620 else
621 dir = g_strdup(current_dir);
622
623 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
624 g_list_free(list);
625
628 g_free(dir);
629}
630
631
632static void on_hidden_files_clicked(GtkCheckMenuItem *item)
633{
634 show_hidden_files = gtk_check_menu_item_get_active(item);
635 refresh();
636}
637
638
639static void on_hide_sidebar(void)
640{
642}
643
644
645static void on_show_preferences(void)
646{
648}
649
650
651static GtkWidget *create_popup_menu(void)
652{
653 GtkWidget *item, *menu;
654
655 menu = gtk_menu_new();
656
657 item = ui_image_menu_item_new(GTK_STOCK_OPEN, _("Open in _Geany"));
658 gtk_widget_show(item);
659 gtk_container_add(GTK_CONTAINER(menu), item);
660 g_signal_connect(item, "activate", G_CALLBACK(on_open_clicked), NULL);
661 popup_items.open = item;
662
663 item = ui_image_menu_item_new(GTK_STOCK_OPEN, _("Open _Externally"));
664 gtk_widget_show(item);
665 gtk_container_add(GTK_CONTAINER(menu), item);
666 g_signal_connect(item, "activate", G_CALLBACK(on_external_open), NULL);
667 popup_items.open_external = item;
668
669 item = gtk_separator_menu_item_new();
670 gtk_widget_show(item);
671 gtk_container_add(GTK_CONTAINER(menu), item);
672
673 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REFRESH, NULL);
674 gtk_widget_show(item);
675 gtk_container_add(GTK_CONTAINER(menu), item);
676 g_signal_connect(item, "activate", G_CALLBACK(refresh), NULL);
677
678 item = ui_image_menu_item_new(GTK_STOCK_FIND, _("_Find in Files..."));
679 gtk_widget_show(item);
680 gtk_container_add(GTK_CONTAINER(menu), item);
681 g_signal_connect(item, "activate", G_CALLBACK(on_find_in_files), NULL);
682 popup_items.find_in_files = item;
683
684 item = gtk_separator_menu_item_new();
685 gtk_widget_show(item);
686 gtk_container_add(GTK_CONTAINER(menu), item);
687
688 item = gtk_check_menu_item_new_with_mnemonic(_("Show _Hidden Files"));
689 gtk_widget_show(item);
690 gtk_container_add(GTK_CONTAINER(menu), item);
691 g_signal_connect(item, "activate", G_CALLBACK(on_hidden_files_clicked), NULL);
692 popup_items.show_hidden_files = item;
693
694 item = gtk_separator_menu_item_new();
695 gtk_widget_show(item);
696 gtk_container_add(GTK_CONTAINER(menu), item);
697
698 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
699 gtk_widget_show(item);
700 gtk_container_add(GTK_CONTAINER(menu), item);
701 g_signal_connect(item, "activate", G_CALLBACK(on_show_preferences), NULL);
702
703 item = gtk_separator_menu_item_new();
704 gtk_widget_show(item);
705 gtk_container_add(GTK_CONTAINER(menu), item);
706
707 item = ui_image_menu_item_new(GTK_STOCK_CLOSE, _("H_ide Sidebar"));
708 gtk_widget_show(item);
709 gtk_container_add(GTK_CONTAINER(menu), item);
710 g_signal_connect(item, "activate", G_CALLBACK(on_hide_sidebar), NULL);
711
712 return menu;
713}
714
715
716static void on_tree_selection_changed(GtkTreeSelection *selection, gpointer data)
717{
718 gboolean have_sel = (gtk_tree_selection_count_selected_rows(selection) > 0);
719 gboolean multi_sel = (gtk_tree_selection_count_selected_rows(selection) > 1);
720
721 if (popup_items.open != NULL)
722 gtk_widget_set_sensitive(popup_items.open, have_sel);
723 if (popup_items.open_external != NULL)
724 gtk_widget_set_sensitive(popup_items.open_external, have_sel);
725 if (popup_items.find_in_files != NULL)
726 gtk_widget_set_sensitive(popup_items.find_in_files, have_sel && ! multi_sel);
727}
728
729
730static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
731{
732 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
733 {
735 return TRUE;
736 }
737 else if (event->button == 3)
738 {
739 static GtkWidget *popup_menu = NULL;
740
741 if (popup_menu == NULL)
742 popup_menu = create_popup_menu();
743
744 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(popup_items.show_hidden_files),
746 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, NULL, NULL, event->button, event->time);
747 /* don't return TRUE here, unless the selection won't be changed */
748 }
749 return FALSE;
750}
751
752
753static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
754{
755 if (ui_is_keyval_enter_or_return(event->keyval))
756 {
758 return TRUE;
759 }
760
761 if (event->keyval == GDK_KEY_space)
762 {
763 on_open_clicked(NULL, GINT_TO_POINTER(TRUE));
764 return TRUE;
765 }
766
767 if (( (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up) && (event->state & GDK_MOD1_MASK)) || /* FIXME: Alt-Up doesn't seem to work! */
768 (event->keyval == GDK_KEY_BackSpace) )
769 {
770 on_go_up();
771 return TRUE;
772 }
773
774 if ((event->keyval == GDK_KEY_F10 && event->state & GDK_SHIFT_MASK) || event->keyval == GDK_KEY_Menu)
775 {
776 GdkEventButton button_event;
777
778 button_event.time = event->time;
779 button_event.button = 3;
780
781 on_button_press(widget, &button_event, data);
782 return TRUE;
783 }
784
785 return FALSE;
786}
787
788
789static void clear_filter(void)
790{
791 if (filter != NULL)
792 {
793 g_strfreev(filter);
794 filter = NULL;
795 }
796}
797
798
799static void on_clear_filter(GtkEntry *entry, gpointer user_data)
800{
801 clear_filter();
802
803 gtk_entry_set_text(GTK_ENTRY(filter_entry), "");
804
805 refresh();
806}
807
808
809static void on_path_entry_activate(GtkEntry *entry, gpointer user_data)
810{
811 gchar *new_dir = (gchar*) gtk_entry_get_text(entry);
812
813 if (!EMPTY(new_dir))
814 {
815 if (g_str_has_suffix(new_dir, ".."))
816 {
817 on_go_up();
818 return;
819 }
820 else if (new_dir[0] == '~')
821 {
822 GString *str = g_string_new(new_dir);
823 utils_string_replace_first(str, "~", g_get_home_dir());
824 new_dir = g_string_free(str, FALSE);
825 }
826 else
827 new_dir = utils_get_locale_from_utf8(new_dir);
828 }
829 else
830 new_dir = g_strdup(g_get_home_dir());
831
832 SETPTR(current_dir, new_dir);
833
835}
836
837
838static void ui_combo_box_changed(GtkComboBox *combo, gpointer user_data)
839{
840 /* we get this callback on typing as well as choosing an item */
841 if (gtk_combo_box_get_active(combo) >= 0)
842 gtk_widget_activate(gtk_bin_get_child(GTK_BIN(combo)));
843}
844
845
846static void on_filter_activate(GtkEntry *entry, gpointer user_data)
847{
848 /* We use spaces for consistency with Find in Files file patterns
849 * ';' also supported like original patch. */
850 filter = g_strsplit_set(gtk_entry_get_text(entry), "; ", -1);
851 if (filter == NULL || g_strv_length(filter) == 0)
852 {
853 clear_filter();
854 }
855 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(filter_combo), NULL, 0);
856 refresh();
857}
858
859
860static void on_filter_clear(GtkEntry *entry, gint icon_pos,
861 GdkEvent *event, gpointer data)
862{
863 clear_filter();
864 refresh();
865}
866
867
868static void prepare_file_view(void)
869{
870 GtkCellRenderer *text_renderer, *icon_renderer;
871 GtkTreeViewColumn *column;
872 GtkTreeSelection *selection;
873
874 file_store = gtk_list_store_new(FILEVIEW_N_COLUMNS, G_TYPE_ICON, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
875
876 gtk_tree_view_set_model(GTK_TREE_VIEW(file_view), GTK_TREE_MODEL(file_store));
877 g_object_unref(file_store);
878
879 icon_renderer = gtk_cell_renderer_pixbuf_new();
880 text_renderer = gtk_cell_renderer_text_new();
881 column = gtk_tree_view_column_new();
882 gtk_tree_view_column_pack_start(column, icon_renderer, FALSE);
883 gtk_tree_view_column_set_attributes(column, icon_renderer, "gicon", FILEVIEW_COLUMN_ICON, NULL);
884 gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
885 gtk_tree_view_column_set_attributes(column, text_renderer, "text", FILEVIEW_COLUMN_NAME, NULL);
886 gtk_tree_view_append_column(GTK_TREE_VIEW(file_view), column);
887 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(file_view), FALSE);
888
889 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(file_view), TRUE);
890 gtk_tree_view_set_search_column(GTK_TREE_VIEW(file_view), FILEVIEW_COLUMN_NAME);
891
892 ui_widget_modify_font_from_string(file_view, geany->interface_prefs->tagbar_font);
893
894 /* tooltips */
896
897 /* selection handling */
898 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(file_view));
899 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
900
901 /* Show the current path when the FB is first needed */
902 g_signal_connect(file_view, "realize", G_CALLBACK(on_realized), NULL);
903 g_signal_connect(selection, "changed", G_CALLBACK(on_tree_selection_changed), NULL);
904 g_signal_connect(file_view, "button-press-event", G_CALLBACK(on_button_press), NULL);
905 g_signal_connect(file_view, "key-press-event", G_CALLBACK(on_key_press), NULL);
906}
907
908
909static GtkWidget *make_toolbar(void)
910{
911 GtkWidget *wid, *toolbar;
912
913 toolbar = gtk_toolbar_new();
914 gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_MENU);
915 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
916
917 wid = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_GO_UP));
918 gtk_widget_set_tooltip_text(wid, _("Up"));
919 g_signal_connect(wid, "clicked", G_CALLBACK(on_go_up), NULL);
920 gtk_container_add(GTK_CONTAINER(toolbar), wid);
921
922 wid = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH));
923 gtk_widget_set_tooltip_text(wid, _("Refresh"));
924 g_signal_connect(wid, "clicked", G_CALLBACK(refresh), NULL);
925 gtk_container_add(GTK_CONTAINER(toolbar), wid);
926
927 wid = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_HOME));
928 gtk_widget_set_tooltip_text(wid, _("Home"));
929 g_signal_connect(wid, "clicked", G_CALLBACK(on_go_home), NULL);
930 gtk_container_add(GTK_CONTAINER(toolbar), wid);
931
932 wid = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_JUMP_TO));
933 gtk_widget_set_tooltip_text(wid, _("Set path from document"));
934 g_signal_connect(wid, "clicked", G_CALLBACK(on_current_path), NULL);
935 gtk_container_add(GTK_CONTAINER(toolbar), wid);
936
937 return toolbar;
938}
939
940
941static GtkWidget *make_filterbar(void)
942{
943 GtkWidget *label, *filterbar;
944
945 filterbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
946
947 label = gtk_label_new(_("Filter:"));
948
949 filter_combo = gtk_combo_box_text_new_with_entry();
950 filter_entry = gtk_bin_get_child(GTK_BIN(filter_combo));
951
953 g_signal_connect(filter_entry, "icon-release", G_CALLBACK(on_filter_clear), NULL);
954
955 gtk_widget_set_tooltip_text(filter_entry,
956 _("Filter your files with the usual wildcards. Separate multiple patterns with a space."));
957 g_signal_connect(filter_entry, "activate", G_CALLBACK(on_filter_activate), NULL);
958 g_signal_connect(filter_combo, "changed", G_CALLBACK(ui_combo_box_changed), NULL);
959
960 gtk_box_pack_start(GTK_BOX(filterbar), label, FALSE, FALSE, 0);
961 gtk_box_pack_start(GTK_BOX(filterbar), filter_combo, TRUE, TRUE, 0);
962
963 return filterbar;
964}
965
966
967static gboolean completion_match_func(GtkEntryCompletion *completion, const gchar *key,
968 GtkTreeIter *iter, gpointer user_data)
969{
970 gchar *str;
971 gboolean is_dir;
972 gboolean result = FALSE;
973
974 gtk_tree_model_get(GTK_TREE_MODEL(file_store), iter,
975 FILEVIEW_COLUMN_IS_DIR, &is_dir, FILEVIEW_COLUMN_NAME, &str, -1);
976
977 if (str != NULL && is_dir && !g_str_has_suffix(key, G_DIR_SEPARATOR_S))
978 {
979 /* key is something like "/tmp/te" and str is a filename like "test",
980 * so strip the path from key to make them comparable */
981 gchar *base_name = g_path_get_basename(key);
982 gchar *str_lowered = g_utf8_strdown(str, -1);
983 result = g_str_has_prefix(str_lowered, base_name);
984 g_free(base_name);
985 g_free(str_lowered);
986 }
987 g_free(str);
988
989 return result;
990}
991
992
993static gboolean completion_match_selected(GtkEntryCompletion *widget, GtkTreeModel *model,
994 GtkTreeIter *iter, gpointer user_data)
995{
996 gchar *str;
997 gtk_tree_model_get(model, iter, FILEVIEW_COLUMN_NAME, &str, -1);
998 if (str != NULL)
999 {
1000 gchar *text = g_strconcat(current_dir, G_DIR_SEPARATOR_S, str, NULL);
1001 gtk_entry_set_text(GTK_ENTRY(path_entry), text);
1002 gtk_editable_set_position(GTK_EDITABLE(path_entry), -1);
1003 /* force change of directory when completion is done */
1005 g_free(text);
1006 }
1007 g_free(str);
1008
1009 return TRUE;
1010}
1011
1012
1013static void completion_create(void)
1014{
1015 entry_completion = gtk_entry_completion_new();
1016
1017 gtk_entry_completion_set_inline_completion(entry_completion, FALSE);
1018 gtk_entry_completion_set_popup_completion(entry_completion, TRUE);
1019 gtk_entry_completion_set_text_column(entry_completion, FILEVIEW_COLUMN_NAME);
1020 gtk_entry_completion_set_match_func(entry_completion, completion_match_func, NULL, NULL);
1021
1022 g_signal_connect(entry_completion, "match-selected",
1023 G_CALLBACK(completion_match_selected), NULL);
1024
1025 gtk_entry_set_completion(GTK_ENTRY(path_entry), entry_completion);
1026}
1027
1028
1029static void load_settings(void)
1030{
1031 GKeyFile *config = g_key_file_new();
1032
1033 config_file = g_strconcat(geany->app->configdir, G_DIR_SEPARATOR_S, "plugins", G_DIR_SEPARATOR_S,
1034 "filebrowser", G_DIR_SEPARATOR_S, "filebrowser.conf", NULL);
1035 g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
1036
1037 open_cmd = utils_get_setting_string(config, "filebrowser", "open_command", OPEN_CMD);
1038 /* g_key_file_get_boolean defaults to FALSE */
1039 show_hidden_files = g_key_file_get_boolean(config, "filebrowser", "show_hidden_files", NULL);
1040 hide_object_files = utils_get_setting_boolean(config, "filebrowser", "hide_object_files", TRUE);
1041 hidden_file_extensions = utils_get_setting_string(config, "filebrowser", "hidden_file_extensions",
1042 ".o .obj .so .dll .a .lib .pyc");
1043 fb_follow_path = g_key_file_get_boolean(config, "filebrowser", "fb_follow_path", NULL);
1044 fb_set_project_base_path = g_key_file_get_boolean(config, "filebrowser", "fb_set_project_base_path", NULL);
1045
1046 g_key_file_free(config);
1047}
1048
1049
1050static void project_open_cb(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED GKeyFile *config,
1051 G_GNUC_UNUSED gpointer data)
1052{
1053 gchar *new_dir;
1054 GeanyProject *project = geany->app->project;
1055
1056 if (! fb_set_project_base_path || project == NULL || EMPTY(project->base_path))
1057 return;
1058
1059 /* TODO this is a copy of project_get_base_path(), add it to the plugin API */
1060 if (g_path_is_absolute(project->base_path))
1061 new_dir = g_strdup(project->base_path);
1062 else
1063 { /* build base_path out of project file name's dir and base_path */
1064 gchar *dir = g_path_get_dirname(project->file_name);
1065
1066 new_dir = g_strconcat(dir, G_DIR_SEPARATOR_S, project->base_path, NULL);
1067 g_free(dir);
1068 }
1069 /* get it into locale encoding */
1070 SETPTR(new_dir, utils_get_locale_from_utf8(new_dir));
1071
1072 if (! utils_str_equal(current_dir, new_dir))
1073 {
1074 SETPTR(current_dir, new_dir);
1075 refresh();
1076 }
1077 else
1078 g_free(new_dir);
1079}
1080
1081
1082static gpointer last_activate_path = NULL;
1083
1084static void document_activate_cb(G_GNUC_UNUSED GObject *obj, GeanyDocument *doc,
1085 G_GNUC_UNUSED gpointer data)
1086{
1087 gchar *new_dir;
1088
1090
1091 if (! fb_follow_path || doc->file_name == NULL || ! g_path_is_absolute(doc->file_name))
1092 return;
1093
1094 new_dir = g_path_get_dirname(doc->file_name);
1095 SETPTR(new_dir, utils_get_locale_from_utf8(new_dir));
1096
1097 if (! utils_str_equal(current_dir, new_dir))
1098 {
1099 SETPTR(current_dir, new_dir);
1100 refresh();
1101 }
1102 else
1103 g_free(new_dir);
1104}
1105
1106
1107static void document_save_cb(GObject *obj, GeanyDocument *doc, gpointer user_data)
1108{
1109 if (!last_activate_path)
1110 document_activate_cb(obj, doc, user_data);
1111}
1112
1113
1114static void kb_activate(guint key_id)
1115{
1116 gtk_notebook_set_current_page(GTK_NOTEBOOK(geany->main_widgets->sidebar_notebook), page_number);
1117 switch (key_id)
1118 {
1119 case KB_FOCUS_FILE_LIST:
1120 gtk_widget_grab_focus(file_view);
1121 break;
1123 gtk_widget_grab_focus(path_entry);
1124 break;
1125 }
1126}
1127
1128
1130{
1131 GeanyKeyGroup *key_group;
1132 GtkWidget *scrollwin, *toolbar, *filterbar;
1133
1134 filter = NULL;
1135
1136 file_view_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1137 toolbar = make_toolbar();
1138 gtk_box_pack_start(GTK_BOX(file_view_vbox), toolbar, FALSE, FALSE, 0);
1139
1140 filterbar = make_filterbar();
1141 gtk_box_pack_start(GTK_BOX(file_view_vbox), filterbar, FALSE, FALSE, 0);
1142
1143 path_combo = gtk_combo_box_text_new_with_entry();
1144 gtk_box_pack_start(GTK_BOX(file_view_vbox), path_combo, FALSE, FALSE, 2);
1145 g_signal_connect(path_combo, "changed", G_CALLBACK(ui_combo_box_changed), NULL);
1146 path_entry = gtk_bin_get_child(GTK_BIN(path_combo));
1147 g_signal_connect(path_entry, "activate", G_CALLBACK(on_path_entry_activate), NULL);
1148
1149 file_view = gtk_tree_view_new();
1152
1153 popup_items.open = popup_items.open_external = popup_items.find_in_files = NULL;
1154
1155 scrollwin = gtk_scrolled_window_new(NULL, NULL);
1156 gtk_scrolled_window_set_policy(
1157 GTK_SCROLLED_WINDOW(scrollwin),
1158 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1159 gtk_container_add(GTK_CONTAINER(scrollwin), file_view);
1160 gtk_box_pack_start(GTK_BOX(file_view_vbox), scrollwin, TRUE, TRUE, 0);
1161
1162 /* load settings before file_view "realize" callback */
1163 load_settings();
1164
1166 page_number = gtk_notebook_append_page(GTK_NOTEBOOK(geany->main_widgets->sidebar_notebook),
1167 file_view_vbox, gtk_label_new(_("Files")));
1168
1169 /* setup keybindings */
1170 key_group = plugin_set_key_group(geany_plugin, "file_browser", KB_COUNT, NULL);
1172 0, 0, "focus_file_list", _("Focus File List"), NULL);
1174 0, 0, "focus_path_entry", _("Focus Path Entry"), NULL);
1175
1176 plugin_signal_connect(geany_plugin, NULL, "document-activate", TRUE,
1177 (GCallback) &document_activate_cb, NULL);
1178 plugin_signal_connect(geany_plugin, NULL, "document-save", TRUE,
1179 (GCallback) &document_save_cb, NULL);
1180}
1181
1182
1183static void save_settings(void)
1184{
1185 GKeyFile *config = g_key_file_new();
1186 gchar *data;
1187 gchar *config_dir = g_path_get_dirname(config_file);
1188
1189 g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
1190
1191 g_key_file_set_string(config, "filebrowser", "open_command", open_cmd);
1192 g_key_file_set_boolean(config, "filebrowser", "show_hidden_files", show_hidden_files);
1193 g_key_file_set_boolean(config, "filebrowser", "hide_object_files", hide_object_files);
1194 g_key_file_set_string(config, "filebrowser", "hidden_file_extensions", hidden_file_extensions);
1195 g_key_file_set_boolean(config, "filebrowser", "fb_follow_path", fb_follow_path);
1196 g_key_file_set_boolean(config, "filebrowser", "fb_set_project_base_path",
1198
1199 if (! g_file_test(config_dir, G_FILE_TEST_IS_DIR) && utils_mkdir(config_dir, TRUE) != 0)
1200 {
1201 dialogs_show_msgbox(GTK_MESSAGE_ERROR,
1202 _("Plugin configuration directory could not be created."));
1203 }
1204 else
1205 {
1206 /* write config to file */
1207 data = g_key_file_to_data(config, NULL, NULL);
1209 g_free(data);
1210 }
1211 g_free(config_dir);
1212 g_key_file_free(config);
1213}
1214
1215
1216static struct
1217{
1218 GtkWidget *open_cmd_entry;
1224}
1226
1227static void
1228on_configure_response(GtkDialog *dialog, gint response, gpointer user_data)
1229{
1230 if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY)
1231 {
1232 g_free(open_cmd);
1233 open_cmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(pref_widgets.open_cmd_entry)));
1234 show_hidden_files = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.show_hidden_checkbox));
1235 hide_object_files = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.hide_objects_checkbox));
1236 g_free(hidden_file_extensions);
1237 hidden_file_extensions = g_strdup(gtk_entry_get_text(GTK_ENTRY(pref_widgets.hidden_files_entry)));
1238 fb_follow_path = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.follow_path_checkbox));
1239 fb_set_project_base_path = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1240 pref_widgets.set_project_base_path_checkbox));
1241
1242 /* apply the changes */
1243 refresh();
1244 }
1245}
1246
1247
1248static void on_toggle_hidden(void)
1249{
1250 gboolean enabled = !gtk_toggle_button_get_active(
1251 GTK_TOGGLE_BUTTON(pref_widgets.show_hidden_checkbox));
1252
1253 gtk_widget_set_sensitive(pref_widgets.hide_objects_checkbox, enabled);
1254 enabled &= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.hide_objects_checkbox));
1255 gtk_widget_set_sensitive(pref_widgets.hidden_files_entry, enabled);
1256}
1257
1258
1259GtkWidget *plugin_configure(GtkDialog *dialog)
1260{
1261 GtkWidget *label, *entry, *checkbox_of, *checkbox_hf, *checkbox_fp, *checkbox_pb, *vbox;
1262 GtkWidget *box, *align;
1263
1264 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1265 box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
1266
1267 label = gtk_label_new(_("External open command:"));
1268 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1269 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
1270
1271 entry = gtk_entry_new();
1272 if (open_cmd != NULL)
1273 gtk_entry_set_text(GTK_ENTRY(entry), open_cmd);
1274 gtk_widget_set_tooltip_text(entry,
1275 _("The command to execute when using \"Open with\". You can use %f and %d wildcards.\n"
1276 "%f will be replaced with the filename including full path\n"
1277 "%d will be replaced with the path name of the selected file without the filename"));
1278 gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 0);
1279 pref_widgets.open_cmd_entry = entry;
1280
1281 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 3);
1282
1283 checkbox_hf = gtk_check_button_new_with_label(_("Show hidden files"));
1284 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox_hf), FALSE);
1285 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox_hf), show_hidden_files);
1286 gtk_box_pack_start(GTK_BOX(vbox), checkbox_hf, FALSE, FALSE, 0);
1287 pref_widgets.show_hidden_checkbox = checkbox_hf;
1288 g_signal_connect(checkbox_hf, "toggled", on_toggle_hidden, NULL);
1289
1290 box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
1291 checkbox_of = gtk_check_button_new_with_label(_("Hide file extensions:"));
1292 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox_of), FALSE);
1293 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox_of), hide_object_files);
1294 gtk_box_pack_start(GTK_BOX(box), checkbox_of, FALSE, FALSE, 0);
1295 pref_widgets.hide_objects_checkbox = checkbox_of;
1296 g_signal_connect(checkbox_of, "toggled", on_toggle_hidden, NULL);
1297
1298 entry = gtk_entry_new();
1300 gtk_entry_set_text(GTK_ENTRY(entry), hidden_file_extensions);
1301 gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 0);
1302 pref_widgets.hidden_files_entry = entry;
1303
1304 align = gtk_alignment_new(1, 0.5, 1, 1);
1305 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
1306 gtk_container_add(GTK_CONTAINER(align), box);
1307 gtk_box_pack_start(GTK_BOX(vbox), align, FALSE, FALSE, 0);
1309
1310 checkbox_fp = gtk_check_button_new_with_label(_("Follow the path of the current file"));
1311 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox_fp), FALSE);
1312 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox_fp), fb_follow_path);
1313 gtk_box_pack_start(GTK_BOX(vbox), checkbox_fp, FALSE, FALSE, 0);
1314 pref_widgets.follow_path_checkbox = checkbox_fp;
1315
1316 checkbox_pb = gtk_check_button_new_with_label(_("Use the project's base directory"));
1317 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox_pb), FALSE);
1318 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox_pb), fb_set_project_base_path);
1319 gtk_widget_set_tooltip_text(checkbox_pb,
1320 _("Change the directory to the base directory of the currently opened project"));
1321 gtk_box_pack_start(GTK_BOX(vbox), checkbox_pb, FALSE, FALSE, 0);
1322 pref_widgets.set_project_base_path_checkbox = checkbox_pb;
1323
1324 gtk_widget_show_all(vbox);
1325
1326 g_signal_connect(dialog, "response", G_CALLBACK(on_configure_response), NULL);
1327 return vbox;
1328}
1329
1330
1332{
1333 save_settings();
1334
1335 g_free(config_file);
1336 g_free(open_cmd);
1337 g_free(hidden_file_extensions);
1338 clear_filter();
1339 gtk_widget_destroy(file_view_vbox);
1340 g_object_unref(G_OBJECT(entry_completion));
1341}
GeanyBuildCommand ** ptr
Definition: build.c:2679
const gchar * label
Definition: build.c:2676
void dialogs_show_msgbox(GtkMessageType type, const gchar *text,...)
Shows a message box of the type type with text.
Definition: dialogs.c:729
GeanyDocument * document_get_current(void)
Finds the current document.
Definition: document.c:371
const gchar * name
Definition: document.c:3219
void document_open_files(const GSList *filenames, gboolean readonly, GeanyFiletype *ft, const gchar *forced_enc)
Opens each file in the list filenames.
Definition: document.c:1555
gchar * text
Definition: editor.c:83
void error(const errorSelection selection, const char *const format,...)
Definition: error.c:53
GtkWidget * open_cmd_entry
Definition: filebrowser.c:1218
static void on_toggle_hidden(void)
Definition: filebrowser.c:1248
static GIcon * get_icon(const gchar *fname)
Definition: filebrowser.c:183
static void on_tree_selection_changed(GtkTreeSelection *selection, gpointer data)
Definition: filebrowser.c:716
@ FILEVIEW_COLUMN_FILENAME
Definition: filebrowser.c:66
@ FILEVIEW_COLUMN_NAME
Definition: filebrowser.c:65
@ FILEVIEW_N_COLUMNS
Definition: filebrowser.c:68
@ FILEVIEW_COLUMN_IS_DIR
Definition: filebrowser.c:67
@ FILEVIEW_COLUMN_ICON
Definition: filebrowser.c:64
static void on_current_path(void)
Definition: filebrowser.c:375
static void on_find_in_files(GtkMenuItem *menuitem, gpointer user_data)
Definition: filebrowser.c:597
@ KB_FOCUS_FILE_LIST
Definition: filebrowser.c:56
@ KB_FOCUS_PATH_ENTRY
Definition: filebrowser.c:57
@ KB_COUNT
Definition: filebrowser.c:58
static gboolean check_filtered(const gchar *base_name)
Definition: filebrowser.c:165
static GtkWidget * file_view_vbox
Definition: filebrowser.c:76
static void save_settings(void)
Definition: filebrowser.c:1183
GtkWidget * open_external
Definition: filebrowser.c:97
static GtkWidget * file_view
Definition: filebrowser.c:77
static gboolean is_folder_selected(GList *selected_items)
Definition: filebrowser.c:429
static void on_filter_clear(GtkEntry *entry, gint icon_pos, GdkEvent *event, gpointer data)
Definition: filebrowser.c:860
static gchar * get_default_dir(void)
Definition: filebrowser.c:358
static gchar * hidden_file_extensions
Definition: filebrowser.c:90
GtkWidget * find_in_files
Definition: filebrowser.c:98
static void ui_combo_box_changed(GtkComboBox *combo, gpointer user_data)
Definition: filebrowser.c:838
static gboolean check_single_selection(GtkTreeSelection *treesel)
Definition: filebrowser.c:418
static void open_folder(GtkTreePath *treepath)
Definition: filebrowser.c:559
static void on_filter_activate(GtkEntry *entry, gpointer user_data)
Definition: filebrowser.c:846
#define OPEN_CMD
Definition: filebrowser.c:40
static void on_go_home(void)
Definition: filebrowser.c:350
static void on_configure_response(GtkDialog *dialog, gint response, gpointer user_data)
Definition: filebrowser.c:1228
static GtkWidget * make_filterbar(void)
Definition: filebrowser.c:941
static void add_item(const gchar *name)
Definition: filebrowser.c:217
static gboolean show_hidden_files
Definition: filebrowser.c:73
GtkWidget * set_project_base_path_checkbox
Definition: filebrowser.c:1223
static gint page_number
Definition: filebrowser.c:92
static gpointer last_activate_path
Definition: filebrowser.c:1082
GeanyPlugin * geany_plugin
Definition: filebrowser.c:43
GtkWidget * open
Definition: filebrowser.c:96
static void document_activate_cb(G_GNUC_UNUSED GObject *obj, GeanyDocument *doc, G_GNUC_UNUSED gpointer data)
Definition: filebrowser.c:1084
static GtkWidget * create_popup_menu(void)
Definition: filebrowser.c:651
static gchar * current_dir
Definition: filebrowser.c:86
static gboolean check_hidden(const gchar *filename, const gchar *base_name)
Definition: filebrowser.c:128
static gchar * config_file
Definition: filebrowser.c:88
static void on_realized(void)
Definition: filebrowser.c:397
void plugin_cleanup(void)
Called before unloading the plugin.
Definition: filebrowser.c:1331
PluginCallback plugin_callbacks[]
Definition: filebrowser.c:106
static GtkWidget * path_combo
Definition: filebrowser.c:84
static gboolean completion_match_selected(GtkEntryCompletion *widget, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
Definition: filebrowser.c:993
static void on_hidden_files_clicked(GtkCheckMenuItem *item)
Definition: filebrowser.c:632
static GtkEntryCompletion * entry_completion
Definition: filebrowser.c:80
static void on_hide_sidebar(void)
Definition: filebrowser.c:639
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition: filebrowser.c:753
GtkWidget * hidden_files_entry
Definition: filebrowser.c:1221
static void on_go_up(void)
Definition: filebrowser.c:407
static GtkWidget * path_entry
Definition: filebrowser.c:85
static gchar * open_cmd
Definition: filebrowser.c:87
static gboolean check_object(const gchar *base_name)
Definition: filebrowser.c:145
static void prepare_file_view(void)
Definition: filebrowser.c:868
void plugin_init(GeanyData *data)
Called after loading the plugin.
Definition: filebrowser.c:1129
static void load_settings(void)
Definition: filebrowser.c:1029
static void on_open_clicked(GtkMenuItem *menuitem, gpointer user_data)
Definition: filebrowser.c:568
static void completion_create(void)
Definition: filebrowser.c:1013
GeanyData * geany_data
Definition: filebrowser.c:44
static gboolean fb_set_project_base_path
Definition: filebrowser.c:71
static void clear(void)
Definition: filebrowser.c:302
static GtkWidget * filter_entry
Definition: filebrowser.c:83
static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition: filebrowser.c:730
GtkWidget * plugin_configure(GtkDialog *dialog)
Called before showing the plugin preferences dialog for multiple plugins.
Definition: filebrowser.c:1259
static GtkTreeIter * last_dir_iter
Definition: filebrowser.c:79
static void on_external_open(GtkMenuItem *menuitem, gpointer user_data)
Definition: filebrowser.c:503
static void on_show_preferences(void)
Definition: filebrowser.c:645
static void kb_activate(guint key_id)
Definition: filebrowser.c:1114
static GtkWidget * filter_combo
Definition: filebrowser.c:82
GtkWidget * hide_objects_checkbox
Definition: filebrowser.c:1220
static gboolean fb_follow_path
Definition: filebrowser.c:72
GtkWidget * follow_path_checkbox
Definition: filebrowser.c:1222
static void project_open_cb(GObject *obj, GKeyFile *config, gpointer data)
static gchar ** filter
Definition: filebrowser.c:89
static struct @32 popup_items
static gchar * get_tree_path_filename(GtkTreePath *treepath)
Definition: filebrowser.c:452
static void clear_filter(void)
Definition: filebrowser.c:789
GtkWidget * show_hidden_checkbox
Definition: filebrowser.c:1219
static struct @33 pref_widgets
static void add_top_level_entry(void)
Definition: filebrowser.c:275
static gboolean completion_match_func(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data)
Definition: filebrowser.c:967
static gboolean hide_object_files
Definition: filebrowser.c:74
static GtkListStore * file_store
Definition: filebrowser.c:78
static void open_selected_files(GList *list, gboolean do_not_focus)
Definition: filebrowser.c:535
static GtkWidget * make_toolbar(void)
Definition: filebrowser.c:909
static void on_clear_filter(GtkEntry *entry, gpointer user_data)
Definition: filebrowser.c:799
static void refresh(void)
Definition: filebrowser.c:314
static void on_path_entry_activate(GtkEntry *entry, gpointer user_data)
Definition: filebrowser.c:809
static void document_save_cb(GObject *obj, GeanyDocument *doc, gpointer user_data)
Definition: filebrowser.c:1107
tokenInfo * list
Single include for plugins.
void keybindings_send_command(guint group_id, guint key_id)
Mimics a (built-in only) keybinding action.
Definition: keybindings.c:1426
GeanyKeyBinding * keybindings_set_item(GeanyKeyGroup *group, gsize key_id, GeanyKeyCallback callback, guint key, GdkModifierType mod, const gchar *kf_name, const gchar *label, GtkWidget *menu_item)
<simplesect kind="geany:skip"></simplesect> Fills a GeanyKeyBinding struct item.
Definition: keybindings.c:171
@ GEANY_KEY_GROUP_VIEW
Group.
Definition: keybindings.h:112
@ GEANY_KEY_GROUP_FOCUS
Group.
Definition: keybindings.h:113
@ GEANY_KEYS_VIEW_SIDEBAR
Keybinding.
Definition: keybindings.h:236
@ GEANY_KEYS_FOCUS_EDITOR
Keybinding.
Definition: keybindings.h:194
#define GEANY_API_VERSION
The Application Programming Interface (API) version, incremented whenever any plugin data types are m...
Definition: plugindata.h:61
#define PLUGIN_SET_INFO(p_name, p_description, p_version, p_author)
Sets the plugin name and some other basic information about a plugin.
Definition: plugindata.h:117
#define PLUGIN_VERSION_CHECK(api_required)
Defines a function to check the plugin is safe to load.
Definition: plugindata.h:83
#define geany
Simple macro for geany_data that reduces typing.
Definition: plugindata.h:225
void plugin_show_configure(GeanyPlugin *plugin)
Shows the plugin's configure dialog.
Definition: pluginutils.c:474
GeanyKeyGroup * plugin_set_key_group(GeanyPlugin *plugin, const gchar *section_name, gsize count, GeanyKeyGroupCallback callback)
<simplesect kind="geany:skip"></simplesect> Sets up or resizes a keybinding group for the plugin.
Definition: pluginutils.c:331
void plugin_signal_connect(GeanyPlugin *plugin, GObject *object, const gchar *signal_name, gboolean after, GCallback callback, gpointer user_data)
<simplesect kind="geany:skip"></simplesect> Connects a signal which will be disconnected on unloading...
Definition: pluginutils.c:158
#define NULL
Definition: rbtree.h:150
GtkWidget * entry
Definition: search.c:118
void search_show_find_in_files_dialog(const gchar *dir)
Shows the Find in Files dialog.
Definition: search.c:1041
gboolean spawn_async(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, GPid *child_pid, GError **error)
Executes a child asynchronously.
Definition: spawn.c:814
const gchar filename[]
Definition: stash-example.c:4
GtkWidget * dialog
gtk_container_add(GTK_CONTAINER(dialog->vbox), check_button)
gtk_widget_show_all(dialog)
long files
Definition: stats.c:32
This contains pointers to global variables owned by Geany for plugins to use.
Definition: plugindata.h:167
Structure for representing an open tab with all its properties.
Definition: document.h:81
gchar * file_name
The UTF-8 encoded file name.
Definition: document.h:92
gchar * real_path
The link-dereferenced, locale-encoded file name.
Definition: document.h:115
Basic information for the plugin and identification.
Definition: plugindata.h:233
Structure for representing a project.
Definition: project.h:35
gchar * file_name
Where the project file is stored (in UTF-8).
Definition: project.h:38
gchar * base_path
Base path of the project directory (in UTF-8, maybe relative).
Definition: project.h:39
Callback array entry type used with the plugin_callbacks symbol.
Definition: plugindata.h:148
#define _(String)
Definition: support.h:42
gboolean ui_is_keyval_enter_or_return(guint keyval)
Checks whether the passed keyval is the Enter or Return key.
Definition: ui_utils.c:3009
void ui_entry_add_clear_icon(GtkEntry *entry)
Adds a small clear icon to the right end of the passed entry.
Definition: ui_utils.c:1604
void ui_set_statusbar(gboolean log, const gchar *format,...)
Displays text on the statusbar.
Definition: ui_utils.c:168
void ui_combo_box_add_to_history(GtkComboBoxText *combo_entry, const gchar *text, gint history_len)
Prepends text to the drop down list, removing a duplicate element in the list if found.
Definition: ui_utils.c:1697
GtkWidget * ui_image_menu_item_new(const gchar *stock_id, const gchar *label)
Creates a GtkImageMenuItem with a stock image and a custom label.
Definition: ui_utils.c:1574
void ui_tree_view_set_tooltip_text_column(GtkTreeView *tree_view, gint column)
Adds text tooltips to a tree view.
Definition: ui_utils.c:1876
void ui_widget_modify_font_from_string(GtkWidget *widget, const gchar *str)
Modifies the font of a widget using gtk_widget_modify_font().
Definition: ui_utils.c:1894
gint utils_write_file(const gchar *filename, const gchar *text)
Writes text into a file named filename.
Definition: utils.c:209
guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
Definition: utils.c:1529
gboolean utils_get_setting_boolean(GKeyFile *config, const gchar *section, const gchar *key, const gboolean default_value)
Wraps g_key_file_get_boolean() to add a default value argument.
Definition: utils.c:836
gchar * utils_get_utf8_from_locale(const gchar *locale_text)
Converts the given string (in locale encoding) into UTF-8 encoding.
Definition: utils.c:1272
GSList * utils_get_file_list(const gchar *path, guint *length, GError **error)
Gets a sorted list of files from the specified directory.
Definition: utils.c:1441
gint utils_mkdir(const gchar *path, gboolean create_parent_dirs)
Creates a directory if it doesn't already exist.
Definition: utils.c:1359
gboolean utils_str_equal(const gchar *a, const gchar *b)
NULL-safe string comparison.
Definition: utils.c:599
gchar * utils_get_setting_string(GKeyFile *config, const gchar *section, const gchar *key, const gchar *default_value)
Wraps g_key_file_get_string() to add a default value argument.
Definition: utils.c:867
gchar * utils_get_locale_from_utf8(const gchar *utf8_text)
Converts the given UTF-8 encoded string into locale encoding.
Definition: utils.c:1243
guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
Replaces only the first occurrence of needle in haystack with replace.
Definition: utils.c:1563
#define foreach_slist(node, list)
Iterates all the nodes in list.
Definition: utils.h:121
#define foreach_strv(str_ptr, strv)
Iterates a null-terminated string vector.
Definition: utils.h:152
#define SETPTR(ptr, result)
Assigns result to ptr, then frees the old value.
Definition: utils.h:50
#define EMPTY(ptr)
Returns TRUE if ptr is NULL or *ptr is FALSE.
Definition: utils.h:38