1 /*
2  * (SLIK) SimpLIstic sKin functions
3  * (C) 2004 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11 
12 #ifdef HAVE_CONFIG_H
13 #  include "config.h"
14 #endif
15 #include "intl.h"
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include <dirent.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 
27 #include <gtk/gtk.h>
28 
29 #include <gdk/gdkkeysyms.h> /* for key values */
30 
31 #include "ui_pathsel.h"
32 
33 #include "ui_bookmark.h"
34 #include "ui_fileops.h"
35 #include "ui_menu.h"
36 #include "ui_misc.h"
37 #include "ui_utildlg.h"
38 #include "ui_tabcomp.h"
39 #include "ui_tree_edit.h"
40 
41 
42 #define DEST_WIDTH 250
43 #define DEST_HEIGHT 210
44 
45 #define RENAME_PRESS_DELAY 333	/* 1/3 second, to allow double clicks */
46 
47 #define PATH_SEL_USE_HEADINGS FALSE
48 
49 enum {
50 	FILTER_COLUMN_NAME = 0,
51 	FILTER_COLUMN_FILTER
52 };
53 
54 typedef struct _Dest_Data Dest_Data;
55 struct _Dest_Data
56 {
57 	GtkWidget *d_view;
58 	GtkWidget *f_view;
59 	GtkWidget *entry;
60 	gchar *filter;
61 	gchar *path;
62 
63 	GList *filter_list;
64 	GList *filter_text_list;
65 	GtkWidget *filter_combo;
66 
67 	gint show_hidden;
68 	GtkWidget *hidden_button;
69 
70 	GtkWidget *bookmark_list;
71 
72 	GtkTreePath *right_click_path;
73 
74 	void (*select_func)(const gchar *path, gpointer data);
75 	gpointer select_data;
76 
77 	GenericDialog *gd;	/* any open confirm dialogs ? */
78 };
79 
80 typedef struct _DestDel_Data DestDel_Data;
81 struct _DestDel_Data
82 {
83 	Dest_Data *dd;
84 	gchar *path;
85 };
86 
87 
88 static void dest_view_delete_dlg_cancel(GenericDialog *gd, gpointer data);
89 
90 
91 /*
92  *-----------------------------------------------------------------------------
93  * (private)
94  *-----------------------------------------------------------------------------
95  */
96 
dest_free_data(GtkWidget * widget,gpointer data)97 static void dest_free_data(GtkWidget *widget, gpointer data)
98 {
99 	Dest_Data *dd = data;
100 
101 	if (dd->gd)
102 		{
103 		GenericDialog *gd = dd->gd;
104 		dest_view_delete_dlg_cancel(dd->gd, dd->gd->data);
105 		generic_dialog_close(gd);
106 		}
107 	if (dd->right_click_path) gtk_tree_path_free(dd->right_click_path);
108 
109 	g_free(dd->filter);
110 	g_free(dd->path);
111 	g_free(dd);
112 }
113 
dest_check_filter(const gchar * filter,const gchar * file)114 static gint dest_check_filter(const gchar *filter, const gchar *file)
115 {
116 	const gchar *f_ptr = filter;
117 	const gchar *strt_ptr;
118 	gint i;
119 	gint l;
120 
121 	l = strlen(file);
122 
123 	if (filter[0] == '*') return TRUE;
124 	while (f_ptr < filter + strlen(filter))
125 		{
126 		strt_ptr = f_ptr;
127 		i=0;
128 		while (*f_ptr != ';' && *f_ptr != '\0')
129 			{
130 			f_ptr++;
131 			i++;
132 			}
133 		if (*f_ptr != '\0' && f_ptr[1] == ' ') f_ptr++;	/* skip space immediately after separator */
134 		f_ptr++;
135 		if (l >= i && strncasecmp(file + l - i, strt_ptr, i) == 0) return TRUE;
136 		}
137 	return FALSE;
138 }
139 
140 #ifndef CASE_SORT
141 #define CASE_SORT strcmp
142 #endif
143 
dest_sort_cb(void * a,void * b)144 static gint dest_sort_cb(void *a, void *b)
145 {
146 	return CASE_SORT((gchar *)a, (gchar *)b);
147 }
148 
is_hidden(const gchar * name)149 static gint is_hidden(const gchar *name)
150 {
151 	if (name[0] != '.') return FALSE;
152 	if (name[1] == '\0') return FALSE;
153 	if (name[1] == '.' && name[2] == '\0') return FALSE;
154 	return TRUE;
155 }
156 
dest_populate(Dest_Data * dd,const gchar * path)157 static void dest_populate(Dest_Data *dd, const gchar *path)
158 {
159 	DIR *dp;
160 	struct dirent *dir;
161 	struct stat ent_sbuf;
162 	GList *path_list = NULL;
163 	GList *file_list = NULL;
164 	GList *list;
165 	GtkListStore *store;
166 	gchar *pathl;
167 
168 	if(!path) return;
169 
170 	pathl = path_from_utf8(path);
171 	dp = opendir(pathl);
172 	if (!dp)
173 		{
174 		/* dir not found */
175 		g_free(pathl);
176 		return;
177 		}
178 	while ((dir = readdir(dp)) != NULL)
179 		{
180 		/* skips removed files */
181 		if (dir->d_ino > 0 && (dd->show_hidden || !is_hidden(dir->d_name)) )
182 			{
183 			gchar *name = dir->d_name;
184 			gchar *filepath = g_strconcat(pathl, "/", name, NULL);
185 			if (stat(filepath, &ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
186 				{
187 				path_list = g_list_prepend(path_list, path_to_utf8(name));
188 				}
189 			else if (dd->f_view)
190 				{
191 				if (!dd->filter || (dd->filter && dest_check_filter(dd->filter, name)))
192 					file_list = g_list_prepend(file_list, path_to_utf8(name));
193 				}
194 			g_free(filepath);
195 			}
196 		}
197 	closedir(dp);
198 	g_free(pathl);
199 
200 	path_list = g_list_sort(path_list, (GCompareFunc) dest_sort_cb);
201 	file_list = g_list_sort(file_list, (GCompareFunc) dest_sort_cb);
202 
203 	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(dd->d_view)));
204 	gtk_list_store_clear(store);
205 
206 	list = path_list;
207 	while (list)
208 		{
209 		GtkTreeIter iter;
210 		gchar *filepath;
211 
212 		if (strcmp(list->data, ".") == 0)
213 			{
214 			filepath = g_strdup(path);
215 			}
216 		else if (strcmp(list->data, "..") == 0)
217 			{
218 			gchar *p;
219 			filepath = g_strdup(path);
220 			p = (gchar *)filename_from_path(filepath);
221 			if (p - 1 != filepath) p--;
222 			p[0] = '\0';
223 			}
224 		else
225 			{
226 			filepath = concat_dir_and_file(path, list->data);
227 			}
228 
229 		gtk_list_store_append(store, &iter);
230 		gtk_list_store_set(store, &iter, 0, list->data, 1, filepath, -1);
231 
232 		g_free(filepath);
233 		list = list->next;
234 		}
235 
236 	path_list_free(path_list);
237 
238 
239 	if (dd->f_view)
240 		{
241 		store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(dd->f_view)));
242 		gtk_list_store_clear(store);
243 
244 		list = file_list;
245 		while (list)
246         	        {
247 			GtkTreeIter iter;
248 			gchar *filepath;
249 			const gchar *name = list->data;
250 
251 			filepath = concat_dir_and_file(path, name);
252 
253 			gtk_list_store_append(store, &iter);
254 			gtk_list_store_set(store, &iter, 0, name, 1, filepath, -1);
255 
256 			g_free(filepath);
257 			list = list->next;
258 			}
259 
260 		path_list_free(file_list);
261 		}
262 
263 	g_free(dd->path);
264 	dd->path = g_strdup(path);
265 }
266 
dest_change_dir(Dest_Data * dd,const gchar * path,gint retain_name)267 static void dest_change_dir(Dest_Data *dd, const gchar *path, gint retain_name)
268 {
269 	gchar *old_name = NULL;
270 	gint s = 0;
271 
272 	if (retain_name)
273 		{
274 		const gchar *buf = gtk_entry_get_text(GTK_ENTRY(dd->entry));
275 		if (!isdir(buf))
276 			{
277 			if (path && strcmp(path, "/") == 0)
278 				{
279 				old_name = g_strdup(filename_from_path(buf));
280 				}
281 			else
282 				{
283 				old_name = g_strconcat("/", filename_from_path(buf), NULL);
284 				s = 1;
285 				}
286 			}
287 		}
288 
289 	gtk_entry_set_text(GTK_ENTRY(dd->entry), path);
290 
291 	dest_populate(dd, path);
292 
293 	/* remember filename */
294 	if (old_name)
295 		{
296 		gint pos = -1;
297 		gtk_editable_insert_text(GTK_EDITABLE(dd->entry), old_name, -1, &pos);
298 		gtk_editable_select_region(GTK_EDITABLE(dd->entry), strlen(path) + s, strlen(path) + strlen(old_name));
299 		g_free(old_name);
300 		}
301 }
302 
303 /*
304  *-----------------------------------------------------------------------------
305  * drag and drop
306  *-----------------------------------------------------------------------------
307  */
308 
309 enum {
310 	TARGET_URI_LIST,
311 	TARGET_TEXT_PLAIN
312 };
313 
314 static GtkTargetEntry dest_drag_types[] = {
315 	{ "text/uri-list", 0, TARGET_URI_LIST },
316 	{ "text/plain",    0, TARGET_TEXT_PLAIN }
317 };
318 #define dest_drag_types_n 2
319 
320 
dest_dnd_set_data(GtkWidget * view,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time,gpointer data)321 static void dest_dnd_set_data(GtkWidget *view,
322 			      GdkDragContext *context, GtkSelectionData *selection_data,
323 			      guint info, guint time, gpointer data)
324 {
325 	gchar *path = NULL;
326 	gchar *uri_text = NULL;
327 	GList *list = NULL;
328 	gint length = 0;
329 	GtkTreeModel *model;
330 	GtkTreeSelection *selection;
331 	GtkTreeIter iter;
332 
333 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
334 	if (!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
335 
336 	gtk_tree_model_get(model, &iter, 1, &path, -1);
337 	if (!path) return;
338 
339 	list = g_list_append(list, path);
340 
341 	switch (info)
342 		{
343 		case TARGET_URI_LIST:
344 			uri_text = uri_text_from_list(list, &length, FALSE);
345 			break;
346 		case TARGET_TEXT_PLAIN:
347 			uri_text = uri_text_from_list(list, &length, TRUE);
348 			break;
349 		}
350 
351 	path_list_free(list);
352 
353 	if (!uri_text) return;
354 
355 	gtk_selection_data_set(selection_data, selection_data->target,
356 			       8, uri_text, length);
357 	g_free(uri_text);
358 }
359 
dest_dnd_init(Dest_Data * dd)360 static void dest_dnd_init(Dest_Data *dd)
361 {
362 	gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(dd->d_view), GDK_BUTTON1_MASK,
363 					       dest_drag_types, dest_drag_types_n,
364 					       GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
365 	g_signal_connect(G_OBJECT(dd->d_view), "drag_data_get",
366 			 G_CALLBACK(dest_dnd_set_data), dd);
367 
368 	if (dd->f_view)
369 		{
370 		gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(dd->f_view), GDK_BUTTON1_MASK,
371 						       dest_drag_types, dest_drag_types_n,
372 						       GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
373 		g_signal_connect(G_OBJECT(dd->f_view), "drag_data_get",
374 				 G_CALLBACK(dest_dnd_set_data), dd);
375 		}
376 }
377 
378 
379 /*
380  *-----------------------------------------------------------------------------
381  * destination widget file management utils
382  *-----------------------------------------------------------------------------
383  */
384 
dest_view_store_selection(Dest_Data * dd,GtkTreeView * view)385 static void dest_view_store_selection(Dest_Data *dd, GtkTreeView *view)
386 {
387 	GtkTreeModel *model;
388 	GtkTreeSelection *selection;
389 	GtkTreeIter iter;
390 
391 	if (dd->right_click_path) gtk_tree_path_free(dd->right_click_path);
392 	dd->right_click_path = NULL;
393 
394 	selection = gtk_tree_view_get_selection(view);
395 	if (!gtk_tree_selection_get_selected(selection, &model, &iter))
396 		{
397 		return;
398 		}
399 
400 	dd->right_click_path = gtk_tree_model_get_path(model, &iter);
401 }
402 
dest_view_rename_cb(TreeEditData * ted,const gchar * old,const gchar * new,gpointer data)403 static gint dest_view_rename_cb(TreeEditData *ted, const gchar *old, const gchar *new, gpointer data)
404 {
405 	Dest_Data *dd = data;
406 	GtkTreeModel *model;
407 	GtkTreeIter iter;
408 	gchar *buf;
409 	gchar *old_path;
410 	gchar *new_path;
411 
412 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(ted->tree));
413 	gtk_tree_model_get_iter(model, &iter, dd->right_click_path);
414 
415 	gtk_tree_model_get(model, &iter, 1, &old_path, -1);
416 	if (!old_path) return FALSE;
417 
418 	buf = remove_level_from_path(old_path);
419 	new_path = concat_dir_and_file(buf, new);
420 	g_free(buf);
421 
422 	if (isname(new_path))
423 		{
424 		buf = g_strdup_printf(_("A file with name %s already exists."), new);
425 		warning_dialog("Rename failed", buf, GTK_STOCK_DIALOG_INFO, dd->entry);
426 		g_free(buf);
427 		}
428 	else if (!rename_file(old_path, new_path))
429 		{
430 		buf = g_strdup_printf(_("Failed to rename %s to %s."), old, new);
431 		warning_dialog("Rename failed", buf, GTK_STOCK_DIALOG_ERROR, dd->entry);
432 		g_free(buf);
433 		}
434 	else
435 		{
436 		const gchar *text;
437 
438 		gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, new, 1, new_path, -1);
439 
440 		text = gtk_entry_get_text(GTK_ENTRY(dd->entry));
441 		if (text && old_path && strcmp(text, old_path) == 0)
442 			{
443 			gtk_entry_set_text(GTK_ENTRY(dd->entry), new_path);
444 			}
445 		}
446 
447 	g_free(old_path);
448 	g_free(new_path);
449 
450 	return TRUE;
451 }
452 
dest_view_rename(Dest_Data * dd,GtkTreeView * view)453 static void dest_view_rename(Dest_Data *dd, GtkTreeView *view)
454 {
455 	GtkTreeModel *model;
456 	GtkTreeIter iter;
457 	gchar *text;
458 
459 	if (!dd->right_click_path) return;
460 
461 	model = gtk_tree_view_get_model(view);
462 	gtk_tree_model_get_iter(model, &iter, dd->right_click_path);
463 	gtk_tree_model_get(model, &iter, 0, &text, -1);
464 
465 	tree_edit_by_path(view, dd->right_click_path, 0, text,
466 			  dest_view_rename_cb, dd);
467 
468 	g_free(text);
469 }
470 
dest_view_delete_dlg_cancel(GenericDialog * gd,gpointer data)471 static void dest_view_delete_dlg_cancel(GenericDialog *gd, gpointer data)
472 {
473 	DestDel_Data *dl = data;
474 
475 	dl->dd->gd = NULL;
476 	g_free(dl->path);
477 	g_free(dl);
478 }
479 
dest_view_delete_dlg_ok_cb(GenericDialog * gd,gpointer data)480 static void dest_view_delete_dlg_ok_cb(GenericDialog *gd, gpointer data)
481 {
482 	DestDel_Data *dl = data;
483 
484 	if (!unlink_file(dl->path))
485 		{
486 		gchar *text = g_strdup_printf(_("Unable to delete file:\n%s"), dl->path);
487 		warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_WARNING, dl->dd->entry);
488 		g_free(text);
489 		}
490 	else if (dl->dd->path)
491 		{
492 		/* refresh list */
493 		gchar *path = g_strdup(dl->dd->path);
494 		dest_populate(dl->dd, path);
495 		g_free(path);
496 		}
497 
498 	dest_view_delete_dlg_cancel(gd, data);
499 }
500 
dest_view_delete(Dest_Data * dd,GtkTreeView * view)501 static void dest_view_delete(Dest_Data *dd, GtkTreeView *view)
502 {
503 	gchar *path;
504 	gchar *text;
505 	DestDel_Data *dl;
506 	GtkTreeModel *model;
507 	GtkTreeIter iter;
508 
509 	if (view != GTK_TREE_VIEW(dd->f_view)) return;
510 	if (!dd->right_click_path) return;
511 
512 	model = gtk_tree_view_get_model(view);
513 	gtk_tree_model_get_iter(model, &iter, dd->right_click_path);
514 	gtk_tree_model_get(model, &iter, 1, &path, -1);
515 
516 	if (!path) return;
517 
518 	dl = g_new(DestDel_Data, 1);
519 	dl->dd = dd;
520 	dl->path = path;
521 
522 	if (dd->gd)
523 		{
524 		GenericDialog *gd = dd->gd;
525 		dest_view_delete_dlg_cancel(dd->gd, dd->gd->data);
526 		generic_dialog_close(gd);
527 		}
528 
529 	dd->gd = generic_dialog_new(_("Delete file"), PACKAGE, "dlg_confirm",
530 				    dd->entry, TRUE,
531 				    dest_view_delete_dlg_cancel, dl);
532 
533 	generic_dialog_add_button(dd->gd, GTK_STOCK_DELETE, NULL, dest_view_delete_dlg_ok_cb, TRUE);
534 
535 	text = g_strdup_printf(_("About to delete the file:\n %s"), path);
536 	generic_dialog_add_message(dd->gd, GTK_STOCK_DIALOG_QUESTION,
537 				   _("Delete file"), text);
538 	g_free(text);
539 
540 	gtk_widget_show(dd->gd->dialog);
541 }
542 
dest_view_bookmark(Dest_Data * dd,GtkTreeView * view)543 static void dest_view_bookmark(Dest_Data *dd, GtkTreeView *view)
544 {
545 	GtkTreeModel *model;
546 	GtkTreeIter iter;
547 	gchar *path;
548 
549 	if (!dd->right_click_path) return;
550 
551 	model = gtk_tree_view_get_model(view);
552 	gtk_tree_model_get_iter(model, &iter, dd->right_click_path);
553 	gtk_tree_model_get(model, &iter, 1, &path, -1);
554 
555 	bookmark_list_add(dd->bookmark_list, filename_from_path(path), path);
556 	g_free(path);
557 }
558 
dest_popup_dir_rename_cb(GtkWidget * widget,gpointer data)559 static void dest_popup_dir_rename_cb(GtkWidget *widget, gpointer data)
560 {
561 	Dest_Data *dd = data;
562 	dest_view_rename(dd, GTK_TREE_VIEW(dd->d_view));
563 }
564 
dest_popup_dir_bookmark_cb(GtkWidget * widget,gpointer data)565 static void dest_popup_dir_bookmark_cb(GtkWidget *widget, gpointer data)
566 {
567 	Dest_Data *dd = data;
568 	dest_view_bookmark(dd, GTK_TREE_VIEW(dd->d_view));
569 }
570 
dest_popup_file_rename_cb(GtkWidget * widget,gpointer data)571 static void dest_popup_file_rename_cb(GtkWidget *widget, gpointer data)
572 {
573 	Dest_Data *dd = data;
574 	dest_view_rename(dd, GTK_TREE_VIEW(dd->f_view));
575 }
576 
dest_popup_file_delete_cb(GtkWidget * widget,gpointer data)577 static void dest_popup_file_delete_cb(GtkWidget *widget, gpointer data)
578 {
579 	Dest_Data *dd = data;
580 	dest_view_delete(dd, GTK_TREE_VIEW(dd->f_view));
581 }
582 
dest_popup_file_bookmark_cb(GtkWidget * widget,gpointer data)583 static void dest_popup_file_bookmark_cb(GtkWidget *widget, gpointer data)
584 {
585 	Dest_Data *dd = data;
586 	dest_view_bookmark(dd, GTK_TREE_VIEW(dd->f_view));
587 }
588 
dest_popup_position_cb(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer data)589 static void dest_popup_position_cb(GtkMenu *menu, gint *x, gint *y,
590 				   gboolean *push_in, gpointer data)
591 {
592 	Dest_Data *dd = data;
593 	GtkTreeView *view;
594 	gint cw, ch;
595 
596 	view = g_object_get_data(G_OBJECT(menu), "active_view");
597 
598 	tree_view_get_cell_clamped(view, dd->right_click_path, 0, TRUE, x, y, &cw, &ch);
599 	*y += ch;
600 	popup_menu_position_clamp(menu, x, y, 0);
601 }
602 
dest_popup_menu(Dest_Data * dd,GtkTreeView * view,gint button,guint32 time,gint local)603 static gint dest_popup_menu(Dest_Data *dd, GtkTreeView *view,
604 			    gint button, guint32 time, gint local)
605 {
606 	GtkWidget *menu;
607 
608 	if (!dd->right_click_path) return FALSE;
609 
610 	if (view == GTK_TREE_VIEW(dd->d_view))
611 		{
612 		GtkTreeModel *model;
613 		GtkTreeIter iter;
614 		gchar *text;
615 		gint normal_dir;
616 
617 		model = gtk_tree_view_get_model(view);
618 		gtk_tree_model_get_iter(model, &iter, dd->right_click_path);
619 		gtk_tree_model_get(model, &iter, 0, &text, -1);
620 
621 		if (!text) return FALSE;
622 
623 		normal_dir = (strcmp(text, ".") == 0 || strcmp(text, "..") == 0);
624 
625 		menu = popup_menu_short_lived();
626 		menu_item_add_sensitive(menu, _("_Rename"), !normal_dir,
627 			      G_CALLBACK(dest_popup_dir_rename_cb), dd);
628 		menu_item_add_stock(menu, _("Add _Bookmark"), GTK_STOCK_JUMP_TO,
629 			      G_CALLBACK(dest_popup_dir_bookmark_cb), dd);
630 		}
631 	else
632 		{
633 		menu = popup_menu_short_lived();
634 		menu_item_add(menu, _("_Rename"),
635 				G_CALLBACK(dest_popup_file_rename_cb), dd);
636 		menu_item_add_stock(menu, _("_Delete"), GTK_STOCK_DELETE,
637 				G_CALLBACK(dest_popup_file_delete_cb), dd);
638 		menu_item_add_stock(menu, _("Add _Bookmark"), GTK_STOCK_JUMP_TO,
639 				G_CALLBACK(dest_popup_file_bookmark_cb), dd);
640 		}
641 
642 	if (local)
643 		{
644 		g_object_set_data(G_OBJECT(menu), "active_view", view);
645 		gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
646 			       dest_popup_position_cb, dd, button, time);
647 		}
648 	else
649 		{
650 		gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, time);
651 		}
652 
653 	return TRUE;
654 }
655 
dest_press_cb(GtkWidget * view,GdkEventButton * event,gpointer data)656 static gint dest_press_cb(GtkWidget *view, GdkEventButton *event, gpointer data)
657 {
658 	Dest_Data *dd = data;
659 	GtkTreePath *tpath;
660 	GtkTreeViewColumn *column;
661 	gint cell_x, cell_y;
662 	GtkTreeModel *model;
663 	GtkTreeIter iter;
664 	GtkTreeSelection *selection;
665 
666 	if (event->button != 3 ||
667 	    !gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), event->x, event->y,
668 					   &tpath, &column, &cell_x, &cell_y))
669 		{
670 		return FALSE;
671 		}
672 
673 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
674 	gtk_tree_model_get_iter(model, &iter, tpath);
675 
676 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
677 	gtk_tree_selection_select_iter(selection, &iter);
678 
679 	if (dd->right_click_path) gtk_tree_path_free(dd->right_click_path);
680 	dd->right_click_path = tpath;
681 
682 	return dest_popup_menu(dd, GTK_TREE_VIEW(view), 0, event->time, FALSE);
683 }
684 
dest_keypress_cb(GtkWidget * view,GdkEventKey * event,gpointer data)685 static gboolean dest_keypress_cb(GtkWidget *view, GdkEventKey *event, gpointer data)
686 {
687 	Dest_Data *dd = data;
688 
689 	switch (event->keyval)
690 		{
691 		case GDK_F10:
692 			if (!(event->state & GDK_CONTROL_MASK)) return FALSE;
693 		case GDK_Menu:
694 			dest_view_store_selection(dd, GTK_TREE_VIEW(view));
695 			dest_popup_menu(dd, GTK_TREE_VIEW(view), 0, event->time, TRUE);
696 			return TRUE;
697 			break;
698 		case 'R': case 'r':
699 			if (event->state & GDK_CONTROL_MASK)
700 				{
701 				dest_view_store_selection(dd, GTK_TREE_VIEW(view));
702 				dest_view_rename(dd, GTK_TREE_VIEW(view));
703 				return TRUE;
704 				}
705 			break;
706 		case GDK_Delete:
707 			dest_view_store_selection(dd, GTK_TREE_VIEW(view));
708 			dest_view_delete(dd, GTK_TREE_VIEW(view));
709 			return TRUE;
710 			break;
711 		case 'B' : case 'b':
712 			if (event->state & GDK_CONTROL_MASK)
713 				{
714 				dest_view_store_selection(dd, GTK_TREE_VIEW(view));
715 				dest_view_bookmark(dd, GTK_TREE_VIEW(view));
716 				return TRUE;
717 				}
718 			break;
719 		}
720 
721 	return FALSE;
722 }
723 
dest_new_dir_cb(GtkWidget * widget,gpointer data)724 static void dest_new_dir_cb(GtkWidget *widget, gpointer data)
725 {
726 	Dest_Data *dd = data;
727 	gchar *path;
728 	gchar *buf;
729 	const gchar *tmp;
730 	gint from_text = FALSE;
731 
732 	tmp = gtk_entry_get_text(GTK_ENTRY(dd->entry));
733 	if (!isname(tmp))
734 		{
735 		path = g_strdup(tmp);
736 		from_text = TRUE;
737 		}
738 	else
739 		{
740 		buf = concat_dir_and_file(dd->path, _("New folder"));
741 		path = unique_filename(buf, NULL, " ", FALSE);
742 		g_free(buf);
743 		}
744 
745 	if (!mkdir_utf8(path, 0755))
746 		{
747 		/* failed */
748 		gchar *text;
749 
750 		text = g_strdup_printf(_("Unable to create folder:\n%s"), filename_from_path(path));
751 		warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, dd->entry);
752 		g_free(text);
753 		}
754 	else
755 		{
756 		GtkTreeIter iter;
757 		GtkListStore *store;
758 		const gchar *text;
759 
760 		if (from_text) gtk_entry_set_text(GTK_ENTRY(dd->entry), dd->path);
761 
762 		store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(dd->d_view)));
763 
764 		text = filename_from_path(path);
765 
766 		gtk_list_store_append(store, &iter);
767 		gtk_list_store_set(store, &iter, 0, text, 1, path, -1);
768 
769 		if (dd->right_click_path) gtk_tree_path_free(dd->right_click_path);
770 		dd->right_click_path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
771 
772 		tree_edit_by_path(GTK_TREE_VIEW(dd->d_view), dd->right_click_path, 0, text,
773 				  dest_view_rename_cb, dd);
774 		}
775 
776 	g_free(path);
777 }
778 
779 /*
780  *-----------------------------------------------------------------------------
781  * destination widget file selection, traversal, view options
782  *-----------------------------------------------------------------------------
783  */
784 
dest_select_cb(GtkTreeSelection * selection,gpointer data)785 static void dest_select_cb(GtkTreeSelection *selection, gpointer data)
786 {
787 	Dest_Data *dd = data;
788 	GtkTreeView *view;
789 	GtkTreeModel *store;
790 	GtkTreeIter iter;
791 	gchar *path;
792 
793 	if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) return;
794 
795 	view = gtk_tree_selection_get_tree_view(selection);
796 	store = gtk_tree_view_get_model(view);
797 	gtk_tree_model_get(store, &iter, 1, &path, -1);
798 
799 	if (view == GTK_TREE_VIEW(dd->d_view))
800 		{
801 		dest_change_dir(dd, path, (dd->f_view != NULL));
802 		}
803 	else
804 		{
805 		gtk_entry_set_text(GTK_ENTRY(dd->entry), path);
806 		}
807 
808 	g_free(path);
809 }
810 
dest_activate_cb(GtkWidget * view,GtkTreePath * tpath,GtkTreeViewColumn * column,gpointer data)811 static void dest_activate_cb(GtkWidget *view, GtkTreePath *tpath, GtkTreeViewColumn *column, gpointer data)
812 {
813 	Dest_Data *dd = data;
814 	GtkTreeModel *store;
815 	GtkTreeIter iter;
816 	gchar *path;
817 
818 	store = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
819 	gtk_tree_model_get_iter(store, &iter, tpath);
820 	gtk_tree_model_get(store, &iter, 1, &path, -1);
821 
822 	if (view == dd->d_view)
823 		{
824 		dest_change_dir(dd, path, (dd->f_view != NULL));
825 		}
826 	else
827 		{
828 		if (dd->select_func)
829 			{
830 			dd->select_func(path, dd->select_data);
831 			}
832 		}
833 
834 	g_free(path);
835 }
836 
dest_home_cb(GtkWidget * widget,gpointer data)837 static void dest_home_cb(GtkWidget *widget, gpointer data)
838 {
839 	Dest_Data *dd = data;
840 
841 	dest_change_dir(dd, homedir(), (dd->f_view != NULL));
842 }
843 
dest_show_hidden_cb(GtkWidget * widget,gpointer data)844 static void dest_show_hidden_cb(GtkWidget *widget, gpointer data)
845 {
846 	Dest_Data *dd = data;
847 	gchar *buf;
848 
849 	dd->show_hidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dd->hidden_button));
850 
851 	buf = g_strdup(dd->path);
852 	dest_populate(dd, buf);
853 	g_free(buf);
854 }
855 
dest_entry_changed_cb(GtkEditable * editable,gpointer data)856 static void dest_entry_changed_cb(GtkEditable *editable, gpointer data)
857 {
858 	Dest_Data *dd = data;
859 	const gchar *path;
860 	gchar *buf;
861 
862 	path = gtk_entry_get_text(GTK_ENTRY(dd->entry));
863 	if (strcmp(path, dd->path) == 0) return;
864 
865 	buf = remove_level_from_path(path);
866 
867 	if (buf && strcmp(buf, dd->path) != 0)
868 		{
869 		gchar *tmp = remove_trailing_slash(path);
870 		if (isdir(tmp))
871 			{
872 			dest_populate(dd, tmp);
873 			}
874 		else if (isdir(buf))
875 			{
876 			dest_populate(dd, buf);
877 			}
878 		g_free(tmp);
879 		}
880 	g_free(buf);
881 }
882 
dest_filter_list_sync(Dest_Data * dd)883 static void dest_filter_list_sync(Dest_Data *dd)
884 {
885 	GtkWidget *entry;
886 	GtkListStore *store;
887 	gchar *old_text;
888 	GList *fwork;
889 	GList *twork;
890 
891 	if (!dd->filter_list || !dd->filter_combo) return;
892 
893 	entry = GTK_BIN(dd->filter_combo)->child;
894 	old_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
895 
896 	store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(dd->filter_combo)));
897 	gtk_list_store_clear(store);
898 
899 	fwork = dd->filter_list;
900 	twork = dd->filter_text_list;
901 	while (fwork && twork)
902 		{
903 		GtkTreeIter iter;
904 		gchar *name;
905 		gchar *filter;
906 
907 		name = twork->data;
908 		filter = fwork->data;
909 
910 		gtk_list_store_append(store, &iter);
911 		gtk_list_store_set(store, &iter, FILTER_COLUMN_NAME, name,
912 						 FILTER_COLUMN_FILTER, filter, -1);
913 
914 		if (strcmp(old_text, filter) == 0)
915 			{
916 			gtk_combo_box_set_active_iter(GTK_COMBO_BOX(dd->filter_combo), &iter);
917 			}
918 
919 		fwork = fwork->next;
920 		twork = twork->next;
921 		}
922 
923 	g_free(old_text);
924 }
925 
dest_filter_add(Dest_Data * dd,const gchar * filter,const gchar * description,gint set)926 static void dest_filter_add(Dest_Data *dd, const gchar *filter, const gchar *description, gint set)
927 {
928 	GList *work;
929 	gchar *buf;
930 	gint c = 0;
931 
932 	if (!filter) return;
933 
934 	work = dd->filter_list;
935 	while (work)
936 		{
937 		gchar *f = work->data;
938 
939 		if (strcmp(f, filter) == 0)
940 			{
941 			if (set) gtk_combo_box_set_active(GTK_COMBO_BOX(dd->filter_combo), c);
942 			return;
943 			}
944 		work = work->next;
945 		c++;
946 		}
947 
948 	dd->filter_list = uig_list_insert_link(dd->filter_list, g_list_last(dd->filter_list), g_strdup(filter));
949 
950 	if (description)
951 		{
952 		buf = g_strdup_printf("%s  ( %s )", description, filter);
953 		}
954 	else
955 		{
956 		buf = g_strdup_printf("( %s )", filter);
957 		}
958 	dd->filter_text_list = uig_list_insert_link(dd->filter_text_list, g_list_last(dd->filter_text_list), buf);
959 
960 	if (set) gtk_entry_set_text(GTK_ENTRY(GTK_BIN(dd->filter_combo)->child), filter);
961 	dest_filter_list_sync(dd);
962 }
963 
dest_filter_clear(Dest_Data * dd)964 static void dest_filter_clear(Dest_Data *dd)
965 {
966 	path_list_free(dd->filter_list);
967 	dd->filter_list = NULL;
968 
969 	path_list_free(dd->filter_text_list);
970 	dd->filter_text_list = NULL;
971 
972 	dest_filter_add(dd, "*", _("All Files"), TRUE);
973 }
974 
dest_filter_changed_cb(GtkEditable * editable,gpointer data)975 static void dest_filter_changed_cb(GtkEditable *editable, gpointer data)
976 {
977 	Dest_Data *dd = data;
978 	GtkWidget *entry;
979 	const gchar *buf;
980 	gchar *path;
981 
982 	entry = GTK_BIN(dd->filter_combo)->child;
983 	buf = gtk_entry_get_text(GTK_ENTRY(entry));
984 
985 	g_free(dd->filter);
986 	dd->filter = NULL;
987 	if (strlen(buf) > 0) dd->filter = g_strdup(buf);
988 
989 	path = g_strdup(dd->path);
990 	dest_populate(dd, path);
991 	g_free(path);
992 }
993 
dest_bookmark_select_cb(const gchar * path,gpointer data)994 static void dest_bookmark_select_cb(const gchar *path, gpointer data)
995 {
996 	Dest_Data *dd = data;
997 
998 	if (isdir(path))
999 		{
1000 		dest_change_dir(dd, path, (dd->f_view != NULL));
1001 		}
1002 	else if (isfile(path) && dd->f_view)
1003 		{
1004 		gtk_entry_set_text(GTK_ENTRY(dd->entry), path);
1005 		}
1006 }
1007 
1008 /*
1009  *-----------------------------------------------------------------------------
1010  * destination widget setup routines (public)
1011  *-----------------------------------------------------------------------------
1012  */
1013 
path_selection_new_with_files(GtkWidget * entry,const gchar * path,const gchar * filter,const gchar * filter_desc)1014 GtkWidget *path_selection_new_with_files(GtkWidget *entry, const gchar *path,
1015 					 const gchar *filter, const gchar *filter_desc)
1016 {
1017 	GtkWidget *hbox2;
1018 	Dest_Data *dd;
1019 	GtkWidget *scrolled;
1020 	GtkWidget *table;
1021 	GtkWidget *paned;
1022 	GtkListStore *store;
1023 	GtkTreeSelection *selection;
1024 	GtkTreeViewColumn *column;
1025 	GtkCellRenderer *renderer;
1026 
1027 	dd = g_new0(Dest_Data, 1);
1028 	dd->show_hidden = FALSE;
1029 	dd->select_func = NULL;
1030 	dd->select_data = NULL;
1031 	dd->gd = NULL;
1032 
1033 	table = gtk_table_new(4, (filter != NULL) ? 3 : 1, FALSE);
1034 	gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
1035 	gtk_table_set_row_spacing(GTK_TABLE(table), 0, PREF_PAD_GAP);
1036 	gtk_widget_show(table);
1037 
1038 	dd->entry = entry;
1039 	g_object_set_data(G_OBJECT(dd->entry), "destination_data", dd);
1040 
1041 	hbox2 = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
1042 	gtk_box_set_spacing(GTK_BOX(hbox2), PREF_PAD_BUTTON_GAP);
1043 	pref_button_new(hbox2, NULL, _("Home"), FALSE,
1044 			G_CALLBACK(dest_home_cb), dd);
1045 	pref_button_new(hbox2, NULL, _("New folder"), FALSE,
1046 			G_CALLBACK(dest_new_dir_cb), dd);
1047 
1048 	dd->hidden_button = gtk_check_button_new_with_label(_("Show hidden"));
1049 	g_signal_connect(G_OBJECT(dd->hidden_button), "clicked",
1050 			 G_CALLBACK(dest_show_hidden_cb), dd);
1051 	gtk_box_pack_end(GTK_BOX(hbox2), dd->hidden_button, FALSE, FALSE, 0);
1052 	gtk_widget_show(dd->hidden_button);
1053 
1054 	hbox2 = gtk_hbox_new(FALSE, PREF_PAD_GAP);
1055 	if (filter)
1056 		{
1057 		paned = gtk_hpaned_new();
1058 		gtk_table_attach(GTK_TABLE(table), paned, 0, 3, 1, 2,
1059 				 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1060 		gtk_widget_show(paned);
1061 		gtk_paned_add1(GTK_PANED(paned), hbox2);
1062 		}
1063 	else
1064 		{
1065 		paned = NULL;
1066 		gtk_table_attach(GTK_TABLE(table), hbox2, 0, 1, 1, 2,
1067 				 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1068 		}
1069 	gtk_widget_show(hbox2);
1070 
1071 	/* bookmarks */
1072 	scrolled = bookmark_list_new(NULL, dest_bookmark_select_cb, dd);
1073 	gtk_box_pack_start(GTK_BOX(hbox2), scrolled, FALSE, FALSE, 0);
1074 	gtk_widget_show(scrolled);
1075 
1076 	dd->bookmark_list = scrolled;
1077 
1078 	scrolled = gtk_scrolled_window_new(NULL, NULL);
1079 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
1080 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1081 					GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1082 	gtk_box_pack_start(GTK_BOX(hbox2), scrolled, TRUE, TRUE, 0);
1083 	gtk_widget_show(scrolled);
1084 
1085 	store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
1086 	dd->d_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1087 	g_object_unref(store);
1088 
1089 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(dd->d_view), PATH_SEL_USE_HEADINGS);
1090 
1091 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dd->d_view));
1092 	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
1093 
1094 	column = gtk_tree_view_column_new();
1095 	gtk_tree_view_column_set_title(column, _("Folders"));
1096 	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1097 
1098 	renderer = gtk_cell_renderer_text_new();
1099 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
1100 	gtk_tree_view_column_add_attribute(column, renderer, "text", 0);
1101 
1102 	gtk_tree_view_append_column(GTK_TREE_VIEW(dd->d_view), column);
1103 
1104 #if 0
1105 	/* only for debugging */
1106 	column = gtk_tree_view_column_new();
1107 	gtk_tree_view_column_set_title(column, _("Path"));
1108 	renderer = gtk_cell_renderer_text_new();
1109 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
1110 	gtk_tree_view_column_add_attribute(column, renderer, "text", 1);
1111 	gtk_tree_view_append_column(GTK_TREE_VIEW(dd->d_view), column);
1112 #endif
1113 
1114 	gtk_widget_set_size_request(dd->d_view, DEST_WIDTH, DEST_HEIGHT);
1115 	gtk_container_add(GTK_CONTAINER(scrolled), dd->d_view);
1116 	gtk_widget_show(dd->d_view);
1117 
1118 	g_signal_connect(G_OBJECT(dd->d_view), "button_press_event",
1119 			 G_CALLBACK(dest_press_cb), dd);
1120 	g_signal_connect(G_OBJECT(dd->d_view), "key_press_event",
1121 			 G_CALLBACK(dest_keypress_cb), dd);
1122 	g_signal_connect(G_OBJECT(dd->d_view), "row_activated",
1123 			 G_CALLBACK(dest_activate_cb), dd);
1124 	g_signal_connect(G_OBJECT(dd->d_view), "destroy",
1125 			 G_CALLBACK(dest_free_data), dd);
1126 
1127 	if (filter)
1128 		{
1129 		GtkListStore *store;
1130 
1131 		hbox2 = pref_table_box(table, 2, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
1132 		pref_label_new(hbox2, _("Filter:"));
1133 
1134 		store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
1135 
1136 		dd->filter_combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(store),
1137 								      FILTER_COLUMN_FILTER);
1138 		g_object_unref(store);
1139 		gtk_cell_layout_clear(GTK_CELL_LAYOUT(dd->filter_combo));
1140 		renderer = gtk_cell_renderer_text_new();
1141 		gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dd->filter_combo), renderer, TRUE);
1142 		gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dd->filter_combo), renderer,
1143 					       "text", FILTER_COLUMN_NAME, NULL);
1144 #if 0
1145 		gtk_combo_set_case_sensitive(GTK_COMBO(dd->filter_combo), TRUE);
1146 #endif
1147 		gtk_box_pack_start(GTK_BOX(hbox2), dd->filter_combo, TRUE, TRUE, 0);
1148 		gtk_widget_show(dd->filter_combo);
1149 
1150 		scrolled = gtk_scrolled_window_new(NULL, NULL);
1151 		gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
1152 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1153 					GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1154 		if (paned)
1155 			{
1156 			gtk_paned_add2(GTK_PANED(paned), scrolled);
1157 			}
1158 		else
1159 			{
1160 			gtk_table_attach(GTK_TABLE(table), scrolled, 2, 3, 1, 2,
1161 				 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1162 			}
1163 		gtk_widget_show(scrolled);
1164 
1165 		store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
1166 		dd->f_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1167 		g_object_unref(store);
1168 
1169 		gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(dd->f_view), PATH_SEL_USE_HEADINGS);
1170 
1171 		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dd->f_view));
1172 		gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
1173 
1174 		column = gtk_tree_view_column_new();
1175 		gtk_tree_view_column_set_title(column, _("Files"));
1176 		gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1177 
1178 		renderer = gtk_cell_renderer_text_new();
1179 		gtk_tree_view_column_pack_start(column, renderer, TRUE);
1180 		gtk_tree_view_column_add_attribute(column, renderer, "text", 0);
1181 
1182 		gtk_tree_view_append_column(GTK_TREE_VIEW(dd->f_view), column);
1183 
1184 		gtk_widget_set_size_request(dd->f_view, DEST_WIDTH, DEST_HEIGHT);
1185 		gtk_container_add(GTK_CONTAINER(scrolled), dd->f_view);
1186 		gtk_widget_show(dd->f_view);
1187 
1188 		g_signal_connect(G_OBJECT(dd->f_view), "button_press_event",
1189 				 G_CALLBACK(dest_press_cb), dd);
1190 		g_signal_connect(G_OBJECT(dd->f_view), "key_press_event",
1191 				 G_CALLBACK(dest_keypress_cb), dd);
1192 		g_signal_connect(G_OBJECT(dd->f_view), "row_activated",
1193 				 G_CALLBACK(dest_activate_cb), dd);
1194 		g_signal_connect(selection, "changed",
1195 				 G_CALLBACK(dest_select_cb), dd);
1196 
1197 		dest_filter_clear(dd);
1198 		dest_filter_add(dd, filter, filter_desc, TRUE);
1199 
1200 		dd->filter = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(dd->filter_combo)->child)));
1201 		}
1202 
1203 	if (path && path[0] == '/' && isdir(path))
1204 		{
1205 		dest_populate(dd, path);
1206 		}
1207 	else
1208 		{
1209 		gchar *buf = remove_level_from_path(path);
1210 		if (buf && buf[0] == '/' && isdir(buf))
1211 			{
1212 			dest_populate(dd, buf);
1213 			}
1214 		else
1215 			{
1216 			gint pos = -1;
1217 
1218 			dest_populate(dd, (gchar *)homedir());
1219 			if (path) gtk_editable_insert_text(GTK_EDITABLE(dd->entry), "/", -1, &pos);
1220 			if (path) gtk_editable_insert_text(GTK_EDITABLE(dd->entry), path, -1, &pos);
1221 			}
1222 		g_free(buf);
1223 		}
1224 
1225 	if (dd->filter_combo)
1226 		{
1227 		g_signal_connect(G_OBJECT(GTK_BIN(dd->filter_combo)->child), "changed",
1228 				 G_CALLBACK(dest_filter_changed_cb), dd);
1229 		}
1230 	g_signal_connect(G_OBJECT(dd->entry), "changed",
1231 			 G_CALLBACK(dest_entry_changed_cb), dd);
1232 
1233 	dest_dnd_init(dd);
1234 
1235 	return table;
1236 }
1237 
path_selection_new(const gchar * path,GtkWidget * entry)1238 GtkWidget *path_selection_new(const gchar *path, GtkWidget *entry)
1239 {
1240 	return path_selection_new_with_files(entry, path, NULL, NULL);
1241 }
1242 
path_selection_sync_to_entry(GtkWidget * entry)1243 void path_selection_sync_to_entry(GtkWidget *entry)
1244 {
1245 	Dest_Data *dd = g_object_get_data(G_OBJECT(entry), "destination_data");
1246 	const gchar *path;
1247 
1248 	if (!dd) return;
1249 
1250 	path = gtk_entry_get_text(GTK_ENTRY(entry));
1251 
1252 	if (isdir(path) && strcmp(path, dd->path) != 0)
1253 		{
1254 		dest_populate(dd, path);
1255 		}
1256 	else
1257 		{
1258 		gchar *buf = remove_level_from_path(path);
1259 		if (isdir(buf) && strcmp(buf, dd->path) != 0)
1260 			{
1261 			dest_populate(dd, buf);
1262 			}
1263 		g_free(buf);
1264 		}
1265 }
1266 
path_selection_add_select_func(GtkWidget * entry,void (* func)(const gchar *,gpointer),gpointer data)1267 void path_selection_add_select_func(GtkWidget *entry,
1268 				    void (*func)(const gchar *, gpointer), gpointer data)
1269 {
1270 	Dest_Data *dd = g_object_get_data(G_OBJECT(entry), "destination_data");
1271 
1272 	if (!dd) return;
1273 
1274 	dd->select_func = func;
1275 	dd->select_data = data;
1276 }
1277 
path_selection_add_filter(GtkWidget * entry,const gchar * filter,const gchar * description,gint set)1278 void path_selection_add_filter(GtkWidget *entry, const gchar *filter, const gchar *description, gint set)
1279 {
1280 	Dest_Data *dd = g_object_get_data(G_OBJECT(entry), "destination_data");
1281 
1282 	if (!dd) return;
1283 	if (!filter) return;
1284 
1285 	dest_filter_add(dd, filter, description, set);
1286 }
1287 
path_selection_clear_filter(GtkWidget * entry)1288 void path_selection_clear_filter(GtkWidget *entry)
1289 {
1290 	Dest_Data *dd = g_object_get_data(G_OBJECT(entry), "destination_data");
1291 
1292 	if (!dd) return;
1293 
1294 	dest_filter_clear(dd);
1295 }
1296 
1297