1 /* Bluefish HTML Editor
2 * filebrowser2.c - the filebrowser v2
3 *
4 * Copyright (C) 2002-2017 Olivier Sessink
5 * Copyright (C) 2011 James Hayward
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * use G_DEBUG=fatal-warnings
21 * gdb src/bluefish
22 * to get backtraces what causes a warning
23 */
24
25 /*#define DEBUG*/
26
27 #if defined(__GNUC__) || (defined(__SUNPRO_C) && __SUNPRO_C > 0x580)
28 #define DBG_NONE(args...)
29 /**/
30 #else /* notdef __GNUC__ || __SUNPRO_C */
31 extern void g_none(char *first, ...);
32 #define DBG_NONE g_none
33 #endif
34
35 #define DEBUG_TREEMODELREFS DBG_NONE
36 #define DBG_FILTERSORT DBG_NONE
37 #define DBG_REFRESH DBG_NONE
38
39 /* ******* FILEBROWSER DESIGN ********
40 there is only one treestore left for all bluefish windows. This treestore has all files
41 and all directories used in all bluefish windows. This treestore has a column for the pixmap
42 and the name (both shown to the user), but also the GFile for the full path (excluding the
43 trailing slash for directories, see #1), and the type. This treestore is in main_v->fb2config. Each file
44 or directory added to the treestore is also added to a hashtable in main_v->fb2config
45
46 the GUI uses two filtermodels on top of the treestore, and then two sortmodels, so they can
47 show a specific part of the treestore (for example with a basedir other then the root, or with
48 some filter applied).
49
50 #1) the hash for file:///home/ is different from file:///home so we should choose which hash is
51 to be stored in the hashtable. The method gnome_vfs_uri_get_parent() returns directories without
52 the trailing slash. So it is convenient to use directories without trailing slashes all the way.
53
54 alex: g_hash_table_new(gnome_vfs_uri_hash, gnome_vfs_uri_hequal) is what you're supposed to do
55 */
56
57 /*#define DBG_FBREFCOUNT*/
58
59 #include <gtk/gtk.h>
60 #include <string.h>
61
62 #include "bluefish.h"
63 #include "filebrowser2.h"
64 #include "file_treemodel.h"
65 #include "bf_lib.h"
66 #include "bfwin_uimanager.h"
67 #include "dialog_utils.h"
68 #include "document.h"
69 #include "file.h"
70 #include "filefilter.h"
71 #include "file_dialogs.h"
72 #include "snr3.h"
73 #include "gtk_easy.h"
74 #include "project.h"
75 #include "pixmap.h"
76 #include "stringlist.h" /* add_to_history_stringlist() */
77
78 enum {
79 DIR_NAME_COLUMN,
80 DIR_URI_COLUMN,
81 DIR_ICON_COLUMN
82 };
83
84 enum {
85 viewmode_dual,
86 viewmode_flat,
87 viewmode_tree
88 };
89
90 typedef struct {
91 GtkWidget *dirmenu_v;
92 gulong dirmenu_changed_signal;
93 GtkTreeModel *dirmenu_m;
94 GFile *currentdir;
95
96 GtkWidget *vbox; /* toiplevel vbox for filebrowser widgets */
97 GtkWidget *dir_v; /* treeview widget */
98 GtkWidget *vpaned; /* added to the vbox when two panels are used */
99 GtkWidget *dirscrolwin; /* added to the vbox when one panel is used */
100 GtkWidget *file_v; /* treeview widget */
101
102 gulong expand_signal;
103 gulong dirselection_changed_id;
104
105 GFile *basedir;
106 Tfilter *curfilter;
107
108 GtkTreeModel *dir_filter;
109 GtkTreeModel *file_filter;
110
111 gboolean last_popup_on_dir;
112 gboolean filebrowser_show_hidden_files;
113 gboolean filebrowser_show_backup_files;
114 gint filebrowser_viewmode;
115 Tbfwin *bfwin;
116 } Tfilebrowser2;
117
118 #define FILEBROWSER2(var) ((Tfilebrowser2 *)(var))
119 static void fb2_set_dirmenu(Tfilebrowser2 * fb2, GFile * newcurdir, gboolean force_rebuild);
120 static void refilter_filelist(Tfilebrowser2 * fb2, GtkTreePath * newroot);
121 static void fb2_set_viewmode_widgets(Tfilebrowser2 * fb2, gint viewmode);
122 static void refilter_dirlist(Tfilebrowser2 * fb2, GtkTreePath * newroot);
123 static void set_basedir_backend(Tfilebrowser2 *fb2, GFile *dir_uri);
124 static void fb2_set_basedir(Tfilebrowser2 *fb2, GFile *dir_uri);
125 static void change_focus_to_uri(Tfilebrowser2 *fb2, GFile *uri);
126 static void set_file_v_root(Tfilebrowser2 *fb2, GFile *dir_uri);
127
128
129 /**************/
130 #ifdef DEBUG
131 static void
debug_gfile(GFile * uri,gboolean newline)132 debug_gfile(GFile * uri, gboolean newline)
133 {
134 if (uri) {
135 gchar *name = g_file_get_uri(uri);
136 g_print("%s (%p)%s", name, uri, newline ? "\n" : "");
137 g_free(name);
138 } else {
139 g_print("(GFile=NULL)%s", newline ? "\n" : "");
140 }
141 }
142 #endif
143
144 #ifdef DEBUG
145 #define DEBUG_GFILE debug_gfile
146 #else /* not DEBUG */
147 #ifdef __GNUC__
148 #define DEBUG_GFILE(args...)
149 /**/
150 #else /* notdef __GNUC__ */
151 extern void g_none(gchar * first, ...);
152 #define DEBUG_GFILE g_none
153 #endif /* __GNUC__ */
154 #endif /* DEBUG */
155
156 #ifdef DBG_FBREFCOUNT
157 guint fake_finfo_ref = 0;
158 #endif
159
160 /*****************************************************************************************************************************/
161 /* UTILITY FUNCTIONS */
162 /*****************************************************************************************************************************/
163
164 #define dir_v_filter_path_from_treestore_path(fb2, treestorepath) gtk_tree_model_filter_convert_child_path_to_path(GTK_TREE_MODEL_FILTER(fb2->dir_filter), treestorepath)
165 #define file_v_filter_path_from_treestore_path(fb2, treestorepath) gtk_tree_model_filter_convert_child_path_to_path(GTK_TREE_MODEL_FILTER(fb2->file_filter), treestorepath)
166
167 /*
168 treepath_for_uri
169
170 returns a GtkTreePath for the uri, and builds it (using fb2_build_dir()) if needed
171 */
172
173 static GtkTreePath *
treepath_for_uri(Tfilebrowser2 * fb2,GFile * uri)174 treepath_for_uri(Tfilebrowser2 * fb2, GFile * uri)
175 {
176 GtkTreeIter iter;
177 if (!uri)
178 return NULL;
179
180 if (!filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, uri, &iter)) {
181 if (!filetreemodel_build_dir(FB2CONFIG(main_v->fb2config)->ftm, uri, &iter))
182 return NULL;
183 }
184 return gtk_tree_model_get_path(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), &iter);
185 }
186
187
188 /* Gets treepath from uri; treepath is required for scrolling to it or selecting
189 * It combines the logic of two functions above ( treepath_for_uri() and and
190 dir_filter_path_from_treestore_path() ) and can be used for quick document
191 treepath selection after we refresh directory and required iter is already in hash table
192 */
193
194 /*static GtkTreePath *dir_sort_path_from_uri(Tfilebrowser2 * fb2, GFile * uri)
195 {
196 GtkTreePath *fs_path, *sort_path;
197
198 fs_path = treepath_for_uri(fb2, uri);
199 if (!fs_path)
200 return NULL;
201
202 sort_path = dir_v_filter_path_from_treestore_path(fb2, fs_path);
203 gtk_tree_path_free(fs_path);
204 return sort_path;
205 }*/
206
207 /*static gboolean
208 need_to_scroll_to_dir(Tfilebrowser2 * fb2, GFile *diruri)
209 {
210 GtkTreePath *start_path=NULL, *end_path=NULL;
211 GtkTreeIter it1;
212 gboolean retval=FALSE;
213 if (!gtk_tree_view_get_visible_range(GTK_TREE_VIEW(fb2->dir_v),&start_path,&end_path))
214 return FALSE;
215
216 if (!gtk_tree_model_get_iter(fb2->dir_filter,&it1,start_path)) {
217 UriRecord *record=NULL;
218
219 gtk_tree_model_get(fb2->dir_filter, &it1, filetreemodel_COL_RECORD, &record, -1);
220
221 if (record) {
222 / * now see if diruri is the parent of uri * /
223 retval = !gfile_uri_is_parent(diruri,record->uri,TRUE);
224 }
225 }
226 gtk_tree_path_free(start_path);
227 gtk_tree_path_free(end_path);
228 return retval;
229 }*/
230
231 static void
expand_without_directory_refresh(Tfilebrowser2 * fb2,GtkTreePath * sort_path)232 expand_without_directory_refresh(Tfilebrowser2 * fb2, GtkTreePath *sort_path)
233 {
234 g_signal_handler_block(fb2->dir_v, fb2->expand_signal);
235 DEBUG_MSG("expand_without_directory_refresh, sort+path=%p\n",sort_path);
236 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(fb2->dir_v), sort_path);
237 g_signal_handler_unblock(fb2->dir_v, fb2->expand_signal);
238 }
239
240 /**
241 * fb2_fspath_from_uri:
242 *
243 * return a newly allocated treepath for the filesystem_tstore based on 'uri'
244 */
245 /*static GtkTreePath *
246 fb2_fspath_from_uri(Tfilebrowser2 * fb2, GFile * uri)
247 {
248 GtkTreeIter iter;
249 if (filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, uri, &iter)) {
250 return gtk_tree_model_get_path(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), &iter);
251 }
252 return NULL;
253 }*/
254
255 /**
256 * fb2_uri_from_fspath:
257 *
258 * returns the uri stored in the treestore based on the 'fs_path' from the filesystem treestore
259 * /
260 static GFile *
261 fb2_uri_from_fspath(Tfilebrowser2 * fb2, GtkTreePath * fs_path)
262 {
263 if (fs_path) {
264 GtkTreeIter fsiter;
265 if (gtk_tree_model_get_iter
266 (GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), &fsiter, fs_path)) {
267 return fb2_uri_from_iter(&fsiter);
268 } else {
269 DEBUG_MSG("fb2_uri_from_fspath, WARNING, no fsiter for fs_path=%p ", fs_path);
270 DEBUG_TPATH(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), fs_path, TRUE);
271 }
272 }
273 return NULL;
274 }*/
275
276 /**
277 * fb2_isdir_from_dir_sort_path:
278 *
279 * returns TRUE if 'sort_path' from the directory sort model points to a directory
280 */
281 static gboolean
fb2_isdir_from_dir_sort_path(Tfilebrowser2 * fb2,GtkTreePath * sort_path)282 fb2_isdir_from_dir_sort_path(Tfilebrowser2 * fb2, GtkTreePath * sort_path)
283 {
284 GtkTreeIter iter;
285 if (gtk_tree_model_get_iter(fb2->dir_filter, &iter, sort_path)) {
286 UriRecord *record=NULL;
287 gtk_tree_model_get(fb2->dir_filter, &iter, filetreemodel_COL_RECORD, &record, -1);
288 return record->isdir;
289 }
290 return FALSE;
291 }
292
293 /**
294 * fb2_uri_from_dir_selection:
295 *
296 * returns a newly allocated treepath for the current selected entry in the dir view
297 */
298 static GFile *
fb2_uri_from_dir_selection(Tfilebrowser2 * fb2)299 fb2_uri_from_dir_selection(Tfilebrowser2 * fb2)
300 {
301 GFile *uri = NULL;
302 GtkTreeModel *sort_model;
303 GtkTreeIter sort_iter;
304 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(fb2->dir_v));
305 if (gtk_tree_selection_get_selected(selection, &sort_model, &sort_iter)) {
306 UriRecord *record=NULL;
307 gtk_tree_model_get(sort_model, &sort_iter, filetreemodel_COL_RECORD, &record, -1);
308 if (record) {
309 uri = record->uri;
310 g_object_ref(uri);
311 }
312 }
313 return uri;
314 }
315
316 /**
317 * fb2_uri_from_file_selection:
318 *
319 * returns the uri stored in the treestore for the current selected entry in the file_v
320 */
321 static GFile *
fb2_uri_from_file_selection(Tfilebrowser2 * fb2,gchar ** mime)322 fb2_uri_from_file_selection(Tfilebrowser2 * fb2, gchar ** mime)
323 {
324 GtkTreeModel *sort_model;
325 GtkTreeIter sort_iter;
326 UriRecord *record=NULL;
327 if (fb2->file_v
328 && gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(fb2->file_v)),
329 &sort_model, &sort_iter)) {
330 gtk_tree_model_get(sort_model, &sort_iter, filetreemodel_COL_RECORD, &record, -1);
331 } else if (!fb2->file_v && fb2->dir_v
332 &&
333 gtk_tree_selection_get_selected(gtk_tree_view_get_selection
334 (GTK_TREE_VIEW(fb2->dir_v)), &sort_model, &sort_iter)) {
335 gtk_tree_model_get(sort_model, &sort_iter, filetreemodel_COL_RECORD, &record, -1);
336 }
337 #ifdef DEBUG
338 else
339 DEBUG_MSG("fb2_uri_from_file_selection, no selection!\n");
340 #endif
341 if (record) {
342 if (mime) {
343 *mime = record->fast_content_type;
344 }
345 return record->uri;
346 }
347 return NULL;
348 }
349
350 static void
add_uri_to_recent_dirs(Tfilebrowser2 * fb2,GFile * uri)351 add_uri_to_recent_dirs(Tfilebrowser2 * fb2, GFile * uri)
352 {
353 gchar *tmp;
354
355 tmp = g_file_get_uri(uri);
356 DEBUG_MSG("add_uri_to_recent_dirs, add %s to history stringlist\n",tmp);
357 fb2->bfwin->session->recent_dirs =
358 add_to_history_stringlist(fb2->bfwin->session->recent_dirs, tmp, TRUE);
359 g_free(tmp);
360 }
361
362 static void
remove_uri_from_recent_dirs(Tfilebrowser2 * fb2,GFile * uri)363 remove_uri_from_recent_dirs(Tfilebrowser2 * fb2, GFile * uri)
364 {
365 gchar *tmp;
366
367 tmp = g_file_get_uri(uri);
368
369 fb2->bfwin->session->recent_dirs =
370 remove_from_stringlist(fb2->bfwin->session->recent_dirs, tmp);
371 g_free(tmp);
372 }
373
374
375 /* Return common uri part of two input uri's. Required for setting basedir when activating or opening file that is outside current basedir */
376
find_common_path(GFile * file1,GFile * file2)377 GFile *find_common_path(GFile *file1, GFile *file2)
378 {
379 gchar *filename1, *filename2;
380 gint pos;
381
382 if (!file1 || !file2)
383 return NULL;
384 DEBUG_MSG("find_common_path, file1=%p, file2=%p\n",file1,file2);
385 filename1 = g_file_get_parse_name(file1);
386 if (!filename1)
387 return NULL;
388
389 filename2 = g_file_get_parse_name(file2);
390 if (!filename2) {
391 g_free(filename1);
392 return NULL;
393 }
394
395 DEBUG_MSG("find_common_path, filename1=%s, filename2=%s \n", filename1, filename2);
396 for (pos = 0; ; pos++) {
397 if (filename1[pos] != '\0' && filename1[pos] == filename2[pos])
398 continue;
399 if (filename1[pos] != G_DIR_SEPARATOR && filename2[pos] != G_DIR_SEPARATOR) {
400 /* scroll back */
401 while (pos > 0 && filename1[--pos] != G_DIR_SEPARATOR);
402 } else {
403 if (filename1[pos] == G_DIR_SEPARATOR)
404 --pos;
405 }
406 if (pos<=0) {
407 DEBUG_MSG("find_common_path, sorry, common path is not found (pos==0)\n");
408 g_free(filename1);
409 g_free(filename2);
410 return NULL;
411 }
412 DEBUG_MSG("got pos=%d, len=%d\n",pos,strlen(filename1));
413 gchar *common_path = g_strndup (filename1, pos);
414 DEBUG_MSG("find_common_path, found commom path is %s, pos=%d \n", common_path, pos);
415 GFile *common_uri = g_file_parse_name(common_path);
416 g_free(filename1);
417 g_free(filename2);
418 g_free(common_path);
419 if (g_file_query_exists(common_uri,NULL)) {
420 return common_uri;
421 } else {
422 g_object_unref(common_uri);
423 g_warning("find_common_path, there is some error, common path does not exist");
424 return NULL;
425 }
426 }
427 return NULL;
428 }
429
430
431 /*****************************************************************************************************************************/
432 /* END OF UTILITY FUNCTIONS - START OF DIRECTORY RE-READ FUNCTIONS*/
433 /*****************************************************************************************************************************/
434
delayed_refresh_cb(gpointer data)435 gboolean delayed_refresh_cb(gpointer data)
436 {
437 if (FB2CONFIG(main_v->fb2config)->uri_to_refresh) {
438 DEBUG_MSG("delayed_refresh_cb, refresh uri %s\n",g_file_get_path(FB2CONFIG(main_v->fb2config)->uri_to_refresh));
439 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, FB2CONFIG(main_v->fb2config)->uri_to_refresh);
440 g_object_unref(FB2CONFIG(main_v->fb2config)->uri_to_refresh);
441 }
442 FB2CONFIG(main_v->fb2config)->uri_to_refresh = NULL;
443 FB2CONFIG(main_v->fb2config)->delayed_refresh_id = 0;
444 return FALSE;
445 }
446
447 /*
448 by delaying the refresh, if quickly change multiple tabs, you won't call a directory refresh for every document, only
449 a dir for a document where you stay more than 200ms will be refreshed
450 */
451 static void
register_delayed_refresh(GFile * uri)452 register_delayed_refresh(GFile *uri) {
453 if (!uri)
454 return;
455 DEBUG_MSG("register_delayed_refresh, called for %s\n",g_file_get_path(uri));
456 if (uri_in_refresh(FB2CONFIG(main_v->fb2config)->ftm, uri)) {
457 if (FB2CONFIG(main_v->fb2config)->uri_to_refresh && g_file_equal(FB2CONFIG(main_v->fb2config)->uri_to_refresh,uri)) {
458 g_object_unref(FB2CONFIG(main_v->fb2config)->uri_to_refresh);
459 FB2CONFIG(main_v->fb2config)->uri_to_refresh = NULL;
460 if (FB2CONFIG(main_v->fb2config)->delayed_refresh_id) {
461 g_source_remove(FB2CONFIG(main_v->fb2config)->delayed_refresh_id);
462 }
463 }
464 DEBUG_MSG("register_delayed_refresh, uri=%s is already in refresh, returning\n",g_file_get_path(uri));
465 return;
466 }
467
468 if (FB2CONFIG(main_v->fb2config)->uri_to_refresh && g_file_equal(FB2CONFIG(main_v->fb2config)->uri_to_refresh,uri)) {
469 return;
470 }
471 if (FB2CONFIG(main_v->fb2config)->uri_to_refresh)
472 g_object_unref(FB2CONFIG(main_v->fb2config)->uri_to_refresh);
473 FB2CONFIG(main_v->fb2config)->uri_to_refresh = uri;
474 g_object_ref(FB2CONFIG(main_v->fb2config)->uri_to_refresh);
475 if (FB2CONFIG(main_v->fb2config)->delayed_refresh_id) {
476 g_source_remove(FB2CONFIG(main_v->fb2config)->delayed_refresh_id);
477 }
478 FB2CONFIG(main_v->fb2config)->delayed_refresh_id = g_timeout_add_full( G_PRIORITY_LOW,200,delayed_refresh_cb,NULL,NULL);
479 }
480
481 /**
482 * fb2_refresh_parent_of_uri:
483 *
484 * convenience function, will refresh the parent directory of child_uri
485 */
486 void
fb2_refresh_parent_of_uri(GFile * child_uri)487 fb2_refresh_parent_of_uri(GFile * child_uri)
488 {
489 GFile *parent_uri = g_file_get_parent(child_uri);
490 if (parent_uri) {
491 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, parent_uri);
492 g_object_unref(parent_uri);
493 }
494 }
495
496 /*****************************************************************************************************************************/
497 /* END OF DIRECTORY RE-READ FUNCTIONS - START OF FILTERING FUNCTIONS*/
498 /*****************************************************************************************************************************/
499
500
501 /**
502 * tree_model_filter_func
503 *
504 * will return TRUE if this file should be visible in the dir view
505 */
506 static gboolean
tree_model_filter_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)507 tree_model_filter_func(GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
508 {
509 Tfilebrowser2 *fb2 = data;
510 gint len;
511 gboolean retval = TRUE;
512 UriRecord *record=NULL;
513 DBG_FILTERSORT("tree_model_filter_func, model=%p, fb2=%p, model,fb2, iter %p with iter->user_data=%p\n",model,fb2,iter,iter?iter->user_data:NULL);
514 gtk_tree_model_get(GTK_TREE_MODEL(model), iter, filetreemodel_COL_RECORD, &record, -1);
515 if (!record) {
516 g_warning("tree_model_filter_func, model=%p, fb2=%p, iter=%p, iter->user_data=%p, item without record!!\n",model, fb2, iter, iter?iter->user_data:NULL);
517 return FALSE;
518 }
519
520 if (!record->isdir) { /* file */
521 if (fb2->filebrowser_viewmode == viewmode_dual) {
522 /* in the two paned view we don't show files in the dir view */
523 retval = FALSE;
524 } else {
525 if (fb2->filebrowser_viewmode == viewmode_flat) {
526 if (fb2->basedir) {
527 if (!gfile_uri_is_parent(fb2->basedir, record->uri, FALSE)) {
528 retval = FALSE;
529 }
530 }
531 }
532 if (retval && !fb2->filebrowser_show_backup_files) {
533 len = strlen(record->name);
534 if (len > 1 && (record->name[len - 1] == '~'))
535 retval = FALSE;
536 }
537 if (retval && !fb2->filebrowser_show_hidden_files) {
538 if (record->name[0] == '.')
539 retval = FALSE;
540 }
541 if (fb2->basedir && !gfile_uri_is_parent(fb2->basedir, record->uri, TRUE)) {
542 retval = FALSE;
543 }
544 if (retval && fb2->curfilter)
545 retval = file_visible_in_filter(fb2->curfilter, record->fast_content_type, record->name);
546 }
547 DBG_FILTERSORT("file %s with mime_type %s: returning %d\n", record->name, record->fast_content_type, retval);
548 } else { /* directory */
549 if (fb2->filebrowser_viewmode == viewmode_flat) {
550 if (fb2->basedir) {
551 /* show only the level of our basedir no deeper directories */
552 if (!gfile_uri_is_parent(fb2->basedir, record->uri, FALSE)) {
553 retval = FALSE;
554 }
555 }
556 } else {
557 if (fb2->basedir) {
558 /* show only our basedir on the root level, no other directories at that level */
559 if (!gfile_uri_is_parent(fb2->basedir, record->uri, TRUE)
560 && !g_file_equal(fb2->basedir, record->uri)) {
561 retval = FALSE;
562 }
563 }
564 }
565 if (retval && !fb2->filebrowser_show_hidden_files) {
566 if (record->name[0] == '.')
567 retval = FALSE;
568 }
569 DBG_FILTERSORT("dir %s: returning %d\n", record->name, retval);
570 }
571 return retval;
572 }
573
574 /**
575 * file_list_filter_func:
576 *
577 * will return TRUE if this file should be visible in the file list
578 */
579 static gboolean
file_list_filter_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)580 file_list_filter_func(GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
581 {
582 Tfilebrowser2 *fb2 = data;
583 gint len;
584 gboolean retval = TRUE;
585 UriRecord *record=NULL;
586 DBG_FILTERSORT("file_list_filter_func, called for model=%p and fb2=%p, iter %p with iter->user_data=%p\n",model,fb2,iter,iter?iter->user_data:NULL);
587 gtk_tree_model_get(GTK_TREE_MODEL(model), iter, filetreemodel_COL_RECORD, &record, -1);
588
589 if (!record)
590 return FALSE;
591
592 if (record->isdir)
593 return FALSE;
594
595 if (retval && !fb2->filebrowser_show_backup_files) {
596 len = strlen(record->name);
597 if (len > 1 && (record->name[len - 1] == '~'))
598 retval = FALSE;
599 }
600 if (retval && !fb2->filebrowser_show_hidden_files) {
601 if (record->name[0] == '.')
602 retval = FALSE;
603 }
604 if (retval && fb2->curfilter)
605 retval = file_visible_in_filter(fb2->curfilter, record->fast_content_type, record->name);
606 #ifdef DEBUG
607 if (retval == FALSE) {
608 DBG_FILTERSORT("file_list_filter_func, hiding %s (%s)\n", record->name, record->fast_content_type);
609 }
610 #endif
611 return retval;
612 }
613
614 static void
fb2_refilter(Tfilebrowser2 * fb2)615 fb2_refilter(Tfilebrowser2 * fb2)
616 {
617 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fb2->dir_filter));
618 if (fb2->filebrowser_viewmode == viewmode_dual) {
619 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fb2->file_filter));
620 }
621 }
622
623 /*****************************************************************************************************************************/
624 /* END OF FILTERING FUNCTIONS, START OF DATA MODEL FUNCTIONS */
625 /*****************************************************************************************************************************/
626
627 void
fb2_file_is_opened(GFile * uri,const gchar * mimetype)628 fb2_file_is_opened(GFile *uri, const gchar *mimetype)
629 {
630 GtkTreeIter iter;
631 if (!uri)
632 return;
633 DEBUG_MSG("fb2_file_is_opened, called for %s with mimetype %s\n",g_file_get_path(uri),mimetype);
634 if (!filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, uri, &iter)) {
635 /* add entry */
636 filetreemodel_add_file(FB2CONFIG(main_v->fb2config)->ftm, uri, mimetype, PANGO_WEIGHT_BOLD);
637 } else {
638 filetreemodel_set_weight(FB2CONFIG(main_v->fb2config)->ftm, uri, PANGO_WEIGHT_BOLD);
639 }
640 }
641
fb2_file_is_closed(GFile * uri)642 void fb2_file_is_closed(GFile *uri)
643 {
644 if (!uri)
645 return;
646 filetreemodel_set_weight(FB2CONFIG(main_v->fb2config)->ftm, uri, PANGO_WEIGHT_NORMAL);
647 }
648
649 static void
fb2config_set_documentroot_icon(GFile * uri)650 fb2config_set_documentroot_icon(GFile *uri)
651 {
652 if (!uri)
653 return;
654 filetreemodel_set_icon(FB2CONFIG(main_v->fb2config)->ftm, uri,BF_STOCK_BROWSER_PREVIEW);
655 }
656
657 /* if a filter is deleted, this function is called to make sure
658 the current filebrowser isn't actually using the filter */
659 void
fb2_unset_filter(Tbfwin * bfwin,Tfilter * filter)660 fb2_unset_filter(Tbfwin * bfwin, Tfilter * filter)
661 {
662 if (bfwin->fb2 && FILEBROWSER2(bfwin->fb2)->curfilter == filter) {
663 FILEBROWSER2(bfwin->fb2)->curfilter = NULL;
664 fb2_refilter(FILEBROWSER2(bfwin->fb2));
665 }
666 }
667
668 void
fb2config_init(void)669 fb2config_init(void)
670 {
671 Tfilebrowser2config *fb2config;
672 DEBUG_MSG("fb2config_init, started\n");
673 /* a lot of data can be stored once for all windows;
674 This will be initialized in this function */
675 fb2config = g_new0(Tfilebrowser2config, 1);
676 main_v->fb2config = fb2config;
677 fb2config->ftm = filetreemodel_new();
678 }
679
680 #ifdef MEMORY_LEAK_DEBUG
681
682 /* avoid segfaults during bluefish exit */
683 void
fb2config_cleanup(void)684 fb2config_cleanup(void)
685 {
686 g_object_unref(FB2CONFIG(main_v->fb2config)->ftm);
687 DEBUG_TREEMODELREFS("fb2config_cleanup, unreffed filesystem treestore at %p\n",FB2CONFIG(main_v->fb2config)->ftm);
688 g_free(main_v->fb2config);
689 }
690 #endif
691
692 /*****************************************************************************************************************************/
693 /* END OF DATA MODEL FUNCTIONS, START OF DIALOGS / MENU FUNCTIONS */
694 /*****************************************************************************************************************************/
695
696
697 typedef struct {
698 Tbfwin *bfwin;
699 GtkWidget *win;
700 GtkWidget *doc_entry;
701 GtkWidget *web_entry;
702 GFile *documentroot;
703 GFile *webroot;
704 } Tdocrootdialog;
705
706 static void
drd_response_lcb(GtkDialog * dialog,gint response,Tdocrootdialog * drd)707 drd_response_lcb(GtkDialog * dialog, gint response, Tdocrootdialog * drd)
708 {
709 if (response == GTK_RESPONSE_ACCEPT) {
710 GFile *docroot, *webroot;
711
712 docroot = g_file_new_for_uri(gtk_entry_get_text(GTK_ENTRY(drd->doc_entry)));
713 webroot = g_file_new_for_uri(gtk_entry_get_text(GTK_ENTRY(drd->web_entry)));
714
715 if (docroot && webroot) {
716 if (drd->bfwin->session->documentroot)
717 g_free(drd->bfwin->session->documentroot);
718 drd->bfwin->session->documentroot = g_file_get_uri(docroot);
719 fb2config_set_documentroot_icon(docroot);
720
721 if (drd->bfwin->session->webroot)
722 g_free(drd->bfwin->session->webroot);
723 drd->bfwin->session->webroot = g_file_get_uri(webroot);
724
725 g_object_unref(docroot);
726 g_object_unref(webroot);
727 }
728 }
729 gtk_widget_destroy(drd->win);
730 if (drd->webroot)
731 g_object_unref(drd->webroot);
732 if (drd->documentroot)
733 g_object_unref(drd->documentroot);
734 g_free(drd);
735 }
736
737 static void
set_documentroot_dialog(Tbfwin * bfwin,GFile * uri)738 set_documentroot_dialog(Tbfwin * bfwin, GFile * uri)
739 {
740 /* now start the dialog to set the webroot */
741 GtkWidget *table, *label;
742 gchar *tmp;
743 Tdocrootdialog *drd;
744 drd = g_new0(Tdocrootdialog, 1);
745 drd->bfwin = bfwin;
746 drd->documentroot = uri;
747 g_object_ref(drd->documentroot);
748 drd->win =
749 gtk_dialog_new_with_buttons(_("Set documentroot and webroot"),
750 GTK_WINDOW(bfwin->main_window), GTK_DIALOG_DESTROY_WITH_PARENT,
751 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK,
752 GTK_RESPONSE_ACCEPT, NULL);
753 g_signal_connect(G_OBJECT(drd->win), "response", G_CALLBACK(drd_response_lcb), drd);
754 table = dialog_table_in_vbox_defaults(2, 2, 5, gtk_dialog_get_content_area(GTK_DIALOG(drd->win)));
755 label = gtk_label_new(NULL);
756 gtk_label_set_markup(GTK_LABEL(label),
757 _
758 ("The <i>documentroot</i> is the place where Bluefish finds the file,\nthe <i>webroot</i> is the location where those same files can be viewed on the webserver"));
759 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
760
761 tmp = g_file_get_uri(uri);
762 drd->doc_entry = dialog_entry_in_table(tmp, table, 1, 2, 1, 2);
763 gtk_editable_set_editable(GTK_EDITABLE(drd->doc_entry), FALSE);
764 g_free(tmp);
765 dialog_mnemonic_label_in_table(_("Documentroot"), drd->doc_entry, table, 0, 1, 1, 2);
766
767 if (bfwin->session->webroot) {
768 tmp = g_strdup(bfwin->session->webroot);
769 } else if (g_file_has_uri_scheme(uri, "file")) {
770 tmp = g_strdup("http://localhost/");
771 } else {
772 tmp = g_strdup("http://");
773 }
774 drd->web_entry = dialog_entry_in_table(tmp, table, 1, 2, 2, 3);
775 g_free(tmp);
776 dialog_mnemonic_label_in_table(_("Webroot"), drd->web_entry, table, 0, 1, 2, 3);
777
778 gtk_widget_show_all(drd->win);
779 }
780
781
782
783 static void
rename_not_open_file(Tbfwin * bfwin,GFile * olduri)784 rename_not_open_file(Tbfwin * bfwin, GFile * olduri)
785 {
786 gchar *newfilename = NULL, *oldfilename, *dialogtext;
787 /* Promt user, "File/Move To"-style. */
788 oldfilename = g_file_get_uri(olduri);
789 dialogtext = g_strdup_printf(_("Move/rename %s to"), oldfilename);
790 newfilename = ask_new_filename(bfwin, oldfilename, dialogtext);
791 g_free(dialogtext);
792 if (newfilename) {
793 GFile *newuri = NULL;
794 gboolean res;
795 GError *error = NULL;
796 newuri = g_file_new_for_uri(newfilename);
797 res = g_file_move(olduri, newuri, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
798
799 if (!res) {
800 gchar *errmessage = g_strconcat(_("Could not rename\n"), oldfilename, NULL);
801 message_dialog_new(bfwin->main_window, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, errmessage, NULL);
802 g_free(errmessage);
803 } else {
804 GFile *parent1, *parent2;
805
806 /*
807 GtkTreeIter *olditer;
808 olditer = g_hash_table_lookup(FB2CONFIG(main_v->fb2config)->filesystem_itable, olduri);
809 if (olditer)
810 fb2_treestore_delete_children(FB2CONFIG(main_v->fb2config)->ftm, olditer,
811 FALSE);
812 */
813 parent1 = g_file_get_parent(olduri);
814 parent2 = g_file_get_parent(newuri);
815 DEBUG_MSG("parent1=");
816 DEBUG_GFILE(parent1, FALSE);
817 DEBUG_MSG(", parent2=");
818 DEBUG_GFILE(parent2, TRUE);
819 if (parent1 && parent2 && !g_file_equal(parent1, parent2)) {
820 fb2_refresh_parent_of_uri(olduri);
821 fb2_refresh_parent_of_uri(newuri);
822 } else if (parent1) {
823 DEBUG_MSG("rename_not_open_file, refresh parent1");
824 DEBUG_GFILE(parent1, TRUE);
825 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, parent1);
826 } else if (parent2) {
827 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, parent2);
828 }
829 g_object_unref(parent1);
830 g_object_unref(parent2);
831 }
832 g_free(newfilename);
833 g_object_unref(newuri);
834 }
835 g_free(oldfilename);
836 }
837
838 static void
fb2rpopup_new(Tfilebrowser2 * fb2,gboolean newisdir,GFile * noselectionbaseuri)839 fb2rpopup_new(Tfilebrowser2 * fb2, gboolean newisdir, GFile * noselectionbaseuri)
840 {
841 GFile *baseuri = NULL;
842 if (fb2->last_popup_on_dir) {
843 baseuri = fb2_uri_from_dir_selection(fb2);
844 /* fb2_uri_from_dir_selection returns a pointer without an extra reference, so we ref it */
845 if (baseuri) {
846 g_object_ref(baseuri);
847 DEBUG_MSG("fb2rpopup_new, baseuri from dir selection=");
848 DEBUG_GFILE(baseuri, TRUE);
849 }
850 } else {
851 GFile *childuri = fb2_uri_from_file_selection(fb2, NULL);
852 /* fb2_uri_from_file_selection returns a pointer without an extra reference, so we ref it */
853 if (childuri) {
854 baseuri = g_file_get_parent(childuri);
855 DEBUG_MSG("fb2rpopup_new, baseuri from file selection=");
856 DEBUG_GFILE(baseuri, TRUE);
857 }
858 }
859 if (!baseuri) {
860 /* no selection, try the noselectionbaseuri and give it an extra reference */
861 baseuri = noselectionbaseuri;
862 g_object_ref(baseuri);
863 DEBUG_MSG("fb2rpopup_new, baseuri from noselectionbaseuri=");
864 DEBUG_GFILE(baseuri, TRUE);
865 }
866
867 if (baseuri) {
868 GFile *newuri;
869 gboolean done = FALSE;
870 DEBUG_MSG("fb2rpopup_new, baseuri=");
871 DEBUG_GFILE(baseuri, TRUE);
872 if (newisdir) {
873 GError *error = NULL;
874 newuri = g_file_get_child(baseuri, _("New directory"));
875 if (!newuri) {
876 DEBUG_MSG("failed to create GFile for baseuri ???????????????????????????\n");
877 g_object_unref(baseuri);
878 return;
879 }
880 DEBUG_MSG("fb2rpopup_new, newuri=");
881 DEBUG_GFILE(newuri, TRUE);
882 done = g_file_make_directory(newuri, NULL, &error);
883 if (error) {
884 gchar *tmp = g_file_get_uri(newuri);
885 DEBUG_MSG("fb2rpopup_new, failed to create directory %s: %s, done=%d\n", tmp, error->message,
886 done);
887 g_error_free(error);
888 g_free(tmp);
889 } else {
890 rename_not_open_file(fb2->bfwin, newuri);
891 }
892 } else {
893 gint counter = 0;
894 gchar *filename;
895 do {
896 GFileOutputStream *gfos;
897 GError *error = NULL;
898 if (counter == 0)
899 filename = g_strdup(_("New file"));
900 else
901 filename = g_strdup_printf("%s%d", _("New file"), counter);
902 newuri = g_file_get_child(baseuri, filename);
903 g_free(filename);
904 DEBUG_MSG("fb2rpopup_new, newuri=");
905 DEBUG_GFILE(newuri, TRUE);
906 gfos = g_file_create(newuri, G_FILE_CREATE_NONE, NULL, &error);
907 if (gfos) {
908 g_output_stream_close((GOutputStream *) gfos, NULL, &error);
909 done = TRUE;
910 counter = 100;
911 } else if (error && error->code == G_IO_ERROR_EXISTS) {
912 counter++;
913 g_object_unref(newuri);
914 g_error_free(error);
915 } else {
916 counter = 100;
917 if (error) {
918 DEBUG_MSG("fb2rpopup_new, failed to create file: %s\n", error->message);
919 g_error_free(error);
920 }
921 }
922 } while (counter < 100);
923 if (done) {
924 rename_not_open_file(fb2->bfwin, newuri);
925 }
926 }
927 if (done) {
928 DEBUG_MSG("calling for newuri %p %s\n", newuri, g_file_get_uri(newuri));
929 fb2_refresh_parent_of_uri(newuri);
930 }
931 g_object_unref(newuri);
932 g_object_unref(baseuri);
933 } else {
934 DEBUG_MSG("not creating new file => no baseuri, perhaps we should use the basedir ??\n");
935 }
936 }
937
938 static void
rcpopup_async_delete_lcb(gpointer data)939 rcpopup_async_delete_lcb(gpointer data)
940 {
941 GFile *uri = data;
942 if (uri) {
943 GList *alldocs;
944 Tdocument *exdoc;
945 alldocs = return_allwindows_documentlist();
946 exdoc = documentlist_return_document_from_uri(alldocs, uri);
947 if (exdoc) {
948 document_unset_filename(exdoc);
949 }
950 g_list_free(alldocs);
951
952 fb2_refresh_parent_of_uri(uri);
953
954 g_object_unref(uri);
955 }
956 }
957
958 static void
popup_menu_delete(GtkAction * action,gpointer user_data)959 popup_menu_delete(GtkAction * action, gpointer user_data)
960 {
961 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
962 GFile *uri;
963
964 if (fb2->last_popup_on_dir) {
965 uri = fb2_uri_from_dir_selection(fb2);
966 } else {
967 uri = fb2_uri_from_file_selection(fb2, NULL);
968 }
969 if (uri) {
970 const gchar *buttons[] = { GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL };
971 gchar *text, *text2;
972 gint retval;
973 gchar *fullpath, *filename;
974 fullpath = g_file_get_uri(uri);
975 filename = gfile_display_name(uri, NULL);
976 /* Do we really need to display the full path here?
977 *
978 * Having the filename in the both the primary and secondary text seems to be redundant.
979 * Set back to just the primary text for now.
980 *
981 * --> Sometimes you can have a file in many directories (e.g. Makefile.in, index.html)
982 * and then this might give you more indication if this is indeed the file you wanted to delete
983 */
984 text = g_strdup_printf(_("Are you sure you want to delete\n\"%s\"?"), filename);
985 text2 = g_strdup_printf(_("If you delete %s, it will be permanently lost."), fullpath);
986 retval =
987 message_dialog_new_multi(fb2->bfwin->main_window, GTK_MESSAGE_QUESTION, buttons, text, text2);
988 g_free(text);
989 g_free(text2);
990 if (retval == 1) {
991 /* ref the uri, it is unreffed by the callback */
992 g_object_ref(uri);
993 DEBUG_MSG("fb2rpopup_delete, calling file_delete_file_async\n");
994 file_delete_async(uri, FALSE, rcpopup_async_delete_lcb, uri);
995 }
996 g_free(filename);
997 g_free(fullpath);
998 }
999 }
1000
1001 static void
popup_menu_filter_activate(GtkAction * action,gpointer user_data)1002 popup_menu_filter_activate(GtkAction * action, gpointer user_data)
1003 {
1004
1005 Tbfwin *bfwin=user_data;
1006 Tfilebrowser2 *fb2;
1007
1008 if (!bfwin || !bfwin->fb2)
1009 return;
1010
1011 fb2 = FILEBROWSER2(bfwin->fb2);
1012 if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action))) {
1013 /* loop trough the filters for a filter with this name */
1014 const gchar *name = gtk_action_get_name(action);
1015 Tfilter *filter = find_filter_by_name(name);
1016
1017 DEBUG_MSG("popup_menu_filter_activate, setting curfilter to %p from name %s\n", filter, name);
1018 fb2->curfilter = filter;
1019 if (filter) {
1020 if (fb2->bfwin->session->last_filefilter)
1021 g_free(fb2->bfwin->session->last_filefilter);
1022 fb2->bfwin->session->last_filefilter = g_strdup(filter->name);
1023 }
1024 fb2_refilter(fb2);
1025 }
1026 }
1027
1028 static void
popup_menu_follow_active_doc(GtkAction * action,gpointer user_data)1029 popup_menu_follow_active_doc(GtkAction * action, gpointer user_data)
1030 {
1031 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1032
1033 fb2->bfwin->session->filebrowser_focus_follow = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1034
1035 if (fb2->bfwin->session->filebrowser_focus_follow && fb2->bfwin->current_document)
1036 fb2_focus_document(fb2->bfwin, fb2->bfwin->current_document);
1037 }
1038
1039 static void
popup_menu_new_directory(GtkAction * action,gpointer user_data)1040 popup_menu_new_directory(GtkAction * action, gpointer user_data)
1041 {
1042 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1043
1044 fb2rpopup_new(fb2, TRUE, fb2->basedir);
1045 }
1046
1047 static void
popup_menu_new_file(GtkAction * action,gpointer user_data)1048 popup_menu_new_file(GtkAction * action, gpointer user_data)
1049 {
1050 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1051
1052 if (fb2->file_v == NULL) {
1053 fb2rpopup_new(fb2, FALSE, fb2->basedir);
1054 } else {
1055 fb2rpopup_new(fb2, FALSE, fb2->currentdir);
1056 }
1057 }
1058
1059 static void
popup_menu_open(GtkAction * action,gpointer user_data)1060 popup_menu_open(GtkAction * action, gpointer user_data)
1061 {
1062 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1063 GFile *uri;
1064 gchar *mime = NULL;
1065
1066 uri = fb2_uri_from_file_selection(fb2, &mime);
1067 if (uri) {
1068 DEBUG_MSG("calling file_handle for popup menu\n");
1069 file_handle(uri, fb2->bfwin, mime, TRUE, TRUE);
1070 }
1071 }
1072
1073 static void
popup_menu_open_advanced(GtkAction * action,gpointer user_data)1074 popup_menu_open_advanced(GtkAction * action, gpointer user_data)
1075 {
1076 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1077 GFile *uri = fb2_uri_from_dir_selection(fb2);
1078 gchar *curi = g_file_get_uri(uri);
1079 files_advanced_win(fb2->bfwin, curi);
1080 g_free(curi);
1081 }
1082
1083 static void
popup_menu_search_files(GtkAction * action,gpointer user_data)1084 popup_menu_search_files(GtkAction * action, gpointer user_data)
1085 {
1086 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1087 GFile *uri = fb2_uri_from_dir_selection(fb2);
1088 gchar *curi = g_file_get_uri(uri);
1089 snr3_advanced_dialog_files(BFWIN(fb2->bfwin), curi);
1090 g_free(curi);
1091 }
1092
1093 static void
popup_menu_refresh(GtkAction * action,gpointer user_data)1094 popup_menu_refresh(GtkAction * action, gpointer user_data)
1095 {
1096 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1097 GFile *baseuri = NULL;
1098 gboolean unref_baseuri = FALSE;
1099
1100 if (fb2->last_popup_on_dir) {
1101 baseuri = fb2_uri_from_dir_selection(fb2); /* returns the uri in the treestore */
1102 } else {
1103 GFile *childuri = fb2_uri_from_file_selection(fb2, NULL); /* returns the uri in the treestore */
1104 if (childuri) {
1105 baseuri = g_file_get_parent(childuri);
1106 unref_baseuri = TRUE;
1107 }
1108 }
1109 if (!baseuri
1110 && (fb2->filebrowser_viewmode == viewmode_flat || fb2->filebrowser_viewmode == viewmode_dual)) {
1111 /* in flat view or dual view we can refresh the basedir now */
1112 baseuri = fb2->basedir;
1113 }
1114 if (baseuri) {
1115 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, baseuri);
1116 if (unref_baseuri)
1117 g_object_unref(baseuri);
1118 }
1119 }
1120
1121 static void
popup_menu_rename(GtkAction * action,gpointer user_data)1122 popup_menu_rename(GtkAction * action, gpointer user_data)
1123 {
1124 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1125 GFile *olduri;
1126
1127 if (fb2->last_popup_on_dir) {
1128 olduri = fb2_uri_from_dir_selection(fb2);
1129 } else {
1130 olduri = fb2_uri_from_file_selection(fb2, NULL);
1131 }
1132 if (olduri) {
1133 Tdocument *tmpdoc;
1134 GList *alldocs;
1135
1136 g_object_ref(olduri);
1137
1138 /* Use doc_save(doc, 1, 1) if the file is open. */
1139
1140 alldocs = return_allwindows_documentlist();
1141 tmpdoc = documentlist_return_document_from_uri(alldocs, olduri);
1142 g_list_free(alldocs);
1143 if (tmpdoc != NULL) {
1144 DEBUG_MSG("fb2rpopup_rename, file is open. Calling doc_save() with 'do_move'.\n");
1145 /* If an error occurs, doc_save takes care of notifying the user.
1146 * Currently, nothing is done here. */
1147 doc_save_backend(tmpdoc, docsave_move, FALSE, FALSE);
1148 } else { /* olduri is not open */
1149 rename_not_open_file(fb2->bfwin, olduri);
1150 }
1151 g_object_unref(olduri);
1152 }
1153 }
1154
1155 static void
popup_menu_insert_link(GtkAction * action,gpointer user_data)1156 popup_menu_insert_link(GtkAction * action, gpointer user_data)
1157 {
1158 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1159 GFile *uri;
1160 gchar *curi, *docuri, *rlink;
1161
1162 if (!fb2->bfwin->current_document || !fb2->bfwin->current_document->uri) {
1163 return;
1164 }
1165
1166 if (fb2->last_popup_on_dir) {
1167 uri = fb2_uri_from_dir_selection(fb2);
1168 } else {
1169 uri = fb2_uri_from_file_selection(fb2, NULL);
1170 }
1171 if (!uri) {
1172 return;
1173 }
1174 curi = g_file_get_uri(uri);
1175 docuri = g_file_get_uri(fb2->bfwin->current_document->uri);
1176 rlink = create_relative_link_to(docuri, curi);
1177
1178 doc_insert_two_strings(DOCUMENT(BFWIN(fb2->bfwin)->current_document), rlink, NULL);
1179 g_free(curi);
1180 g_free(docuri);
1181 g_free(rlink);
1182 }
1183 static void
popup_menu_set_basedir(GtkAction * action,gpointer user_data)1184 popup_menu_set_basedir(GtkAction * action, gpointer user_data)
1185 {
1186 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1187 GtkTreePath *fs_path;
1188 GtkTreeIter iter;
1189
1190 GFile *new_basedir = fb2_uri_from_dir_selection(fb2);
1191 fb2_set_basedir(fb2, new_basedir);
1192 g_object_unref(new_basedir);
1193 if (fb2->bfwin->session->filebrowser_focus_follow && fb2->bfwin->current_document) {
1194 fb2_focus_document(fb2->bfwin, fb2->bfwin->current_document);
1195 } else {
1196 gtk_tree_model_get_iter_first(fb2->dir_filter, &iter);
1197 fs_path = gtk_tree_model_get_path(fb2->dir_filter, &iter);
1198 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(fb2->dir_v), fs_path)) {
1199 gtk_tree_view_expand_row(GTK_TREE_VIEW(fb2->dir_v), fs_path, FALSE);
1200 }
1201 gtk_tree_path_free(fs_path);
1202 }
1203 }
1204
1205 static void
popup_menu_set_document_root(GtkAction * action,gpointer user_data)1206 popup_menu_set_document_root(GtkAction * action, gpointer user_data)
1207 {
1208 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1209 GFile *uri = fb2_uri_from_dir_selection(fb2);
1210
1211 set_documentroot_dialog(fb2->bfwin, uri);
1212 }
1213
1214 static void
popup_menu_show_backup_files(GtkAction * action,gpointer user_data)1215 popup_menu_show_backup_files(GtkAction * action, gpointer user_data)
1216 {
1217 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1218
1219 fb2->filebrowser_show_backup_files = fb2->bfwin->session->filebrowser_show_backup_files =
1220 gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1221 fb2_refilter(fb2);
1222 }
1223
1224 static void
popup_menu_show_hidden_files(GtkAction * action,gpointer user_data)1225 popup_menu_show_hidden_files(GtkAction * action, gpointer user_data)
1226 {
1227 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1228
1229 fb2->filebrowser_show_hidden_files = fb2->bfwin->session->filebrowser_show_hidden_files =
1230 gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1231 fb2_refilter(fb2);
1232 }
1233
1234 static void
popup_menu_show_full_tree(GtkAction * action,gpointer user_data)1235 popup_menu_show_full_tree(GtkAction * action, gpointer user_data)
1236 {
1237 Tfilebrowser2 *fb2 = FILEBROWSER2(user_data);
1238 set_basedir_backend(fb2, NULL);
1239 if (fb2->bfwin->current_document && fb2->bfwin->session->filebrowser_focus_follow)
1240 fb2_focus_document(fb2->bfwin, fb2->bfwin->current_document);
1241 }
1242
1243 static void
popup_menu_view_mode_changed(GtkRadioAction * action,GtkRadioAction * current,gpointer user_data)1244 popup_menu_view_mode_changed(GtkRadioAction * action, GtkRadioAction * current, gpointer user_data)
1245 {
1246 Tbfwin *bfwin = user_data;
1247 Tfilebrowser2 *fb2;
1248 gint view_mode;
1249
1250 if (!bfwin || !bfwin->fb2)
1251 return;
1252
1253 fb2 = FILEBROWSER2(bfwin->fb2);
1254 view_mode = gtk_radio_action_get_current_value(action);
1255
1256 fb2->bfwin->session->filebrowser_viewmode = view_mode;
1257 fb2_set_viewmode_widgets(fb2, view_mode);
1258
1259 /* Focus to current document in order to update fb view when viewmode changes*/
1260 if (fb2->bfwin->session->filebrowser_focus_follow && fb2->bfwin->current_document)
1261 fb2_focus_document(fb2->bfwin, fb2->bfwin->current_document);
1262 }
1263
1264 static void
popup_menu_delete_filter(GtkAction * action,gpointer user_data)1265 popup_menu_delete_filter(GtkAction * action, gpointer user_data)
1266 {
1267 filter_delete(FILEBROWSER2(user_data)->curfilter);
1268 }
1269
1270 static void
popup_menu_edit_filter(GtkAction * action,gpointer user_data)1271 popup_menu_edit_filter(GtkAction * action, gpointer user_data)
1272 {
1273 filefilter_gui(FILEBROWSER2(user_data)->curfilter);
1274 }
1275
1276 static void
popup_menu_new_filter(GtkAction * action,gpointer user_data)1277 popup_menu_new_filter(GtkAction * action, gpointer user_data)
1278 {
1279 filefilter_gui(NULL);
1280 }
1281
1282 static const gchar *filebrowser_menu_ui =
1283 "<ui>"
1284 " <popup action='FileBrowserMenu'>"
1285 " <menuitem action='FB2Open'/>"
1286 " <menuitem action='FB2OpenAdvanced'/>"
1287 " <menuitem action='FB2SearchFiles'/>"
1288 " <menuitem action='FB2Rename'/>"
1289 " <menuitem action='FB2Delete'/>"
1290 " <separator/>"
1291 " <menuitem action='FB2NewFile'/>"
1292 " <menuitem action='FB2NewDirectory'/>"
1293 " <separator/>"
1294 " <menuitem action='FB2InsertLink'/>"
1295 " <separator/>"
1296 " <menuitem action='FB2Refresh'/>"
1297 " <menuitem action='FB2FollowActiveDoc'/>"
1298 " <menuitem action='FB2SetBaseDir'/>"
1299 " <menuitem action='FB2SetDocumentRoot'/>"
1300 " <separator/>"
1301 " <menuitem action='FB2ShowFullTree'/>"
1302 " <menuitem action='FB2ShowBackupFiles'/>"
1303 " <menuitem action='FB2ShowHiddenFiles'/>"
1304 " <separator/>"
1305 " <menu action='FB2ViewModeMenu'>"
1306 " <menuitem action='FB2ViewModeDual'/>"
1307 " <menuitem action='FB2ViewModeFlat'/>"
1308 " <menuitem action='FB2ViewModeTree'/>"
1309 " </menu>"
1310 " <menu action='FB2FilterMenu'>"
1311 " <placeholder name='FB2FilterPlaceholder'/>"
1312 " <separator/>"
1313 " <menuitem action='FB2NewFilter'/>"
1314 " <menuitem action='FB2EditFilter'/>"
1315 " <menuitem action='FB2DeleteFilter'/>"
1316 " </menu>"
1317 " </popup>"
1318 "</ui>";
1319
1320 static const GtkActionEntry filebrowser_actions[] = {
1321 {"FB2FileBrowserMenu", NULL, N_("File Browser menu")},
1322 {"FB2Open", NULL, N_("_Open"), NULL, N_("Open"), G_CALLBACK(popup_menu_open)},
1323 {"FB2OpenAdvanced", NULL, N_("Open _Advanced..."), NULL, N_("Open advanced"),
1324 G_CALLBACK(popup_menu_open_advanced)},
1325 {"FB2SearchFiles", NULL, N_("Search files..."), NULL, N_("Search files"),
1326 G_CALLBACK(popup_menu_search_files)},
1327 {"FB2Rename", NULL, N_("Rena_me"), NULL, N_("Rename"), G_CALLBACK(popup_menu_rename)},
1328 {"FB2Delete", NULL, N_("_Delete"), NULL, N_("Delete"), G_CALLBACK(popup_menu_delete)},
1329 {"FB2NewFile", NULL, N_("New _File"), NULL, N_("New file"), G_CALLBACK(popup_menu_new_file)},
1330 {"FB2NewDirectory", NULL, N_("_New Directory"), NULL, N_("New directory"),
1331 G_CALLBACK(popup_menu_new_directory)},
1332 {"FB2InsertLink", NULL, N_("Insert _link"), NULL, N_("Insert link"), G_CALLBACK(popup_menu_insert_link)},
1333 {"FB2Refresh", NULL, N_("_Refresh"), NULL, N_("Refresh"), G_CALLBACK(popup_menu_refresh)},
1334 {"FB2SetBaseDir", NULL, N_("_Set as base dir"), NULL, N_("Set as base directory"),
1335 G_CALLBACK(popup_menu_set_basedir)},
1336 {"FB2SetDocumentRoot", NULL, N_("Set as document root"), NULL, N_("Set as document root"),
1337 G_CALLBACK(popup_menu_set_document_root)},
1338 {"FB2ShowFullTree", NULL, N_("Show Full _Tree"), NULL, N_("Show full tree"),
1339 G_CALLBACK(popup_menu_show_full_tree)},
1340 {"FB2ViewModeMenu", NULL, N_("View Mode")},
1341 {"FB2FilterMenu", NULL, N_("Filter")},
1342 {"FB2NewFilter", NULL, N_("New Filter"), NULL, N_("New filter"), G_CALLBACK(popup_menu_new_filter)},
1343 {"FB2EditFilter", NULL, N_("Edit Filter"), NULL, N_("Edit filter"), G_CALLBACK(popup_menu_edit_filter)},
1344 {"FB2DeleteFilter", NULL, N_("Delete Filter"), NULL, N_("Delete filter"), G_CALLBACK(popup_menu_delete_filter)}
1345 };
1346
1347 static const GtkToggleActionEntry filebrowser_toggle_actions[] = {
1348 {"FB2FollowActiveDoc", NULL, N_("Follow Active Document"), NULL, N_("Follow active document"),
1349 G_CALLBACK(popup_menu_follow_active_doc), FALSE},
1350 {"FB2ShowBackupFiles", NULL, N_("Show Backup Files"), NULL, N_("Show backup files"),
1351 G_CALLBACK(popup_menu_show_backup_files), FALSE},
1352 {"FB2ShowHiddenFiles", NULL, N_("Show Hidden Files"), NULL, N_("Show hidden files"),
1353 G_CALLBACK(popup_menu_show_hidden_files), FALSE}
1354 };
1355
1356 static const GtkRadioActionEntry filebrowser_radio_actions[] = {
1357 {"FB2ViewModeDual", NULL, N_("Dual"), NULL, NULL, viewmode_dual},
1358 {"FB2ViewModeFlat", NULL, N_("Flat"), NULL, NULL, viewmode_flat},
1359 {"FB2ViewModeTree", NULL, N_("Tree"), NULL, NULL, viewmode_tree}
1360 };
1361
1362 static void
popup_menu_action_group_init(Tbfwin * bfwin)1363 popup_menu_action_group_init(Tbfwin * bfwin)
1364 {
1365 Tfilebrowser2 *fb2 = FILEBROWSER2(bfwin->fb2);
1366 GError *error = NULL;
1367
1368 bfwin->filebrowserGroup = gtk_action_group_new("FileBrowserActions");
1369 gtk_action_group_set_translation_domain(bfwin->filebrowserGroup, GETTEXT_PACKAGE);
1370 gtk_action_group_add_actions(bfwin->filebrowserGroup, filebrowser_actions, G_N_ELEMENTS(filebrowser_actions), fb2);
1371 gtk_action_group_add_toggle_actions(bfwin->filebrowserGroup, filebrowser_toggle_actions,
1372 G_N_ELEMENTS(filebrowser_toggle_actions), fb2);
1373 DEBUG_MSG("popup_menu_action_group_init, set default viewmode to %d\n",fb2->filebrowser_viewmode);
1374 gtk_action_group_add_radio_actions(bfwin->filebrowserGroup, filebrowser_radio_actions,
1375 G_N_ELEMENTS(filebrowser_radio_actions),
1376 fb2->filebrowser_viewmode, G_CALLBACK(popup_menu_view_mode_changed),
1377 bfwin);
1378 gtk_ui_manager_insert_action_group(bfwin->uimanager, bfwin->filebrowserGroup, 1);
1379 g_object_unref(bfwin->filebrowserGroup);
1380
1381 bfwin->filebrowser_merge_id = gtk_ui_manager_add_ui_from_string(bfwin->uimanager, filebrowser_menu_ui, -1, &error);
1382 if (error != NULL) {
1383 g_warning("building file browser popup menu failed: %s", error->message);
1384 g_error_free(error);
1385 }
1386 }
1387
1388 static void
popup_menu_create(Tfilebrowser2 * fb2,gboolean is_directory,gboolean is_file,GdkEventButton * event)1389 popup_menu_create(Tfilebrowser2 * fb2, gboolean is_directory, gboolean is_file, GdkEventButton * event)
1390 {
1391 Tbfwin *bfwin = fb2->bfwin;
1392 GtkWidget *menu;
1393 GSList *group = NULL;
1394 GList *list;
1395 gint value = 0;
1396
1397 menu = gtk_ui_manager_get_widget(bfwin->uimanager, "/FileBrowserMenu");
1398 if (!menu) {
1399 g_warning("showing file browser popup menu failed");
1400 return;
1401 }
1402 fb2->last_popup_on_dir = is_directory;
1403 /* We need to disconnect signals during toggling the action since this emits the signal and filebrowser follows to active documents on first activation of popup */
1404 GtkToggleAction *follow_active_doc_action = (GtkToggleAction *)gtk_ui_manager_get_action(bfwin->uimanager, "/FileBrowserMenu/FB2FollowActiveDoc");
1405 if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(follow_active_doc_action)) != fb2->bfwin->session->filebrowser_focus_follow) {
1406 g_signal_handlers_disconnect_by_func(GTK_TOGGLE_ACTION(follow_active_doc_action), G_CALLBACK(popup_menu_follow_active_doc), fb2);
1407 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(follow_active_doc_action), fb2->bfwin->session->filebrowser_focus_follow);
1408 g_signal_connect(G_OBJECT(follow_active_doc_action), "toggled", G_CALLBACK(popup_menu_follow_active_doc), fb2);
1409 }
1410 bfwin_set_menu_toggle_item_from_path(bfwin->uimanager, "/FileBrowserMenu/FB2ShowBackupFiles",
1411 fb2->filebrowser_show_backup_files);
1412 bfwin_set_menu_toggle_item_from_path(bfwin->uimanager, "/FileBrowserMenu/FB2ShowHiddenFiles",
1413 fb2->filebrowser_show_hidden_files);
1414 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2InsertLink", is_file);
1415 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2Rename", (is_directory || is_file));
1416 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2Delete", (is_directory || is_file));
1417 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2OpenAdvanced", is_directory);
1418 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2SearchFiles", is_directory);
1419 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2SetDocumentRoot", is_directory);
1420 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2SetBaseDir", is_directory);
1421 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2Open", is_file);
1422 bfwin_action_set_sensitive(bfwin->uimanager, "/FileBrowserMenu/FB2ShowFullTree", (fb2->basedir != NULL && fb2->filebrowser_viewmode != viewmode_flat));
1423
1424 gtk_radio_action_set_current_value((GtkRadioAction *)gtk_ui_manager_get_action(bfwin->uimanager, "/FileBrowserMenu/FB2ViewModeMenu/FB2ViewModeDual"),fb2->filebrowser_viewmode);
1425
1426 if (!bfwin->fb2_filters_group) {
1427 bfwin->fb2_filters_group = gtk_action_group_new("FB2FileBrowserFilterActions");
1428 gtk_ui_manager_insert_action_group(bfwin->uimanager, bfwin->fb2_filters_group, 1);
1429 } else {
1430 GList *actions, *list;
1431
1432 gtk_ui_manager_remove_ui(bfwin->uimanager, bfwin->fb2_filters_merge_id);
1433
1434 actions = gtk_action_group_list_actions(bfwin->fb2_filters_group);
1435 for (list = actions; list; list = list->next) {
1436 g_signal_handlers_disconnect_by_func(GTK_ACTION(list->data),
1437 G_CALLBACK(popup_menu_filter_activate), bfwin);
1438 gtk_action_group_remove_action(bfwin->fb2_filters_group, GTK_ACTION(list->data));
1439 }
1440 g_list_free(actions);
1441 }
1442
1443 bfwin->fb2_filters_merge_id = gtk_ui_manager_new_merge_id(bfwin->uimanager);
1444
1445 for (list = g_list_last(main_v->filefilters); list; list = list->prev) {
1446 Tfilter *filter = (Tfilter *) list->data;
1447 GtkRadioAction *action;
1448
1449 action = gtk_radio_action_new(filter->name, filter->name, NULL, NULL, value);
1450 gtk_action_group_add_action(bfwin->fb2_filters_group, GTK_ACTION(action));
1451 gtk_radio_action_set_group(action, group);
1452 group = gtk_radio_action_get_group(action);
1453
1454 g_signal_connect(G_OBJECT(action), "activate", G_CALLBACK(popup_menu_filter_activate), bfwin);
1455
1456 gtk_ui_manager_add_ui(bfwin->uimanager, bfwin->fb2_filters_merge_id,
1457 "/FileBrowserMenu/FB2FilterMenu/FB2FilterPlaceholder", filter->name,
1458 filter->name, GTK_UI_MANAGER_MENUITEM, FALSE);
1459
1460 if (fb2->curfilter == filter)
1461 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
1462 }
1463
1464 gtk_widget_show(menu);
1465 #if GTK_CHECK_VERSION(3,22,0)
1466 gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
1467 #else
1468 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
1469 #endif
1470 }
1471
1472
1473 /*****************************************************************************************************************************/
1474 /* END OF DIALOGS / MENU FUNCTIONS FUNCTIONS, START OF MAIN GUI FUNCTIONS */
1475 /*****************************************************************************************************************************/
1476
1477
1478 /**
1479 * refilter_dirlist:
1480 *
1481 * will set the root of the directory view to 'newroot'
1482 *
1483 * returns TRUE if a sub-root is set, returns FALSE if the filter shows everything from the real-root
1484 *
1485 */
1486 static void
refilter_dirlist(Tfilebrowser2 * fb2,GtkTreePath * newroot)1487 refilter_dirlist(Tfilebrowser2 * fb2, GtkTreePath * newroot)
1488 {
1489 GtkTreePath *useroot = NULL;
1490
1491 if (newroot) {
1492 /* to make it possible to select the root in treeview or dual-view, we move the filter-root one up */
1493 useroot = gtk_tree_path_copy(newroot);
1494 if (fb2->filebrowser_viewmode != viewmode_flat) {
1495 if (!(gtk_tree_path_get_depth(newroot) > 1) || !gtk_tree_path_up(useroot)) {
1496 /* do not set the root as basedir, it is useless */
1497 DEBUG_MSG("refilter_dirlist, there is no parent for this path, so we will set the filter root to NULL\n");
1498 gtk_tree_path_free(useroot);
1499 useroot = NULL;
1500 }
1501 }
1502 }
1503 fb2->dir_filter =
1504 gtk_tree_model_filter_new(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), useroot);
1505 DEBUG_TREEMODELREFS("refilter_dirlist, created new tree model filter at %p for fb2 %p\n",fb2->dir_filter, fb2);
1506 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(fb2->dir_filter),
1507 tree_model_filter_func, fb2, NULL);
1508
1509 gtk_tree_view_set_model(GTK_TREE_VIEW(fb2->dir_v), GTK_TREE_MODEL(fb2->dir_filter));
1510 /* we remove our reference, so the only reference is kept by the treeview, if the treeview is destroyed, the models will be destroyed */
1511 g_object_unref(fb2->dir_filter);
1512 if (useroot)
1513 gtk_tree_path_free(useroot);
1514 }
1515
1516 /**
1517 * refilter_filelist:
1518 *
1519 * will set the root of the file view to 'newroot'
1520 *
1521 */
1522 static void
refilter_filelist(Tfilebrowser2 * fb2,GtkTreePath * newroot)1523 refilter_filelist(Tfilebrowser2 * fb2, GtkTreePath * newroot)
1524 {
1525 DEBUG_MSG("refilter_filelist, started for fb2=%p, file_filter=%p, viewmode=%d, newroot=%p\n", fb2,
1526 fb2->file_filter, fb2->filebrowser_viewmode, newroot);
1527 if (fb2->filebrowser_viewmode == viewmode_dual) {
1528 if (fb2->file_filter) {
1529 GtkTreePath *curpath;
1530 gboolean equal;
1531 g_object_get(fb2->file_filter, "virtual-root", &curpath, NULL);
1532 equal = (curpath == NULL && newroot == NULL) || (curpath != NULL && newroot != NULL
1533 && gtk_tree_path_compare(curpath, newroot) == 0);
1534 gtk_tree_path_free(curpath);
1535 if (equal) {
1536 #ifdef DEBUG
1537 DEBUG_MSG("refilter_filelist, root did not change!\n");
1538 #endif
1539 return;
1540 }
1541 }
1542 fb2->file_filter =
1543 gtk_tree_model_filter_new(GTK_TREE_MODEL
1544 (FB2CONFIG(main_v->fb2config)->ftm), newroot);
1545 DEBUG_TREEMODELREFS("refilter_filelist, created new tree model filter at %p for fb2 %p\n",fb2->file_filter, fb2);
1546 DEBUG_MSG("refilter_filelist, set file list filter func, fb2=%p\n", fb2);
1547 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(fb2->file_filter),
1548 file_list_filter_func, fb2, NULL);
1549 gtk_tree_view_set_model(GTK_TREE_VIEW(fb2->file_v), GTK_TREE_MODEL(fb2->file_filter));
1550 /* we remove our reference, so the only reference is kept by the treeview, if the treeview is destroyed, the models will be destroyed */
1551 g_object_unref(fb2->file_filter);
1552 }
1553 }
1554
1555 static void
set_file_v_root(Tfilebrowser2 * fb2,GFile * dir_uri)1556 set_file_v_root(Tfilebrowser2 *fb2, GFile *dir_uri)
1557 {
1558 GtkTreeIter dir_iter;
1559 GtkTreePath *dir_path;
1560
1561 DEBUG_MSG("set_file_v_root, dir_uri=%p\n",dir_uri);
1562 if (fb2->filebrowser_viewmode != viewmode_dual) {
1563 g_warning("set_file_v_root called but viewmode is not dual ????\n");
1564 return;
1565 }
1566 if (!filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, dir_uri, &dir_iter)) {
1567 return;
1568 }
1569 dir_path = gtk_tree_model_get_path(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), &dir_iter);
1570 refilter_filelist(fb2, dir_path);
1571 gtk_tree_path_free(dir_path);
1572 }
1573
1574 /**
1575 * scroll_to_uri
1576 *
1577 * scrolls to this uri.
1578 *
1579 *
1580 */
1581 static void
scroll_to_iter(Tfilebrowser2 * fb2,GtkTreeIter * file_iter,GtkTreeIter * dir_iter)1582 scroll_to_iter(Tfilebrowser2 *fb2, GtkTreeIter *file_iter, GtkTreeIter *dir_iter)
1583 {
1584 GtkTreePath *fs_path, *filter_path;
1585 GtkTreeSelection *dirselection;
1586 if (fb2->filebrowser_viewmode == viewmode_dual && file_iter) {
1587 fs_path = gtk_tree_model_get_path(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), file_iter);
1588 if (!fs_path) {
1589 DEBUG_MSG("scroll_to_iter, for file_v fs_path==NULL !?!?!?!\n");
1590 return;
1591 }
1592 filter_path = file_v_filter_path_from_treestore_path(fb2, fs_path);
1593 if (filter_path) { /* possible the file was not yet created in the treestore, or outside the filter */
1594 gtk_tree_selection_select_path(gtk_tree_view_get_selection
1595 (GTK_TREE_VIEW(fb2->file_v)), filter_path);
1596 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fb2->file_v), filter_path, 0, TRUE, 0.5, 0.5);
1597 DEBUG_MSG("scroll_to_iter, selected/scrolled on file_v\n");
1598 }
1599 fs_path = gtk_tree_model_get_path(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), dir_iter);
1600 } else {
1601 fs_path = gtk_tree_model_get_path(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), file_iter ? file_iter : dir_iter);
1602 }
1603 if (!fs_path) {
1604 DEBUG_MSG("scroll_to_iter, for dir_v fs_path==NULL !?!?!?!\n");
1605 return;
1606 }
1607
1608 filter_path = dir_v_filter_path_from_treestore_path(fb2, fs_path);
1609 if (!filter_path) {
1610 gtk_tree_path_free(fs_path);
1611 DEBUG_MSG("scroll_to_iter, no filter_path, return\n");
1612 return;
1613 }
1614 DEBUG_MSG("scroll_to_iter, got filter_path=%p\n",filter_path);
1615 expand_without_directory_refresh(fb2, filter_path);
1616 dirselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(fb2->dir_v));
1617 if (fb2->dirselection_changed_id)
1618 g_signal_handler_block(dirselection, fb2->dirselection_changed_id);
1619 gtk_tree_selection_select_path(gtk_tree_view_get_selection
1620 (GTK_TREE_VIEW(fb2->dir_v)), filter_path);
1621 if (fb2->dirselection_changed_id)
1622 g_signal_handler_unblock(dirselection, fb2->dirselection_changed_id);
1623 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fb2->dir_v), filter_path, 0, TRUE, 0.5, 0.5);
1624 gtk_tree_path_free(fs_path);
1625 gtk_tree_path_free(filter_path);
1626 }
1627
1628 static void
switch_to_directory(Tfilebrowser2 * fb2,GFile * dir_uri)1629 switch_to_directory(Tfilebrowser2 *fb2, GFile *dir_uri)
1630 {
1631 GtkTreeIter dir_iter;
1632 DEBUG_MSG("switch_to_directory for dir_uri %p\n",dir_uri);
1633 /* check if the requested dir is within the basedir, or if we have viewmode flat */
1634 if ((fb2->filebrowser_viewmode == viewmode_flat)
1635 || (fb2->basedir && (!gfile_uri_is_parent(fb2->basedir, dir_uri, TRUE) && !g_file_equal(fb2->basedir, dir_uri)))) {
1636 set_basedir_backend(fb2, dir_uri);
1637 }
1638
1639 if (!filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, dir_uri, &dir_iter)) {
1640 filetreemodel_build_dir(FB2CONFIG(main_v->fb2config)->ftm, dir_uri, &dir_iter);
1641 }
1642 if (fb2->filebrowser_viewmode == viewmode_dual) {
1643 set_file_v_root(fb2, dir_uri);
1644 }
1645 scroll_to_iter(fb2, NULL, &dir_iter);
1646 filetreemodel_refresh_iter_async(FB2CONFIG(main_v->fb2config)->ftm, &dir_iter);
1647 /* last entry in recent_dirs is used as basedir of the project, so we can add entries this way only in flat mode, which does not have basedir
1648 * For other modes the right thing to do would be to add uri not to last postion, but one before last, so basedir will not change.
1649 * Saved basedir should be changed only from popup menu
1650 */
1651 if ((fb2->filebrowser_viewmode == viewmode_flat) || !fb2->bfwin->project)
1652 add_uri_to_recent_dirs(fb2, dir_uri);
1653 }
1654
1655 /**
1656 * change_focus_to_uri
1657 *
1658 * builds the directory tree (if the directory does not yet exist)
1659 * in dual view changes the displayed directory for the file_v
1660 * and scrolls to this file (and in dual view also to the dir).
1661 *
1662 * no callbacks will be called for expand signals,
1663 * the directory will be re-read ONLY IF THE FILE DOES NOT YET EXIST IN THE TREESTORE
1664 * the dirmenu will not reflect this change.
1665 */
1666 static void
change_focus_to_uri(Tfilebrowser2 * fb2,GFile * uri)1667 change_focus_to_uri(Tfilebrowser2 *fb2, GFile *uri)
1668 {
1669 GtkTreeIter dir_iter, iter;
1670 GFile *dir_uri;
1671 if (!uri) {
1672 return;
1673 }
1674 DEBUG_MSG("change_focus_to_uri, called for %s\n",g_file_get_path(uri));
1675 dir_uri = g_file_get_parent(uri);
1676 if (!filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, uri, &iter)) {
1677 if (!filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, dir_uri, &dir_iter)) {
1678 DEBUG_MSG("change_focus_to_uri, need to refresh directory for doc_uri=%p and/or dir_uri=%p\n", uri, dir_uri);
1679 filetreemodel_build_dir(FB2CONFIG(main_v->fb2config)->ftm, dir_uri, &dir_iter);
1680 filetreemodel_refresh_iter_async(FB2CONFIG(main_v->fb2config)->ftm, &dir_iter);
1681 }
1682 g_object_unref(dir_uri);
1683 return;
1684 }
1685 if (!filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, dir_uri, &dir_iter)) {
1686 /* weird ?? file exists, but parent directory not ? */
1687 g_assert_not_reached();
1688 }
1689 if (fb2->filebrowser_viewmode == viewmode_dual) {
1690 set_file_v_root(fb2, dir_uri);
1691 }
1692 scroll_to_iter(fb2, &iter, &dir_iter);
1693 fb2_set_dirmenu(fb2, dir_uri, FALSE);
1694 g_object_unref(dir_uri);
1695 }
1696
1697 /*
1698
1699 * for viewmode dual and tree: if the dir is outside the basedir, this will
1700 * change the basedir
1701
1702 */
1703
1704 void
fb2_follow_uri(Tbfwin * bfwin,GFile * uri)1705 fb2_follow_uri(Tbfwin *bfwin, GFile *uri)
1706 {
1707 if (!bfwin || !uri)
1708 return;
1709
1710 Tfilebrowser2 *fb2 = bfwin->fb2;
1711 GFile *dir_uri = g_file_get_parent(uri);
1712 if (!dir_uri)
1713 return;
1714
1715 DEBUG_MSG("fb2_follow_uri, started for uri %p\n",uri);
1716 /* In flat mode we always have to change basedir */
1717 if (fb2->filebrowser_viewmode == viewmode_flat) {
1718 if (!gfile_uri_is_parent(fb2->basedir, uri, FALSE)) {
1719 set_basedir_backend(bfwin->fb2, dir_uri);
1720 }
1721 } else {
1722 if (!gfile_uri_is_parent(fb2->basedir, uri, TRUE)) {
1723 GFile *new_basedir = find_common_path(dir_uri, fb2->basedir);
1724 set_basedir_backend(bfwin->fb2, new_basedir);
1725 if (new_basedir)
1726 g_object_unref(new_basedir);
1727 }
1728 }
1729 /* now build the directory and focus */
1730 change_focus_to_uri(bfwin->fb2, uri);
1731 register_delayed_refresh(dir_uri);
1732 g_object_unref(dir_uri);
1733 }
1734
1735
1736 static gboolean
dirmenu_idle_cleanup_lcb(gpointer callback_data)1737 dirmenu_idle_cleanup_lcb(gpointer callback_data)
1738 {
1739 GtkTreeModel *oldmodel = callback_data;
1740 gboolean cont;
1741 GtkTreeIter iter;
1742 DEBUG_SIG("dirmenu_idle_cleanup_lcb, priority=%d\n",G_PRIORITY_LOW+10);
1743 DEBUG_MSG("dirmenu_idle_cleanup_lcb, cleanup the old model %p\n", oldmodel);
1744 /* now we cleanup the old model and it's contents */
1745
1746 cont = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(oldmodel), &iter);
1747 while (cont) {
1748 GFile *uri = NULL;
1749 gtk_tree_model_get(GTK_TREE_MODEL(oldmodel), &iter, DIR_URI_COLUMN, &uri, -1);
1750 if (uri) {
1751 DEBUG_MSG("dirmenu_idle_cleanup_lcb, unref uri %p\n",uri);
1752 g_object_unref(uri);
1753 }
1754 /* hmm if this last remove results in an empty liststore there is a crash?? */
1755 cont = gtk_list_store_remove(GTK_LIST_STORE(oldmodel), &iter);
1756 }
1757 DEBUG_MSG("dirmenu_idle_cleanup_lcb, unref the old model\n");
1758 g_object_unref(oldmodel);
1759 return FALSE;
1760 }
1761
1762 /**
1763 * fb2_set_dirmenu
1764 *
1765 * sets the dirmenu to another directory
1766 * no callbacks will be called
1767 */
1768 static void
fb2_set_dirmenu(Tfilebrowser2 * fb2,GFile * newcurdir,gboolean force_rebuild)1769 fb2_set_dirmenu(Tfilebrowser2 *fb2, GFile *newcurdir, gboolean force_rebuild)
1770 {
1771 GtkTreeIter iter, setiter;
1772 GHashTable *hasht;
1773 GList *tmplist;
1774 GtkTreeModel *oldmodel = fb2->dirmenu_m;
1775 GFile *tmp;
1776 GVolumeMonitor *gvolmon;
1777 gboolean cont, havesetiter = FALSE;
1778 DEBUG_MSG("fb2_set_dirmenu(fb2=%p, newcurdir uri=%p)\n", fb2, newcurdir);
1779 if (fb2->currentdir) {
1780 if (newcurdir && !force_rebuild && (fb2->currentdir == newcurdir || g_file_equal(fb2->currentdir, newcurdir)))
1781 return;
1782 g_object_unref(fb2->currentdir);
1783 }
1784 fb2->currentdir = g_object_ref(newcurdir);
1785 #ifdef DEBUG
1786 DEBUG_MSG("fb2_set_dirmenu, newcurdir=");
1787 DEBUG_GFILE(newcurdir, TRUE);
1788 #endif
1789
1790 fb2->dirmenu_m = GTK_TREE_MODEL(gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING));
1791
1792 hasht = g_hash_table_new_full(g_file_hash, (GEqualFunc) g_file_equal, NULL, NULL);
1793
1794 /* rebuild the current uri */
1795 tmp = g_object_ref(newcurdir);
1796 do {
1797 gchar *name;
1798 GFile *tmp2;
1799 tmp2 = g_file_get_parent(tmp);
1800 DEBUG_MSG("parent for uri %p is at %p\n", tmp, tmp2);
1801
1802 gtk_list_store_append(GTK_LIST_STORE(fb2->dirmenu_m), &iter);
1803 if (!havesetiter) {
1804 setiter = iter;
1805 havesetiter = TRUE;
1806 }
1807 name = g_file_is_native(tmp) ? g_file_get_path(tmp) : g_file_get_uri(tmp);
1808 DEBUG_MSG("fb2_set_dirmenu, appended %s (uri=%p) to the new model %p\n", name, tmp, fb2->dirmenu_m);
1809 if (tmp2 == NULL) {
1810 gtk_list_store_set(GTK_LIST_STORE(fb2->dirmenu_m), &iter, DIR_NAME_COLUMN, name, DIR_URI_COLUMN, tmp /* don't unref tmp at this point, because there is a reference in the model */
1811 , DIR_ICON_COLUMN, "drive-harddisk", -1);
1812 } else {
1813 gtk_list_store_set(GTK_LIST_STORE(fb2->dirmenu_m), &iter, DIR_NAME_COLUMN, name, DIR_URI_COLUMN, tmp /* don't unref tmp at this point, because there is a reference in the model */
1814 , DIR_ICON_COLUMN, "folder", -1);
1815 }
1816 g_hash_table_insert(hasht, tmp, GINT_TO_POINTER(1));
1817 g_free(name);
1818 tmp = tmp2;
1819 cont = (tmp != NULL);
1820 } while (cont);
1821
1822 tmplist = g_list_first(fb2->bfwin->session->recent_dirs);
1823 while (tmplist) {
1824 GFile *uri;
1825 gchar *name;
1826
1827 uri = g_file_new_for_uri(tmplist->data);
1828 DEBUG_MSG("new uri at %p for session recent directory %s\n", uri, (gchar *)tmplist->data);
1829 if (uri && g_hash_table_lookup(hasht, uri) == NULL) {
1830 name = g_file_is_native(uri) ? g_file_get_path(uri) : g_file_get_uri(uri);
1831 DEBUG_MSG("fb2_set_dirmenu, appending %s (uri=%p) to model %p\n", name, uri, fb2->dirmenu_m);
1832 gtk_list_store_append(GTK_LIST_STORE(fb2->dirmenu_m), &iter);
1833 gtk_list_store_set(GTK_LIST_STORE(fb2->dirmenu_m), &iter, DIR_NAME_COLUMN, name,
1834 DIR_URI_COLUMN, uri, DIR_ICON_COLUMN, "folder", -1);
1835 g_hash_table_insert(hasht, uri, GINT_TO_POINTER(1));
1836 g_free(name);
1837 } else if (uri) {
1838 g_object_unref(uri);
1839 }
1840 tmplist = g_list_next(tmplist);
1841 }
1842
1843 gvolmon = g_volume_monitor_get();
1844 {
1845 GList *mountlist = tmplist = g_list_first(g_volume_monitor_get_mounts(gvolmon));
1846 while (tmplist) {
1847 GMount *gmount = tmplist->data;
1848 gchar *name;
1849 GFile *root;
1850 name = g_mount_get_name(gmount);
1851 root = g_mount_get_root(gmount);
1852 if (g_hash_table_lookup(hasht, root) == NULL) {
1853 gtk_list_store_append(GTK_LIST_STORE(fb2->dirmenu_m), &iter);
1854 gtk_list_store_set(GTK_LIST_STORE(fb2->dirmenu_m), &iter, DIR_NAME_COLUMN, name, DIR_URI_COLUMN, root /* don't unref root at this point, because there is a reference in the model */
1855 , DIR_ICON_COLUMN, "drive-harddisk", -1);
1856 g_hash_table_insert(hasht, root, GINT_TO_POINTER(1));
1857
1858 }
1859 DEBUG_MSG("set_dirmenu, found mount %s\n", name);
1860 g_free(name);
1861 g_object_unref(gmount);
1862 tmplist = tmplist->next;
1863 }
1864 g_list_free(mountlist);
1865 }
1866 g_object_unref(gvolmon);
1867 g_hash_table_destroy(hasht);
1868
1869 DEBUG_MSG("fb2_set_dirmenu, activate the new model\n");
1870 g_signal_handler_block(fb2->dirmenu_v, fb2->dirmenu_changed_signal);
1871 gtk_combo_box_set_model(GTK_COMBO_BOX(fb2->dirmenu_v), GTK_TREE_MODEL(fb2->dirmenu_m));
1872 if (havesetiter)
1873 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(fb2->dirmenu_v), &setiter);
1874 g_signal_handler_unblock(fb2->dirmenu_v, fb2->dirmenu_changed_signal);
1875 DEBUG_MSG("fb2_set_dirmenu, activated the new model!\n");
1876 g_idle_add_full(G_PRIORITY_LOW+10, dirmenu_idle_cleanup_lcb, oldmodel, NULL);
1877
1878 }
1879
1880 /**
1881 * fb2_set_dir_v_root
1882 *
1883 * refilters the directory view to have this uri as visual root
1884 *
1885 *
1886 */
1887 static void
set_dir_v_root(Tfilebrowser2 * fb2,GFile * dir_uri)1888 set_dir_v_root(Tfilebrowser2 *fb2, GFile *dir_uri)
1889 {
1890 GtkTreePath *basepath = NULL;
1891 GtkTreeSelection *dirselection;
1892 DEBUG_MSG("set_dir_v_root for uri %p\n",dir_uri);
1893 /* disconnect the dir_v and file_v for higher performance */
1894
1895 dirselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(fb2->dir_v));
1896 if (fb2->dirselection_changed_id)
1897 g_signal_handler_block(dirselection, fb2->dirselection_changed_id); /* block signal while we are reconfiguting tree model */
1898
1899 gtk_tree_view_set_model(GTK_TREE_VIEW(fb2->dir_v), NULL);
1900 if (fb2->filebrowser_viewmode == viewmode_dual)
1901 gtk_tree_view_set_model(GTK_TREE_VIEW(fb2->file_v), NULL);
1902 fb2->file_filter = NULL; /* this is required, because the refilter_filelist function tries to check if the virtual root did change for the file filter */
1903 DEBUG_MSG("fb2_set_dir_v_root, disconnected current filter/sort models, lfilter=%p\n",
1904 fb2->file_filter);
1905
1906 if (dir_uri) {
1907 /* treepath_for_uri returns a GtkTreePath for the uri, and builds it (using fb2_build_dir()) if needed */
1908 if (fb2->filebrowser_viewmode != viewmode_flat)
1909 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, dir_uri);
1910 basepath = treepath_for_uri(fb2, dir_uri);
1911 DEBUG_MSG("fb2_set_dir_v_root, refilter, basepath=%p\n", basepath);
1912 if (basepath) {
1913 refilter_dirlist(fb2, basepath);
1914 gtk_tree_path_free(basepath);
1915 }
1916 } else {
1917 refilter_dirlist(fb2, NULL);
1918 }
1919 if (fb2->dirselection_changed_id)
1920 g_signal_handler_unblock(dirselection, fb2->dirselection_changed_id);
1921 }
1922
1923
1924 /**
1925 * set_basedir_backend
1926 *
1927 * use NULL to unset the basedir
1928 *
1929 * sets fb2->basedir
1930 * makes it the visual root
1931 *
1932 * does NOT scroll to the active document or register this basedir in the history !!!!!!
1933 */
1934 static void
set_basedir_backend(Tfilebrowser2 * fb2,GFile * dir_uri)1935 set_basedir_backend(Tfilebrowser2 *fb2, GFile *dir_uri)
1936 {
1937
1938 if (dir_uri && fb2->basedir && (fb2->basedir == dir_uri || g_file_equal(fb2->basedir, dir_uri))) {
1939 DEBUG_MSG("set_basedir_backend, basedir did not change, do nothing\n");
1940 return;
1941 }
1942 DEBUG_MSG("set_basedir_backend, new basedir %p\n", dir_uri);
1943 if (fb2->basedir)
1944 g_object_unref(fb2->basedir);
1945 fb2->basedir = dir_uri;
1946 if (dir_uri) {
1947 g_object_ref(fb2->basedir);
1948 }
1949 set_dir_v_root(fb2, dir_uri);
1950 }
1951
1952 /**
1953 * fb2_set_basedir
1954 *
1955 * to be called from the popup menu
1956 *
1957 * use NULL to unset the basedir
1958 *
1959 * sets fb2->basedir
1960 * makes it the visual root
1961 * DOES add it to the history - TODO
1962 * DOES expand and scroll to the current document - TODO
1963 */
1964 static void
fb2_set_basedir(Tfilebrowser2 * fb2,GFile * dir_uri)1965 fb2_set_basedir(Tfilebrowser2 *fb2, GFile *dir_uri)
1966 {
1967 DEBUG_MSG("fb2_set_basedir, dir_uri=%p\n",dir_uri);
1968 set_basedir_backend(fb2, dir_uri);
1969 if (dir_uri)
1970 add_uri_to_recent_dirs(fb2, dir_uri);
1971 }
1972
1973 /* reset basedir to HOME */
1974 gboolean
fb2_check_reset_basedir(GFile * uri)1975 fb2_check_reset_basedir(GFile *uri)
1976 {
1977 GList *tmplist;
1978 GFile *fallback_basedir;
1979 gboolean retval;
1980 Tfilebrowser2 * fb2;
1981 retval=FALSE;
1982 tmplist = g_list_first(main_v->bfwinlist);
1983 while (tmplist) {
1984 fb2 = BFWIN(tmplist->data)->fb2;
1985 if (fb2->basedir && (fb2->basedir == uri || g_file_equal(fb2->basedir, uri))) {
1986 /* Reset basedir to some location that is known to exist */
1987 g_warning("fb2_check_reset_basedir, basedir %s does not exists, setting basedir to $HOME", g_file_get_path(uri));
1988 retval=TRUE;
1989 fallback_basedir = g_file_new_for_path(g_get_home_dir()); /*If basedir is not valid default to home directory */
1990 fb2_set_basedir(fb2, fallback_basedir);
1991 fb2_set_dirmenu(fb2, fallback_basedir, FALSE);
1992 if (fb2->filebrowser_viewmode == viewmode_dual) {
1993 set_file_v_root(fb2, fallback_basedir);
1994 }
1995 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, fallback_basedir);
1996 /* now expand it */
1997 GtkTreePath *fs_path, *filter_path;
1998 fs_path = treepath_for_uri(fb2, fallback_basedir);
1999 if (fs_path) {
2000 filter_path = dir_v_filter_path_from_treestore_path(fb2, fs_path);
2001 if (filter_path) {
2002 expand_without_directory_refresh(fb2, filter_path);
2003 gtk_tree_path_free(filter_path);
2004 }
2005 gtk_tree_path_free(fs_path);
2006 }
2007 g_object_unref(fallback_basedir);
2008 }
2009 tmplist = g_list_next(tmplist);
2010 }
2011 g_list_free(tmplist);
2012 return retval;
2013 }
2014
2015 static void
dir_v_row_expanded_lcb(GtkTreeView * tree,GtkTreeIter * sort_iter,GtkTreePath * sort_path,Tbfwin * bfwin)2016 dir_v_row_expanded_lcb(GtkTreeView * tree, GtkTreeIter * sort_iter,
2017 GtkTreePath * sort_path, Tbfwin *bfwin)
2018 {
2019 UriRecord *record=NULL;
2020 Tfilebrowser2 * fb2;
2021 if (!bfwin || !bfwin->fb2)
2022 return;
2023 fb2 = bfwin->fb2;
2024 DEBUG_MSG("dir_v_row_expanded_lcb, called for fb2=%p with sort_path=\n", fb2);
2025 /* refresh the directory that is being expanded */
2026 gtk_tree_model_get(fb2->dir_filter, sort_iter, filetreemodel_COL_RECORD, &record, -1);
2027 if (record) {
2028 DEBUG_MSG("dir_v_row_expanded_lcb, calling fb2_refresh_dir()\n");
2029 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, record->uri);
2030 }
2031 }
2032
2033 static gboolean
dir_v_button_press_lcb(GtkWidget * widget,GdkEventButton * event,Tfilebrowser2 * fb2)2034 dir_v_button_press_lcb(GtkWidget * widget, GdkEventButton * event, Tfilebrowser2 * fb2)
2035 {
2036 DEBUG_MSG("dir_v_button_press_lcb, called for fb2=%p and event->button=%d\n", fb2, event->button);
2037 if (event->button == 3) {
2038 GtkTreePath *path;
2039
2040 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(fb2->dir_v), event->x, event->y, &path, NULL, NULL, NULL);
2041 if (path) {
2042 gboolean is_dir = fb2_isdir_from_dir_sort_path(fb2, path);
2043 DEBUG_MSG("context menu: %s selected\n",is_dir?"dir":"file");
2044 popup_menu_create(fb2, is_dir, !is_dir, event);
2045 gtk_tree_path_free(path);
2046 } else {
2047 DEBUG_MSG("dir_v_button_press_lcb, no path for position\n");
2048 DEBUG_MSG("context menu: nothing selected\n");
2049 popup_menu_create(fb2, FALSE, FALSE, event);
2050 }
2051 }/* else if (!(fb2->filebrowser_viewmode == viewmode_dual) && event->button == 1
2052 && event->type == GDK_2BUTTON_PRESS) {
2053 GtkTreePath *path;
2054 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(fb2->dir_v), event->x, event->y, &path, NULL, NULL, NULL);
2055 if (path && !fb2_isdir_from_dir_sort_path(fb2, path)) {
2056 GFile *uri;
2057 gchar *mime = NULL;
2058 uri = fb2_uri_from_dir_sort_path(fb2, path, &mime);
2059 if (uri) {
2060 #ifdef DEBUG
2061 gchar *basename = g_file_get_basename(uri);
2062 DEBUG_MSG("file_v_button_press_lcb, doucleclick on %s\n", basename);
2063 g_free(basename);
2064 #endif
2065 g_print("calling file_handle for doubleclick on dir_v\n");
2066 file_handle(uri, fb2->bfwin, mime, FALSE, TRUE);
2067 }
2068 g_free(mime);
2069 }
2070 }*/
2071 return FALSE; /* pass the event on */
2072 }
2073
2074 static gboolean
file_v_button_press_lcb(GtkWidget * widget,GdkEventButton * event,Tfilebrowser2 * fb2)2075 file_v_button_press_lcb(GtkWidget * widget, GdkEventButton * event, Tfilebrowser2 * fb2)
2076 {
2077 DEBUG_MSG("file_v_button_press_lcb, called for fb2=%p and event->button=%d\n", fb2, event->button);
2078 if (event->button == 3) {
2079 GtkTreePath *path=NULL;
2080 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(fb2->file_v), event->x, event->y, &path, NULL,
2081 NULL, NULL) && path) {
2082 DEBUG_MSG("context menu: file selected\n");
2083 popup_menu_create(fb2, FALSE, TRUE, event);
2084 gtk_tree_path_free(path);
2085 } else {
2086 DEBUG_MSG("context menu: nothing selected\n");
2087 DEBUG_MSG("no path for position\n");
2088 popup_menu_create(fb2, FALSE, FALSE, event);
2089 }
2090 } /*else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
2091 GtkTreePath *sort_path=NULL;
2092 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(fb2->file_v), event->x, event->y, &sort_path,
2093 NULL, NULL, NULL) && sort_path) {
2094 GFile *uri;
2095 gchar *mime = NULL;
2096 uri = fb2_uri_from_file_sort_path(fb2, sort_path, &mime);
2097 if (uri) {
2098 g_print("call file_handle for doubleclick on file_v\n");
2099 file_handle(uri, fb2->bfwin, mime, FALSE, TRUE);
2100 }
2101 #ifdef DEBUG
2102 else {
2103 DEBUG_MSG("file_v_button_press_lcb, WARNING, NO URI FOUND, cannot activate NULL\n");
2104 }
2105 #endif
2106 gtk_tree_path_free(sort_path);
2107 }
2108 }*/
2109 return FALSE; /* pass the event on */
2110 }
2111
2112 static void
add_parent_uri_to_recent_dirs(Tfilebrowser2 * fb2,GFile * uri)2113 add_parent_uri_to_recent_dirs(Tfilebrowser2 * fb2, GFile *uri)
2114 {
2115 GFile *dir_uri = g_file_get_parent(uri);
2116 add_uri_to_recent_dirs(fb2, dir_uri);
2117 g_object_unref(dir_uri);
2118 }
2119
2120 static void
dir_v_row_activated_lcb(GtkTreeView * tree,GtkTreePath * path,GtkTreeViewColumn * column,Tfilebrowser2 * fb2)2121 dir_v_row_activated_lcb(GtkTreeView * tree, GtkTreePath * path,
2122 GtkTreeViewColumn * column, Tfilebrowser2 * fb2)
2123 {
2124 GtkTreeIter iter;
2125 UriRecord *record=NULL;
2126
2127 if (gtk_tree_model_get_iter(fb2->dir_filter, &iter, path)) {
2128 gtk_tree_model_get(fb2->dir_filter, &iter, filetreemodel_COL_RECORD, &record, -1);
2129 }
2130
2131 if (!record)
2132 return;
2133
2134 if (!record->isdir) {
2135 DEBUG_MSG("calling file_handle for row_activated\n");
2136 file_handle(record->uri, fb2->bfwin, record->fast_content_type, FALSE, TRUE);
2137 add_parent_uri_to_recent_dirs(fb2, record->uri);
2138 } else { /* a directory */
2139 add_uri_to_recent_dirs(fb2, record->uri);
2140 if (fb2->filebrowser_viewmode == viewmode_flat) {
2141 set_basedir_backend(fb2, record->uri);
2142 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, record->uri);
2143 fb2_set_dirmenu(fb2, record->uri, FALSE);
2144 } else {
2145 if (gtk_tree_view_row_expanded(tree, path)) {
2146 gtk_tree_view_collapse_row(tree, path);
2147 } else {
2148 expand_without_directory_refresh(fb2, path);
2149 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, record->uri);
2150 }
2151 }
2152 }
2153 }
2154
2155 static void
file_v_row_activated_lcb(GtkTreeView * tree,GtkTreePath * path,GtkTreeViewColumn * column,Tfilebrowser2 * fb2)2156 file_v_row_activated_lcb(GtkTreeView * tree, GtkTreePath * path,
2157 GtkTreeViewColumn * column, Tfilebrowser2 * fb2)
2158 {
2159 GtkTreeIter iter;
2160 if (gtk_tree_model_get_iter(fb2->file_filter, &iter, path)) {
2161 UriRecord *record;
2162 gtk_tree_model_get(fb2->file_filter, &iter, filetreemodel_COL_RECORD, &record, -1);
2163 DEBUG_MSG("calling file_handle for file_v row activat\n");
2164 file_handle(record->uri, fb2->bfwin, record->fast_content_type, FALSE, TRUE);
2165 add_parent_uri_to_recent_dirs(fb2, record->uri);
2166 }
2167 }
2168
2169 static void
dir_v_selection_changed_lcb(GtkTreeSelection * treeselection,Tbfwin * bfwin)2170 dir_v_selection_changed_lcb(GtkTreeSelection * treeselection, Tbfwin *bfwin)
2171 {
2172 GtkTreeModel *sort_model = NULL;
2173 GtkTreeIter sort_iter;
2174 Tfilebrowser2 *fb2;
2175 if (!bfwin || !bfwin->fb2)
2176 return;
2177 fb2 = bfwin->fb2;
2178 /* Get the current selected row and the model. */
2179 DEBUG_MSG("dir_v_selection_changed_lcb, treeselection=%p, fb2=%p\n", treeselection, fb2);
2180 if (treeselection && gtk_tree_selection_get_selected(treeselection, &sort_model, &sort_iter)) {
2181 UriRecord *record=NULL;
2182 gtk_tree_model_get(sort_model, &sort_iter, filetreemodel_COL_RECORD, &record, -1);
2183 if (record && record->isdir) {
2184 fb2_set_dirmenu(fb2, record->uri, FALSE);
2185 if (fb2->filebrowser_viewmode == viewmode_dual)
2186 set_file_v_root(fb2, record->uri);
2187 register_delayed_refresh(record->uri);
2188 }
2189 } else {
2190 DEBUG_MSG("dir_v_selection_changed_lcb, could not get any selection, returning..\n");
2191 }
2192 }
2193
2194 static void
dirmenu_changed_lcb(GtkComboBox * widget,gpointer data)2195 dirmenu_changed_lcb(GtkComboBox * widget, gpointer data)
2196 {
2197 Tbfwin *bfwin = data;
2198 Tfilebrowser2 *fb2;
2199 GtkTreeIter iter;
2200
2201 if (!bfwin || !bfwin->fb2)
2202 return;
2203 fb2 = bfwin->fb2;
2204 DEBUG_MSG("dirmenu_changed_lcb, started for fb2 %p\n", fb2);
2205 if (gtk_combo_box_get_active_iter(widget, &iter)) {
2206 GFile *uri;
2207 DEBUG_MSG("dirmenu_changed_lcb. we have an active iter\n");
2208 gtk_tree_model_get(GTK_TREE_MODEL(fb2->dirmenu_m), &iter, DIR_URI_COLUMN, &uri, -1);
2209 if (!uri) {
2210 DEBUG_MSG("dirmenu_changed_lcb. WEIRD, we do not have uri for this dirmenu entry\n");
2211 return;
2212 }
2213 /* TODO check the uri validity here before trying to display it. If uri is not valid, display error message and remove it from recent_dirs
2214 * Invalid uri will break many things downstream
2215 *
2216 * TODO: having a synchronous filesystem call here might slow down the GUI when working on a remote server over a
2217 * high latency connection. In some way or the other we should get rid of this.
2218 */
2219 if (g_file_query_exists(uri,NULL)) {
2220 g_object_ref(uri);
2221 switch_to_directory(fb2, uri);
2222 g_object_unref(uri);
2223 } else {
2224 remove_uri_from_recent_dirs(fb2, uri);
2225 fb2_set_dirmenu(fb2, fb2->currentdir, TRUE);
2226 g_object_unref(uri);
2227 }
2228 }
2229 }
2230
2231 enum {
2232 TARGET_URI_LIST,
2233 TARGET_STRING
2234 };
2235
2236 static void
fb2_file_v_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint time,Tfilebrowser2 * fb2)2237 fb2_file_v_drag_data_received(GtkWidget * widget, GdkDragContext * context, gint x,
2238 gint y, GtkSelectionData * data, guint info, guint time, Tfilebrowser2 * fb2)
2239 {
2240
2241 gchar *stringdata;
2242 const guchar *seldata;
2243 gint format;
2244 gint length;
2245 GFile *destdir = fb2->currentdir;
2246
2247 g_object_ref(destdir);
2248
2249 g_signal_stop_emission_by_name(widget, "drag_data_received");
2250
2251 format = gtk_selection_data_get_format(data);
2252 length = gtk_selection_data_get_length(data);
2253 seldata = gtk_selection_data_get_data(data);
2254
2255 if ((length == 0) || (format != 8)
2256 || ((info != TARGET_STRING) && (info != TARGET_URI_LIST))) {
2257 gtk_drag_finish(context, FALSE, TRUE, time);
2258 return;
2259 }
2260 stringdata = g_strndup((gchar *) seldata, length);
2261 DEBUG_MSG("fb2_file_v_drag_data_received, stringdata='%s', len=%d\n", stringdata, length);
2262 if (destdir) {
2263 if (strchr(stringdata, '\n') == NULL) { /* no newlines, probably a single file */
2264 GSList *list = NULL;
2265 GFile *uri;
2266 uri = g_file_new_for_commandline_arg(stringdata);
2267 list = g_slist_append(list, uri);
2268 copy_uris_async(fb2->bfwin, destdir, list);
2269 g_slist_free(list);
2270 g_object_unref(uri);
2271 } else { /* there are newlines, probably this is a list of uri's */
2272 copy_files_async(fb2->bfwin, destdir, stringdata);
2273 }
2274 g_object_unref(destdir);
2275 gtk_drag_finish(context, TRUE, TRUE, time);
2276 } else {
2277 gtk_drag_finish(context, FALSE, TRUE, time);
2278 }
2279 g_free(stringdata);
2280 }
2281
2282 static void
fb2_dir_v_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint time,Tfilebrowser2 * fb2)2283 fb2_dir_v_drag_data_received(GtkWidget * widget, GdkDragContext * context, gint x,
2284 gint y, GtkSelectionData * data, guint info, guint time, Tfilebrowser2 * fb2)
2285 {
2286 gchar *stringdata;
2287 const guchar *seldata;
2288 gint format;
2289 gint length;
2290 GFile *destdir = NULL;
2291 GtkTreePath *path;
2292 /* if we don't do this, we get this text: Gtk-WARNING **: You must override the default
2293 'drag_data_received' handler on GtkTreeView when using models that don't support the
2294 GtkTreeDragDest interface and enabling drag-and-drop. The simplest way to do this is to
2295 connect to 'drag_data_received' and call g_signal_stop_emission_by_name() in your signal
2296 handler to prevent the default handler from running. Look at the source code for the
2297 default handler in gtktreeview.c to get an idea what your handler should do. */
2298 g_signal_stop_emission_by_name(widget, "drag_data_received");
2299
2300 format = gtk_selection_data_get_format(data);
2301 length = gtk_selection_data_get_length(data);
2302 seldata = gtk_selection_data_get_data(data);
2303
2304 if ((length == 0) || (format != 8)
2305 || ((info != TARGET_STRING) && (info != TARGET_URI_LIST))) {
2306 gtk_drag_finish(context, FALSE, TRUE, time);
2307 return;
2308 }
2309 stringdata = g_strndup((gchar *) seldata, length);
2310 DEBUG_MSG("fb2_dir_v_drag_data_received, stringdata='%s', len=%d\n", stringdata, length);
2311 /* first find the destination directory */
2312 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(fb2->dir_v), x, y, &path, NULL, NULL, NULL);
2313 if (path) {
2314 GtkTreeIter iter;
2315 if (gtk_tree_model_get_iter(fb2->dir_filter, &iter, path)) {
2316 UriRecord *record;
2317 gtk_tree_model_get(fb2->dir_filter, &iter, filetreemodel_COL_RECORD, &record, -1);
2318 if (record && record->fast_content_type && strncmp(record->fast_content_type, "x-directory", 11) == 0) {
2319 destdir = record->uri;
2320 g_object_ref(destdir);
2321 } else {
2322 destdir = g_file_get_parent(record->uri);
2323 }
2324 }
2325 }
2326 if (destdir) {
2327 if (strchr(stringdata, '\n') == NULL) { /* no newlines, probably a single file */
2328 GSList *list = NULL;
2329 GFile *uri;
2330 uri = g_file_new_for_commandline_arg(stringdata);
2331 list = g_slist_append(list, uri);
2332 copy_uris_async(fb2->bfwin, destdir, list);
2333 g_slist_free(list);
2334 g_object_unref(uri);
2335 } else { /* there are newlines, probably this is a list of uri's */
2336 copy_files_async(fb2->bfwin, destdir, stringdata);
2337 }
2338 g_object_unref(destdir);
2339 gtk_drag_finish(context, TRUE, TRUE, time);
2340 } else {
2341 gtk_drag_finish(context, FALSE, TRUE, time);
2342 }
2343 g_free(stringdata);
2344 }
2345
2346 static gboolean
file_search_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer search_data)2347 file_search_func(GtkTreeModel *model, gint column,const gchar *key, GtkTreeIter *iter,gpointer search_data)
2348 {
2349 gint len;
2350 gchar *tmp=NULL;
2351 gboolean retval = TRUE;
2352 if (!key || key[0]=='\0')
2353 return TRUE;
2354 gtk_tree_model_get(model, iter, filetreemodel_COL_NAME, &tmp, -1);
2355 len = strlen(key);
2356 if (strncasecmp(key, tmp, len)==0){
2357 retval = FALSE;
2358 }
2359 g_free(tmp);
2360 return retval;
2361 }
2362
2363 static void
fb2_two_pane_notify_position_lcb(GObject * object,GParamSpec * pspec,gpointer data)2364 fb2_two_pane_notify_position_lcb(GObject * object, GParamSpec * pspec, gpointer data)
2365 {
2366 gint position;
2367 g_object_get(object, pspec->name, &position, NULL);
2368 if (main_v->props.restore_dimensions) {
2369 DEBUG_MSG("fb2_two_pane_notify_position_lcb, save position %d (previous value was %d)\n",position, main_v->globses.two_pane_filebrowser_height);
2370 main_v->globses.two_pane_filebrowser_height = position;
2371 }
2372 }
2373
2374 static void
dir_changed_lcb(FileTreemodel * ftm,GFile * dir_uri,gpointer user_data)2375 dir_changed_lcb(FileTreemodel * ftm, GFile *dir_uri, gpointer user_data)
2376 {
2377 Tfilebrowser2 *fb2 = user_data;
2378 if (fb2->bfwin->session->filebrowser_focus_follow && fb2->bfwin->current_document && fb2->bfwin->current_document->uri) {
2379 if (gfile_uri_is_parent(dir_uri, fb2->bfwin->current_document->uri, FALSE)) {
2380 change_focus_to_uri(fb2, fb2->bfwin->current_document->uri);
2381 }
2382 }
2383 }
2384
2385 static gboolean
fb2_tooltip_lcb(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltipwidget,gpointer user_data)2386 fb2_tooltip_lcb(GtkWidget * widget, gint x, gint y, gboolean keyboard_tip, GtkTooltip * tooltipwidget,
2387 gpointer user_data)
2388 {
2389 GtkTreeView *tview = (GtkTreeView *)widget;
2390 Tfilebrowser2 *fb2 = user_data;
2391 GtkTreePath *path;
2392 gboolean retval = FALSE;
2393 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tview), x, y, &path, NULL, NULL, NULL)) {
2394 GtkTreeIter iter;
2395 if (gtk_tree_model_get_iter(gtk_tree_view_get_model(tview), &iter, path)) {
2396 UriRecord *record = NULL;
2397 gtk_tree_model_get(gtk_tree_view_get_model(tview), &iter, filetreemodel_COL_RECORD, &record, -1);
2398 if (record) {
2399 gchar *curi = g_file_get_uri(record->uri);
2400 gchar *localname=NULL;
2401 gchar *text;
2402 if (g_file_is_native(record->uri)) {
2403 localname = g_file_get_path(record->uri);
2404 }
2405 if (fb2->bfwin->session->webroot && fb2->bfwin->session->documentroot && g_str_has_prefix(curi, fb2->bfwin->session->documentroot)) {
2406 text = g_strdup_printf(_("<b>%s</b>\nwhich is the equivalent of\n<b>%s%s</b>"), localname?localname:curi, fb2->bfwin->session->webroot, curi+strlen(fb2->bfwin->session->documentroot));
2407 } else {
2408 text = g_strdup_printf("<b>%s</b>",localname?localname:curi);
2409 }
2410 gtk_tooltip_set_markup(tooltipwidget, text);
2411 g_free(curi);
2412 g_free(text);
2413 g_free(localname);
2414 retval = TRUE;
2415 }
2416 }
2417 gtk_tree_path_free(path);
2418 }
2419 return retval;
2420 }
2421
2422 static void
fb2_set_viewmode_widgets(Tfilebrowser2 * fb2,gint viewmode)2423 fb2_set_viewmode_widgets(Tfilebrowser2 * fb2, gint viewmode)
2424 {
2425 GtkTreeViewColumn *column;
2426 GtkTreeSelection *dirselection;
2427 GtkWidget *scrolwin;
2428 GtkCellRenderer *renderer;
2429 GtkTreePath *basepath = NULL;
2430
2431 const GtkTargetEntry drag_dest_types[] = {
2432 {"text/uri-list", 0, TARGET_URI_LIST},
2433 {"STRING", 0, TARGET_STRING},
2434 };
2435
2436
2437 if (fb2->filebrowser_viewmode == viewmode) {
2438 /* the current viewmode is the same as the requested viewmode
2439 we can return if all the widgets exist */
2440 if (fb2->dir_filter != NULL) {
2441 DEBUG_MSG("fb2_set_viewmode_widgets, keep existing widgets and return\n");
2442 return;
2443 }
2444 } else {
2445 DEBUG_MSG("fb2_set_viewmode_widgets, destroying old widgets (if any)\n");
2446 if (fb2->vpaned) {
2447 DEBUG_MSG("remove fb2->vpaned, ");
2448 gtk_container_remove(GTK_CONTAINER(fb2->vbox), fb2->vpaned);
2449 DEBUG_MSG("fb2->vpaned, ");
2450 fb2->vpaned = NULL;
2451 } else if (fb2->dirscrolwin) {
2452 DEBUG_MSG("remove fb2->dirscrolwin, ");
2453 gtk_container_remove(GTK_CONTAINER(fb2->vbox), fb2->dirscrolwin);
2454 fb2->dirscrolwin = NULL;
2455 }
2456 fb2->dir_v = fb2->file_v = NULL;
2457 fb2->dir_filter = fb2->file_filter = NULL;
2458 DEBUG_MSG("fb2_set_viewmode_widgets, reset dir_filter and file_filter\n");
2459 }
2460 fb2->filebrowser_viewmode = viewmode;
2461
2462 DEBUG_MSG("fb2_set_viewmode_widgets, building new GUI\n");
2463 if (fb2->basedir) {
2464 basepath = treepath_for_uri(fb2, fb2->basedir);
2465 if (basepath && fb2->filebrowser_viewmode != viewmode_flat) {
2466 gtk_tree_path_up(basepath);
2467 }
2468 }
2469
2470 fb2->dir_filter =
2471 gtk_tree_model_filter_new(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), basepath);
2472 DEBUG_TREEMODELREFS("fb2_set_viewmode_widgets, created new tree model filter at %p for fb2 %p\n",fb2->dir_filter, fb2);
2473 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(fb2->dir_filter),
2474 tree_model_filter_func, fb2, NULL);
2475
2476 fb2->dir_v = gtk_tree_view_new_with_model(fb2->dir_filter);
2477 /* we remove our reference, so the only reference is kept by the treeview, if the treeview is destroyed, the models will be destroyed */
2478 g_object_unref(G_OBJECT(fb2->dir_filter));
2479 DEBUG_TREEMODELREFS("fb2_set_viewmode_widgets, unreffed tree model filter at %p for fb2 %p, ref is added to view %p\n",fb2->dir_filter, fb2, fb2->dir_v);
2480
2481 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fb2->dir_v), FALSE);
2482 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW(fb2->dir_v),file_search_func,fb2,NULL);
2483 if (fb2->filebrowser_viewmode != viewmode_flat) {
2484 dirselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(fb2->dir_v));
2485 DEBUG_MSG("fb2_set_viewmode_widgets, treeselection=%p, fb2=%p, dir_filter=%p\n",
2486 dirselection, fb2, fb2->dir_filter);
2487 fb2->dirselection_changed_id = g_signal_connect(G_OBJECT(dirselection), "changed", G_CALLBACK(dir_v_selection_changed_lcb), fb2->bfwin);
2488 } else {
2489 fb2->dirselection_changed_id = 0;
2490 }
2491
2492 renderer = gtk_cell_renderer_pixbuf_new();
2493 column = gtk_tree_view_column_new();
2494 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2495 gtk_tree_view_column_set_attributes(column, renderer,
2496 "icon-name", filetreemodel_COL_ICON_NAME,NULL);
2497 renderer = gtk_cell_renderer_text_new();
2498 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2499 gtk_tree_view_column_set_attributes(column, renderer, "text", filetreemodel_COL_NAME, "weight", filetreemodel_COL_WEIGHT, NULL);
2500 g_object_set(G_OBJECT(renderer), "editable", FALSE, "weight-set", TRUE, NULL); /* Not editable. */
2501 gtk_tree_view_append_column(GTK_TREE_VIEW(fb2->dir_v), column);
2502 fb2->dirscrolwin = gtk_scrolled_window_new(NULL, NULL);
2503 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(fb2->dirscrolwin), GTK_POLICY_AUTOMATIC,
2504 GTK_POLICY_AUTOMATIC);
2505 gtk_container_add(GTK_CONTAINER(fb2->dirscrolwin), fb2->dir_v);
2506
2507 gtk_drag_dest_set(fb2->dir_v, (GTK_DEST_DEFAULT_ALL), drag_dest_types, 2,
2508 (GDK_ACTION_DEFAULT | GDK_ACTION_COPY));
2509 g_signal_connect(G_OBJECT(fb2->dir_v), "drag_data_received",
2510 G_CALLBACK(fb2_dir_v_drag_data_received), fb2);
2511
2512 if (fb2->filebrowser_viewmode != viewmode_dual) {
2513 gtk_box_pack_start(GTK_BOX(fb2->vbox), fb2->dirscrolwin, TRUE, TRUE, 0);
2514 } else {
2515 #if GTK_CHECK_VERSION(3,0,0)
2516 fb2->vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
2517 #else
2518 fb2->vpaned = gtk_vpaned_new();
2519 #endif
2520
2521 gtk_paned_pack1(GTK_PANED(fb2->vpaned), fb2->dirscrolwin, FALSE, FALSE);
2522
2523 fb2->file_filter =
2524 gtk_tree_model_filter_new(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), NULL);
2525 DEBUG_TREEMODELREFS("fb2_set_viewmode_widgets, created new tree model filter at %p for fb2 %p\n",fb2->file_filter, fb2);
2526 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(fb2->file_filter),
2527 file_list_filter_func, fb2, NULL);
2528
2529 DEBUG_MSG("fb2_init, file_filter=%p\n", fb2->file_filter);
2530 fb2->file_v = gtk_tree_view_new_with_model(fb2->file_filter);
2531 /* we remove our reference, so the only reference is kept by the treeview, if the treeview is destroyed, the models will be destroyed */
2532 g_object_unref(G_OBJECT(fb2->file_filter));
2533 DEBUG_TREEMODELREFS("fb2_set_viewmode_widgets, unreffed tree model filter at %p for fb2 %p, ref should have been added by view %p\n",fb2->file_filter, fb2, fb2->file_v);
2534
2535 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fb2->file_v), FALSE);
2536 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW(fb2->file_v),file_search_func,fb2,NULL);
2537 renderer = gtk_cell_renderer_pixbuf_new();
2538 column = gtk_tree_view_column_new();
2539 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2540 gtk_tree_view_column_set_attributes(column, renderer,
2541 "icon-name", filetreemodel_COL_ICON_NAME,NULL);
2542 renderer = gtk_cell_renderer_text_new();
2543 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2544 gtk_tree_view_column_set_attributes(column, renderer, "text", filetreemodel_COL_NAME, "weight", filetreemodel_COL_WEIGHT, NULL);
2545 g_object_set(G_OBJECT(renderer), "editable", FALSE, "weight-set", TRUE, NULL); /* Not editable. */
2546 gtk_tree_view_append_column(GTK_TREE_VIEW(fb2->file_v), column);
2547 scrolwin = gtk_scrolled_window_new(NULL, NULL);
2548 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolwin), GTK_POLICY_AUTOMATIC,
2549 GTK_POLICY_AUTOMATIC);
2550 gtk_container_add(GTK_CONTAINER(scrolwin), fb2->file_v);
2551 gtk_paned_pack2(GTK_PANED(fb2->vpaned), scrolwin, FALSE,FALSE);
2552
2553 gtk_box_pack_start(GTK_BOX(fb2->vbox), fb2->vpaned, TRUE, TRUE, 0);
2554 g_signal_connect(G_OBJECT(fb2->file_v), "button_press_event",
2555 G_CALLBACK(file_v_button_press_lcb), fb2);
2556 g_signal_connect(G_OBJECT(fb2->file_v), "row-activated", G_CALLBACK(file_v_row_activated_lcb), fb2);
2557 gtk_drag_dest_set(fb2->file_v, (GTK_DEST_DEFAULT_ALL), drag_dest_types, 2,
2558 (GDK_ACTION_DEFAULT | GDK_ACTION_COPY));
2559 g_signal_connect(G_OBJECT(fb2->file_v), "drag_data_received",
2560 G_CALLBACK(fb2_file_v_drag_data_received), fb2);
2561 g_object_set(fb2->file_v, "has-tooltip", TRUE, NULL);
2562 g_signal_connect(fb2->file_v, "query-tooltip", G_CALLBACK(fb2_tooltip_lcb), fb2);
2563 DEBUG_MSG("gtk_paned_set_position(%d)\n",main_v->globses.two_pane_filebrowser_height);
2564 gtk_paned_set_position(GTK_PANED(fb2->vpaned), main_v->globses.two_pane_filebrowser_height);
2565 g_signal_connect(G_OBJECT(fb2->vpaned), "notify::position",
2566 G_CALLBACK(fb2_two_pane_notify_position_lcb), NULL);
2567
2568 }
2569
2570 g_signal_connect(G_OBJECT(fb2->dir_v), "row-activated", G_CALLBACK(dir_v_row_activated_lcb), fb2);
2571 g_signal_connect(G_OBJECT(fb2->dir_v), "button_press_event", G_CALLBACK(dir_v_button_press_lcb), fb2);
2572 fb2->expand_signal =
2573 g_signal_connect(G_OBJECT(fb2->dir_v), "row-expanded", G_CALLBACK(dir_v_row_expanded_lcb), fb2->bfwin);
2574 g_signal_connect(fb2->dir_v, "query-tooltip", G_CALLBACK(fb2_tooltip_lcb), fb2);
2575 g_object_set(fb2->dir_v, "has-tooltip", TRUE, NULL);
2576 /*gtk_container_resize_children(GTK_CONTAINER(fb2->vbox)); */
2577 gtk_widget_show_all(fb2->vbox);
2578 }
2579
2580 void
fb2_update_settings_from_session(Tbfwin * bfwin,Tdocument * active_doc)2581 fb2_update_settings_from_session(Tbfwin * bfwin, Tdocument *active_doc)
2582 {
2583 if (!bfwin->fb2) {
2584 DEBUG_MSG("fb2_update_settings_from_session, no fb2, nothing to update... returning\n");
2585 return;
2586 }
2587 gboolean need_refilter = FALSE;
2588 Tfilebrowser2 *fb2 = bfwin->fb2;
2589
2590 DEBUG_MSG("fb2_update_settings_from_session, started, bfwin=%p, fb2=%p, viewmode=%d, active_doc=%p\n",
2591 bfwin, fb2, fb2->filebrowser_viewmode, active_doc);
2592
2593 if (!bfwin->filebrowserGroup)
2594 popup_menu_action_group_init(bfwin);
2595
2596 fb2_set_viewmode_widgets(fb2, bfwin->session->filebrowser_viewmode);
2597
2598 if (bfwin->session->last_filefilter) {
2599 Tfilter *newfilter = find_filter_by_name(bfwin->session->last_filefilter);
2600 if (fb2->curfilter == NULL || newfilter == NULL
2601 || !(newfilter == fb2->curfilter || strcmp(newfilter->name, fb2->curfilter->name) == 0)) {
2602 fb2->curfilter = newfilter;
2603 need_refilter = TRUE;
2604 }
2605 }
2606 if (fb2->filebrowser_show_hidden_files != bfwin->session->filebrowser_show_hidden_files) {
2607 fb2->filebrowser_show_hidden_files = bfwin->session->filebrowser_show_hidden_files;
2608 need_refilter = TRUE;
2609 }
2610 if (fb2->filebrowser_show_backup_files != bfwin->session->filebrowser_show_backup_files) {
2611 fb2->filebrowser_show_backup_files = bfwin->session->filebrowser_show_backup_files;
2612 need_refilter = TRUE;
2613 }
2614 if (bfwin->session->recent_dirs) {
2615 const gchar *tmp = (gchar *) ((GList *) g_list_first(bfwin->session->recent_dirs))->data;
2616 /* the set_basedir_backend function tests itself if the basedir if changed, if not it does not refresh */
2617 DEBUG_MSG("fb2_update_settings_from_session, set basedir %s\n", tmp);
2618 if (tmp && tmp[0]) {
2619 GtkTreePath *fs_path, *filter_path;
2620 GFile *uri = g_file_new_for_uri(strcmp(tmp,"file:///")==0?tmp:strip_trailing_slash((gchar *) tmp));
2621 DEBUG_MSG("fb2_update_settings_from_session, set basedir %p\n",uri);
2622 fb2_set_basedir(fb2, uri);
2623 fb2_set_dirmenu(fb2, uri, FALSE);
2624 if (fb2->filebrowser_viewmode == viewmode_dual) {
2625 set_file_v_root(fb2, uri);
2626 }
2627 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, uri);
2628 /* on an empty window / empty project, expand the basedir, because there will be no call for follow document */
2629 if (!active_doc || !bfwin->session->filebrowser_focus_follow) {
2630 fs_path = treepath_for_uri(fb2, uri);
2631 if (fs_path) {
2632 filter_path = dir_v_filter_path_from_treestore_path(fb2, fs_path);
2633 if (filter_path) {
2634 expand_without_directory_refresh(fb2, filter_path);
2635 gtk_tree_path_free(filter_path);
2636 }
2637 gtk_tree_path_free(fs_path);
2638 }
2639
2640 }
2641 g_object_unref(uri);
2642 }
2643 } else {
2644 DEBUG_MSG("fb2_update_settings_from_session, set basedir NULL\n");
2645 set_basedir_backend(fb2, NULL);
2646 }
2647 if (bfwin->session->webroot && bfwin->session->documentroot) {
2648 GtkTreeIter iter;
2649 GFile *uri = g_file_new_for_uri(bfwin->session->documentroot);
2650 if (!filetree_get_iter_for_uri(FB2CONFIG(main_v->fb2config)->ftm, uri, &iter)) {
2651 filetreemodel_build_dir(FB2CONFIG(main_v->fb2config)->ftm, uri, NULL);
2652 }
2653 fb2config_set_documentroot_icon(uri);
2654 g_object_unref(uri);
2655 }
2656 /* TODO: set_basedir_backend already calls refilter in most cases (not if the
2657 requested basedir was already the active basedir), so
2658 we can optimise this and call refilter only when really needed. */
2659 if (need_refilter) {
2660 DEBUG_MSG("fb2_update_settings_from_session, need_refilter=%d, dir_filter=%p, file_filter=%p\n",need_refilter,fb2->file_filter,fb2->dir_filter);
2661 if (fb2->dir_filter)
2662 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fb2->dir_filter));
2663 if (fb2->file_filter && fb2->filebrowser_viewmode == viewmode_dual)
2664 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fb2->file_filter));
2665 }
2666 DEBUG_MSG("fb2_update_settings_from_session, done.\n");
2667 }
2668
2669 GtkWidget *
fb2_init(Tbfwin * bfwin)2670 fb2_init(Tbfwin * bfwin)
2671 {
2672 Tfilebrowser2 *fb2;
2673 GtkCellRenderer *renderer;
2674 #ifdef DEVELOPMENT
2675 if (!bfwin->session) {
2676 g_critical("bfwin->session should not be NULL\n");
2677 g_return_val_if_reached(NULL);
2678 }
2679 #endif
2680 fb2 = g_new0(Tfilebrowser2, 1);
2681 fb2->filebrowser_viewmode = bfwin->session->filebrowser_viewmode;
2682
2683 bfwin->fb2 = fb2;
2684 fb2->bfwin = bfwin;
2685 DEBUG_MSG("fb2_init, started for bfwin=%p, fb2=%p, fb2->filebrowser_viewmode=%d\n", bfwin, fb2,
2686 fb2->filebrowser_viewmode);
2687
2688 fb2->vbox = gtk_vbox_new(FALSE, 0);
2689 g_signal_connect(G_OBJECT(fb2->vbox), "destroy", G_CALLBACK(gtk_widget_destroyed), &fb2->vbox);
2690 fb2->dirmenu_m = GTK_TREE_MODEL(gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING));
2691 fb2->dirmenu_v = gtk_combo_box_new_with_model(fb2->dirmenu_m);
2692 /*gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(fb2->dirmenu_v),3); */
2693 renderer = gtk_cell_renderer_pixbuf_new();
2694 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(fb2->dirmenu_v), renderer, FALSE);
2695 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(fb2->dirmenu_v), renderer, "icon-name", DIR_ICON_COLUMN,
2696 /*"pixbuf_expander_closed", DIR_ICON_COLUMN,
2697 "pixbuf_expander_open", DIR_ICON_COLUMN, */ NULL);
2698
2699 renderer = gtk_cell_renderer_text_new();
2700 #if GTK_CHECK_VERSION(3,0,0)
2701 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_START, NULL);
2702 #endif
2703 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(fb2->dirmenu_v), renderer, TRUE);
2704 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(fb2->dirmenu_v), renderer, "text", DIR_NAME_COLUMN, NULL);
2705 gtk_box_pack_start(GTK_BOX(fb2->vbox), fb2->dirmenu_v, FALSE, FALSE, 0);
2706 fb2->dirmenu_changed_signal =
2707 g_signal_connect(fb2->dirmenu_v, "changed", G_CALLBACK(dirmenu_changed_lcb), bfwin);
2708
2709 /* already called from bfwin.c fb2_update_settings_from_session(bfwin, NULL);*/
2710
2711 gtk_widget_show_all(fb2->vbox);
2712 filetreemodel_dirchange_register(FB2CONFIG(main_v->fb2config)->ftm, dir_changed_lcb, fb2);
2713 return fb2->vbox;
2714 }
2715
2716 void
fb2_cleanup(Tbfwin * bfwin)2717 fb2_cleanup(Tbfwin * bfwin)
2718 {
2719 if (bfwin->fb2) {
2720 Tfilebrowser2 *fb2 = FILEBROWSER2(bfwin->fb2);
2721 GList *actions, *list;
2722 DEBUG_MSG("fb2_cleanup, fb2=%p, fb2->bfwin=%p, fb2->vbox=%p\n",fb2,fb2->bfwin,fb2->vbox);
2723
2724 filetreemodel_dirchange_unregister_by_data(FB2CONFIG(main_v->fb2config)->ftm, fb2);
2725 if (fb2->vbox) {
2726 DEBUG_MSG("fb2_cleanup, we still have a vbox, destroy vbox\n");
2727 gtk_widget_destroy(fb2->vbox);
2728 }
2729
2730 DEBUG_MSG("fb2_cleanup, remove ui_manager actions\n");
2731 if (bfwin->fb2_filters_group) {
2732 gtk_ui_manager_remove_ui(bfwin->uimanager, bfwin->fb2_filters_merge_id);
2733 actions = gtk_action_group_list_actions(bfwin->fb2_filters_group);
2734 for (list = actions; list; list = list->next) {
2735 g_signal_handlers_disconnect_by_func(GTK_ACTION(list->data),
2736 G_CALLBACK(popup_menu_filter_activate), bfwin);
2737 gtk_action_group_remove_action(bfwin->fb2_filters_group, GTK_ACTION(list->data));
2738 }
2739 g_list_free(actions);
2740 bfwin->fb2_filters_group = NULL;
2741 }
2742 gtk_ui_manager_remove_ui(bfwin->uimanager, bfwin->filebrowser_merge_id);
2743 actions = gtk_action_group_list_actions(bfwin->filebrowserGroup);
2744 for (list = actions; list; list = list->next) {
2745 gtk_action_group_remove_action(bfwin->filebrowserGroup, GTK_ACTION(list->data));
2746 }
2747 g_list_free(actions);
2748 gtk_ui_manager_remove_action_group(bfwin->uimanager,bfwin->filebrowserGroup);
2749 bfwin->filebrowserGroup = NULL;
2750 /* g_print("fb2_cleanup, disconnect dirmenu_changed_signal\n");
2751 g_signal_handler_disconnect(fb2->dirmenu_v, fb2->dirmenu_changed_signal);
2752 */ dirmenu_idle_cleanup_lcb(fb2->dirmenu_m);
2753 if (fb2->basedir)
2754 g_object_unref(fb2->basedir);
2755 if (fb2->currentdir)
2756 g_object_unref(fb2->currentdir);
2757 DEBUG_MSG("fb2_cleanup, free %p\n",fb2);
2758 g_free(fb2);
2759 bfwin->fb2 = NULL;
2760 }
2761 }
2762
2763 /*****************************************************************************************************************************/
2764 /* END OF MAIN GUI FUNCTIONS */
2765 /*****************************************************************************************************************************/
2766
2767
2768
2769
2770 /**************/
2771
2772
2773
2774
2775
2776 /**
2777 * fb2_focus_dir:
2778 *
2779 * builds the directory tree, refreshed the directory, and (if not noselect) expands and selects the
2780 * result. during the expand it will block the expand signal handler, so the expand callback
2781 * will not be called
2782 *
2783 */
2784 /*
2785 static void
2786 fb2_focus_dir(Tfilebrowser2 * fb2, GFile * uri, gboolean noselect)
2787 {
2788 GtkTreeIter *dir;
2789 DEBUG_MSG("fb2_focus_dir(fb2=%p, uri=%p, noselect=%d)\n", fb2, uri, noselect);
2790 if (!uri) {
2791 DEBUG_MSG("fb2_focus_dir, WARNING, CANNOT FOCUS WITHOUT URI\n");
2792 return;
2793 }
2794 g_object_ref(uri);
2795 if (fb2->filebrowser_viewmode == viewmode_flat) {
2796 set_basedir_backend(fb2, uri);
2797 } else {
2798 if (fb2->basedir) {
2799 if (!gfile_uri_is_parent(fb2->basedir, uri, TRUE)
2800 && !g_file_equal(fb2->basedir, uri)) {
2801 set_basedir_backend(fb2, NULL);
2802 }
2803 }
2804 }
2805 dir = g_hash_table_lookup(FB2CONFIG(main_v->fb2config)->filesystem_itable, uri);
2806 DEBUG_MSG("fb2_focus_dir, fb2=%p, dir=%p\n", fb2, dir);
2807 if (!dir) {
2808 dir = fb2_build_dir(uri);
2809 DEBUG_MSG("fb2_focus_dir, after building, fb2=%p, dir=%p\n", fb2, dir);
2810 }
2811 if (dir) {
2812 GtkTreePath *fs_path;
2813 DEBUG_DIRITER(dir);
2814
2815 / * set this directory as the top tree for the file widget * /
2816 fs_path =
2817 gtk_tree_model_get_path(GTK_TREE_MODEL(FB2CONFIG(main_v->fb2config)->ftm), dir);
2818 if (fs_path) {
2819 refilter_filelist(fb2, fs_path);
2820 if (!noselect && fb2->filebrowser_viewmode != viewmode_flat) {
2821 GtkTreePath *sort_path = dir_filter_path_from_treestore_path(fb2, fs_path);
2822 if (sort_path) {
2823 if (fb2->filebrowser_viewmode == viewmode_tree && gtk_tree_view_row_expanded(GTK_TREE_VIEW(fb2->dir_v), sort_path) && !need_to_scroll_to_dir(fb2, uri)) {
2824 / * do nothing * /
2825 } else {
2826 g_signal_handler_block(fb2->dir_v, fb2->expand_signal);
2827 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(fb2->dir_v), sort_path);
2828 g_signal_handler_unblock(fb2->dir_v, fb2->expand_signal);
2829 DEBUG_MSG("fb2_focus_dir, selecting path\n");
2830 gtk_tree_selection_select_path(gtk_tree_view_get_selection
2831 (GTK_TREE_VIEW(fb2->dir_v)), sort_path);
2832 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fb2->dir_v), sort_path, 0, TRUE, 0.5, 0.5);
2833 }
2834 gtk_tree_path_free(sort_path);
2835 } else {
2836 DEBUG_MSG("fb2_focus_dir, no sort_path\n");
2837 }
2838 } else {
2839 / * 'uri' is not persistent, 'dir' is peristent, so only pass 'dir'
2840 the "expand" signal (dual or tree view) will also refresh this directory, so
2841 we call this only on 'noselect' or in the flat view
2842 * /
2843 DEBUG_MSG("fb2_focus_dir, noselect, so directly call refresh\n");
2844 fb2_refresh_dir(NULL, dir);
2845 if (fb2->filebrowser_viewmode == viewmode_flat) {
2846 fb2_set_dirmenu(fb2, uri);
2847 }
2848 }
2849 gtk_tree_path_free(fs_path);
2850 } else {
2851 DEBUG_MSG("NO TREEPATH FOR THE DIR ITER WE TRY TO FOCUS ?!?!?!\n");
2852 }
2853 } else {
2854 DEBUG_MSG("NO dir RETURNED by fb2_build_dir or the hash table\n");
2855 }
2856 g_object_unref(uri);
2857 }
2858 */
2859
2860 /**
2861 * fb2_focus_document:
2862 *
2863 * external function, will make sure the filebrowser shows the current dcoument
2864 * directory
2865 */
2866 void
fb2_focus_document(Tbfwin * bfwin,Tdocument * doc)2867 fb2_focus_document(Tbfwin * bfwin, Tdocument * doc)
2868 {
2869 DEBUG_MSG("fb2_focus_document,doc %s\n", gtk_label_get_text(GTK_LABEL(doc->tab_menu)));
2870 if (bfwin->fb2 && doc && doc->uri) {
2871 fb2_follow_uri(bfwin, doc->uri);
2872
2873 /*GFile *dir_uri;*/
2874 /* first we make sure we have the correct directory open, then
2875 we could select the document, but if the directory *was* open already, this
2876 could disturb the user... hmmm... */
2877 /*dir_uri = g_file_get_parent(doc->uri);
2878 fb2_focus_dir(FILEBROWSER2(bfwin->fb2), dir_uri, FALSE);
2879 g_object_unref(dir_uri);*/
2880 }
2881 }
2882