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