comparison src/ui_pathsel.c @ 9:d907d608745f

Sync to GQview 1.5.9 release. ######## DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS! This CVS is never up to date with current development and is provided solely for reference purposes, please use the latest official release package when making any changes or translation updates. ########
author gqview
date Sat, 26 Feb 2005 00:13:35 +0000
parents
children 606fcf461a68
comparison
equal deleted inserted replaced
8:e0d0593d519e 9:d907d608745f
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
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
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
144 static gint dest_sort_cb(void *a, void *b)
145 {
146 return CASE_SORT((gchar *)a, (gchar *)b);
147 }
148
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1238 GtkWidget *path_selection_new(const gchar *path, GtkWidget *entry)
1239 {
1240 return path_selection_new_with_files(entry, path, NULL, NULL);
1241 }
1242
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
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
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
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