replaced directory path with FileData* dir_fd
[geeqie.git] / src / view_dir.c
1 /*
2  * Geeqie
3  * Copyright (C) 2008 The Geeqie Team
4  *
5  * Author: Laurent Monin
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 #include "main.h"
13 #include "view_dir.h"
14
15 #include "dnd.h"
16 #include "dupe.h"
17 #include "filedata.h"
18 #include "layout_image.h"
19 #include "layout_util.h"
20 #include "ui_bookmark.h"
21 #include "ui_fileops.h"
22 #include "ui_tree_edit.h"
23 #include "ui_menu.h"
24 #include "utilops.h"
25 #include "editors.h"
26 #include "view_dir_list.h"
27 #include "view_dir_tree.h"
28
29 GtkRadioActionEntry menu_view_dir_radio_entries[] = {
30   { "FolderList",       NULL,           N_("_List"),            "<meta>L",      NULL, DIRVIEW_LIST },
31   { "FolderTree",       NULL,           N_("_Tree"),            "<control>T",   NULL, DIRVIEW_TREE },
32 };
33
34 static void vd_destroy_cb(GtkWidget *widget, gpointer data)
35 {
36         ViewDir *vd = data;
37
38         if (vd->popup)
39                 {
40                 g_signal_handlers_disconnect_matched(G_OBJECT(vd->popup), G_SIGNAL_MATCH_DATA,
41                                                      0, 0, 0, NULL, vd);
42                 gtk_widget_destroy(vd->popup);
43                 }
44
45         switch(vd->type)
46         {
47         case DIRVIEW_LIST: vdlist_destroy_cb(widget, data); break;
48         case DIRVIEW_TREE: vdtree_destroy_cb(widget, data); break;
49         }
50
51         if (vd->pf) folder_icons_free(vd->pf);
52         if (vd->drop_list) filelist_free(vd->drop_list);
53
54         if (vd->dir_fd) file_data_unref(vd->dir_fd);
55         if (vd->info) g_free(vd->info);
56
57         g_free(vd);
58 }
59
60 ViewDir *vd_new(DirViewType type, FileData *dir_fd)
61 {
62         g_assert(VIEW_DIR_TYPES_COUNT <= G_N_ELEMENTS(menu_view_dir_radio_entries));
63
64         ViewDir *vd = g_new0(ViewDir, 1);
65
66         vd->dir_fd = NULL;
67         vd->click_fd = NULL;
68
69         vd->drop_fd = NULL;
70         vd->drop_list = NULL;
71         vd->drop_scroll_id = -1;
72         vd->drop_list = NULL;
73
74         vd->popup = NULL;
75
76         vd->dnd_drop_leave_func = NULL;
77         vd->dnd_drop_update_func = NULL;
78
79         vd->widget = gtk_scrolled_window_new(NULL, NULL);
80         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(vd->widget), GTK_SHADOW_IN);
81         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vd->widget),
82                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
83
84         vd->pf = folder_icons_new();
85
86         switch(type)
87         {
88         case DIRVIEW_LIST: vd = vdlist_new(vd, dir_fd); break;
89         case DIRVIEW_TREE: vd = vdtree_new(vd, dir_fd); break;
90         }
91
92         gtk_container_add(GTK_CONTAINER(vd->widget), vd->view);
93
94         vd_dnd_init(vd);
95
96         g_signal_connect(G_OBJECT(vd->view), "row_activated",
97                          G_CALLBACK(vd_activate_cb), vd);
98         g_signal_connect(G_OBJECT(vd->widget), "destroy",
99                          G_CALLBACK(vd_destroy_cb), vd);
100         g_signal_connect(G_OBJECT(vd->view), "key_press_event",
101                          G_CALLBACK(vd_press_key_cb), vd);
102         g_signal_connect(G_OBJECT(vd->view), "button_press_event",
103                          G_CALLBACK(vd_press_cb), vd);
104         g_signal_connect(G_OBJECT(vd->view), "button_release_event",
105                          G_CALLBACK(vd_release_cb), vd);
106
107         if (dir_fd) vd_set_fd(vd, dir_fd);
108
109         gtk_widget_show(vd->view);
110
111         return vd;
112 }
113
114 void vd_set_select_func(ViewDir *vd,
115                         void (*func)(ViewDir *vd, const gchar *path, gpointer data), gpointer data)
116 {
117         vd->select_func = func;
118         vd->select_data = data;
119 }
120
121 void vd_set_layout(ViewDir *vd, LayoutWindow *layout)
122 {
123         vd->layout = layout;
124 }
125
126 gint vd_set_fd(ViewDir *vd, FileData *dir_fd)
127 {
128         gint ret = FALSE;
129
130         switch(vd->type)
131         {
132         case DIRVIEW_LIST: ret = vdlist_set_fd(vd, dir_fd); break;
133         case DIRVIEW_TREE: ret = vdtree_set_fd(vd, dir_fd); break;
134         }
135
136         return ret;
137 }
138
139 void vd_refresh(ViewDir *vd)
140 {
141         switch(vd->type)
142         {
143         case DIRVIEW_LIST: return vdlist_refresh(vd);
144         case DIRVIEW_TREE: return vdtree_refresh(vd);
145         }
146 }
147
148 const gchar *vd_row_get_path(ViewDir *vd, gint row)
149 {
150         const gchar *ret = NULL;
151
152         switch(vd->type)
153         {
154         case DIRVIEW_LIST: ret = vdlist_row_get_path(vd, row); break;
155         case DIRVIEW_TREE: ret = vdtree_row_get_path(vd, row); break;
156         }
157
158         return ret;
159 }
160
161 void vd_select_row(ViewDir *vd, FileData *fd)
162 {
163         switch(vd->type)
164         {
165         case DIRVIEW_LIST: vdlist_select_row(vd, fd); break;
166         case DIRVIEW_TREE: vdtree_select_row(vd, fd); break;
167         }
168 }
169
170 gint vd_find_row(ViewDir *vd, FileData *fd, GtkTreeIter *iter)
171 {
172         gint ret = FALSE;
173
174         switch(vd->type)
175         {
176         case DIRVIEW_LIST: ret = vdlist_find_row(vd, fd, iter); break;
177         case DIRVIEW_TREE: ret = vdtree_find_row(vd, fd, iter, NULL); break;
178         }
179
180         return ret;
181 }
182
183 FileData *vd_get_fd_from_tree_path(ViewDir *vd, GtkTreeView *tview, GtkTreePath *tpath)
184 {
185         GtkTreeIter iter;
186         FileData *fd = NULL;
187         GtkTreeModel *store;
188
189         store = gtk_tree_view_get_model(tview);
190         gtk_tree_model_get_iter(store, &iter, tpath);
191         switch (vd->type)
192                 {
193                 case DIRVIEW_LIST:
194                         gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
195                         break;
196                 case DIRVIEW_TREE:
197                         {
198                         NodeData *nd;
199                         gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
200                         fd = (nd) ? nd->fd : NULL;
201                         };
202                         break;
203                 }
204
205         return fd;
206 }
207
208 static gint vd_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
209 {
210         ViewDir *vd = data;
211         FileData *fd;
212         gchar *old_path;
213         gchar *new_path;
214         gchar *base;
215
216         fd = vd_get_fd_from_tree_path(vd, GTK_TREE_VIEW(vd->view), td->path);
217         if (!fd) return FALSE;
218
219         base = remove_level_from_path(old_path);
220         new_path = g_build_filename(base, new, NULL);
221         g_free(base);
222
223         if (file_util_rename_dir(fd, new_path, vd->view))
224                 {
225
226                 if (vd->type == DIRVIEW_TREE) vdtree_populate_path(vd, fd, TRUE, TRUE);
227                 if (vd->layout && vd->dir_fd != fd) /* FIXME */
228                         {
229                         layout_set_path(vd->layout, new_path);
230                         }
231                 else
232                         {
233                         if (vd->type == DIRVIEW_LIST) vd_refresh(vd);
234                         }
235                 }
236
237         g_free(new_path);
238
239         return FALSE;
240 }
241
242 static void vd_rename_by_data(ViewDir *vd, FileData *fd)
243 {
244         GtkTreeModel *store;
245         GtkTreePath *tpath;
246         GtkTreeIter iter;
247
248         if (!fd || vd_find_row(vd, fd, &iter) < 0) return;
249         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
250         tpath = gtk_tree_model_get_path(store, &iter);
251
252         tree_edit_by_path(GTK_TREE_VIEW(vd->view), tpath, 0, fd->name,
253                           vd_rename_cb, vd);
254         gtk_tree_path_free(tpath);
255 }
256
257
258 void vd_color_set(ViewDir *vd, FileData *fd, gint color_set)
259 {
260         GtkTreeModel *store;
261         GtkTreeIter iter;
262
263         if (vd_find_row(vd, fd, &iter) < 0) return;
264         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
265
266         switch(vd->type)
267         {
268         case DIRVIEW_LIST:
269                 gtk_list_store_set(GTK_LIST_STORE(store), &iter, DIR_COLUMN_COLOR, color_set, -1);
270                 break;
271         case DIRVIEW_TREE:
272                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, DIR_COLUMN_COLOR, color_set, -1);
273                 break;
274         }
275 }
276
277 void vd_popup_destroy_cb(GtkWidget *widget, gpointer data)
278 {
279         ViewDir *vd = data;
280
281         vd_color_set(vd, vd->click_fd, FALSE);
282         vd->click_fd = NULL;
283         vd->popup = NULL;
284
285         vd_color_set(vd, vd->drop_fd, FALSE);
286         filelist_free(vd->drop_list);
287         vd->drop_list = NULL;
288         vd->drop_fd = NULL;
289 }
290
291 /*
292  *-----------------------------------------------------------------------------
293  * drop menu (from dnd)
294  *-----------------------------------------------------------------------------
295  */
296
297 static void vd_drop_menu_copy_cb(GtkWidget *widget, gpointer data)
298 {
299         ViewDir *vd = data;
300         const gchar *path;
301         GList *list;
302
303         if (!vd->drop_fd) return;
304
305         path = vd->drop_fd->path;
306         list = vd->drop_list;
307         vd->drop_list = NULL;
308
309         file_util_copy_simple(list, path, vd->widget);
310 }
311
312 static void vd_drop_menu_move_cb(GtkWidget *widget, gpointer data)
313 {
314         ViewDir *vd = data;
315         const gchar *path;
316         GList *list;
317
318         if (!vd->drop_fd) return;
319
320         path = vd->drop_fd->path;
321         list = vd->drop_list;
322
323         vd->drop_list = NULL;
324
325         file_util_move_simple(list, path, vd->widget);
326 }
327
328 static void vd_drop_menu_filter_cb(GtkWidget *widget, gpointer data)
329 {
330         ViewDir *vd = data;
331         const gchar *path;
332         GList *list;
333         guint n;
334
335         if (!vd->drop_fd) return;
336         
337         n = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "filter_idx"));
338         if (n == 0) return;
339         n--;
340
341         path = vd->drop_fd->path;
342         list = vd->drop_list;
343
344         vd->drop_list = NULL;
345
346         file_util_start_filter_from_filelist(n, list, path, vd->widget);
347 }
348
349
350
351 GtkWidget *vd_drop_menu(ViewDir *vd, gint active)
352 {
353         GtkWidget *menu;
354         guint i;
355
356         menu = popup_menu_short_lived();
357         g_signal_connect(G_OBJECT(menu), "destroy",
358                          G_CALLBACK(vd_popup_destroy_cb), vd);
359
360         menu_item_add_stock_sensitive(menu, _("_Copy"), GTK_STOCK_COPY, active,
361                                       G_CALLBACK(vd_drop_menu_copy_cb), vd);
362         menu_item_add_sensitive(menu, _("_Move"), active, G_CALLBACK(vd_drop_menu_move_cb), vd);
363
364         for (i = 0; i < GQ_EDITOR_GENERIC_SLOTS; i++)
365                 {
366                 GtkWidget *item;
367
368                 const gchar *name = editor_get_name(i);
369                 if (!name || !editor_is_filter(i)) continue;
370
371                 item = menu_item_add_sensitive(menu, name, active, G_CALLBACK(vd_drop_menu_filter_cb), vd);
372
373                 g_object_set_data(G_OBJECT(item), "filter_idx", GUINT_TO_POINTER(i + 1));
374                 }
375
376         menu_item_add_divider(menu);
377         menu_item_add_stock(menu, _("Cancel"), GTK_STOCK_CANCEL, NULL, vd);
378
379         return menu;
380 }
381
382 /*
383  *-----------------------------------------------------------------------------
384  * pop-up menu
385  *-----------------------------------------------------------------------------
386  */
387
388 static void vd_pop_menu_up_cb(GtkWidget *widget, gpointer data)
389 {
390         ViewDir *vd = data;
391         gchar *path;
392
393         if (!vd->dir_fd || strcmp(vd->dir_fd->path, G_DIR_SEPARATOR_S) == 0) return;
394         path = remove_level_from_path(vd->dir_fd->path);
395
396         if (vd->select_func)
397                 {
398                 vd->select_func(vd, path, vd->select_data);
399                 }
400
401         g_free(path);
402 }
403
404 static void vd_pop_menu_slide_cb(GtkWidget *widget, gpointer data)
405 {
406         ViewDir *vd = data;
407
408         if (!vd->layout) return;
409         if (!vd->click_fd) return;
410
411         layout_set_fd(vd->layout, vd->click_fd);
412         layout_select_none(vd->layout);
413         layout_image_slideshow_stop(vd->layout);
414         layout_image_slideshow_start(vd->layout);
415 }
416
417 static void vd_pop_menu_slide_rec_cb(GtkWidget *widget, gpointer data)
418 {
419         ViewDir *vd = data;
420         GList *list;
421
422         if (!vd->layout) return;
423         if (!vd->click_fd) return;
424
425         list = filelist_recursive(vd->click_fd);
426
427         layout_image_slideshow_stop(vd->layout);
428         layout_image_slideshow_start_from_list(vd->layout, list);
429 }
430
431 static void vd_pop_menu_dupe(ViewDir *vd, gint recursive)
432 {
433         DupeWindow *dw;
434         GList *list = NULL;
435
436         if (!vd->click_fd) return;
437
438         if (recursive)
439                 {
440                 list = g_list_append(list, file_data_ref(vd->click_fd));
441                 }
442         else
443                 {
444                 filelist_read(vd->click_fd, &list, NULL);
445                 list = filelist_filter(list, FALSE);
446                 }
447
448         dw = dupe_window_new(DUPE_MATCH_NAME);
449         dupe_window_add_files(dw, list, recursive);
450
451         filelist_free(list);
452 }
453
454 static void vd_pop_menu_dupe_cb(GtkWidget *widget, gpointer data)
455 {
456         ViewDir *vd = data;
457         vd_pop_menu_dupe(vd, FALSE);
458 }
459
460 static void vd_pop_menu_dupe_rec_cb(GtkWidget *widget, gpointer data)
461 {
462         ViewDir *vd = data;
463         vd_pop_menu_dupe(vd, TRUE);
464 }
465
466 static void vd_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
467 {
468         ViewDir *vd = data;
469
470         if (!vd->click_fd) return;
471         file_util_delete_dir(vd->click_fd, vd->widget);
472 }
473
474 static void vd_pop_menu_copy_path_cb(GtkWidget *widget, gpointer data)
475 {
476         ViewDir *vd = data;
477
478         if (!vd->click_fd) return;
479
480         file_util_copy_path_to_clipboard(vd->click_fd);
481 }
482
483 #define VIEW_DIR_AS_SUBMENU_KEY "view_dir_as_submenu"
484 static void vd_pop_submenu_dir_view_as_cb(GtkWidget *widget, gpointer data)
485 {
486         ViewDir *vd = data;
487
488         DirViewType new_type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), VIEW_DIR_AS_SUBMENU_KEY));
489         layout_views_set(vd->layout, new_type, vd->layout->file_view_type);
490 }
491
492 static void vd_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
493 {
494         ViewDir *vd = data;
495
496         if (vd->layout) layout_refresh(vd->layout);
497 }
498
499 static void vd_toggle_show_hidden_files_cb(GtkWidget *widget, gpointer data)
500 {
501         ViewDir *vd = data;
502
503         options->file_filter.show_hidden_files = !options->file_filter.show_hidden_files;
504         if (vd->layout) layout_refresh(vd->layout);
505 }
506
507 static void vd_pop_menu_new_cb(GtkWidget *widget, gpointer data)
508 {
509         ViewDir *vd = data;
510         const gchar *path = NULL;
511         gchar *new_path;
512         gchar *buf;
513
514         switch(vd->type)
515                 {
516                 case DIRVIEW_LIST:
517                         {
518                         if (!vd->dir_fd) return;
519                         path = vd->dir_fd->path;
520                         };
521                         break;
522                 case DIRVIEW_TREE:
523                         {
524                         if (!vd->click_fd) return;
525                         path = vd->click_fd->path;
526                         };
527                         break;
528                 }
529
530         buf = g_build_filename(path, _("new_folder"), NULL);
531         new_path = unique_filename(buf, NULL, NULL, FALSE);
532         g_free(buf);
533         if (!new_path) return;
534
535         if (!mkdir_utf8(new_path, 0755))
536                 {
537                 gchar *text;
538
539                 text = g_strdup_printf(_("Unable to create folder:\n%s"), new_path);
540                 file_util_warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, vd->view);
541                 g_free(text);
542                 }
543         else
544                 {
545                 FileData *fd = NULL;
546
547                 switch(vd->type)
548                         {
549                         case DIRVIEW_LIST:
550                                 {
551                                 vd_refresh(vd);
552                                 fd = vdlist_row_by_path(vd, new_path, NULL);
553                                 };
554                                 break;
555                         case DIRVIEW_TREE:
556                                 {
557                                 FileData *new_fd = file_data_new_simple(new_path);
558                                 fd = vdtree_populate_path(vd, new_fd, TRUE, TRUE);
559                                 file_data_unref(new_fd);
560                                 }
561                                 break;
562                         }
563                 vd_rename_by_data(vd, fd);
564                 }
565
566         g_free(new_path);
567 }
568
569 static void vd_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
570 {
571         ViewDir *vd = data;
572
573         vd_rename_by_data(vd, vd->click_fd);
574 }
575
576 GtkWidget *vd_pop_menu(ViewDir *vd, FileData *fd)
577 {
578         GtkWidget *menu;
579         GtkWidget *submenu;
580         GtkWidget *item;
581         gint active;
582         gint rename_delete_active = FALSE;
583         gint new_folder_active = FALSE;
584         gint i;
585
586         active = (fd != NULL);
587         switch(vd->type)
588                 {
589                 case DIRVIEW_LIST:
590                         {
591                         /* check using . (always row 0) */
592                         new_folder_active = (vd->dir_fd && access_file(vd->dir_fd->path , W_OK | X_OK));
593
594                         /* ignore .. and . */
595                         rename_delete_active = (new_folder_active && fd &&
596                                 strcmp(fd->name, ".") != 0 &&
597                                 strcmp(fd->name, "..") != 0 &&
598                                 access_file(fd->path, W_OK | X_OK));
599                         };
600                         break;
601                 case DIRVIEW_TREE:
602                         {
603                         if (fd)
604                                 {
605                                 gchar *parent;
606                                 new_folder_active = (fd && access_file(fd->path, W_OK | X_OK));
607                                 parent = remove_level_from_path(fd->path);
608                                 rename_delete_active = access_file(parent, W_OK | X_OK);
609                                 g_free(parent);
610                                 };
611                         }
612                         break;
613                 }
614
615         menu = popup_menu_short_lived();
616         g_signal_connect(G_OBJECT(menu), "destroy",
617                          G_CALLBACK(vd_popup_destroy_cb), vd);
618
619         menu_item_add_stock_sensitive(menu, _("_Up to parent"), GTK_STOCK_GO_UP,
620                                       (vd->dir_fd && strcmp(vd->dir_fd->path, G_DIR_SEPARATOR_S) != 0),
621                                       G_CALLBACK(vd_pop_menu_up_cb), vd);
622
623         menu_item_add_divider(menu);
624         menu_item_add_sensitive(menu, _("_Slideshow"), active,
625                                 G_CALLBACK(vd_pop_menu_slide_cb), vd);
626         menu_item_add_sensitive(menu, _("Slideshow recursive"), active,
627                                 G_CALLBACK(vd_pop_menu_slide_rec_cb), vd);
628
629         menu_item_add_divider(menu);
630         menu_item_add_stock_sensitive(menu, _("Find _duplicates..."), GTK_STOCK_FIND, active,
631                                       G_CALLBACK(vd_pop_menu_dupe_cb), vd);
632         menu_item_add_stock_sensitive(menu, _("Find duplicates recursive..."), GTK_STOCK_FIND, active,
633                                       G_CALLBACK(vd_pop_menu_dupe_rec_cb), vd);
634
635         menu_item_add_divider(menu);
636
637         menu_item_add_sensitive(menu, _("_New folder..."), new_folder_active,
638                                 G_CALLBACK(vd_pop_menu_new_cb), vd);
639
640         menu_item_add_sensitive(menu, _("_Rename..."), rename_delete_active,
641                                 G_CALLBACK(vd_pop_menu_rename_cb), vd);
642         menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, rename_delete_active,
643                                       G_CALLBACK(vd_pop_menu_delete_cb), vd);
644
645         if (options->show_copy_path)
646                 menu_item_add(menu, _("_Copy path"), 
647                               G_CALLBACK(vd_pop_menu_copy_path_cb), vd);
648
649         menu_item_add_divider(menu);
650
651         item = menu_item_add(menu, _("_View as"), NULL, NULL);
652         submenu = gtk_menu_new();
653         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
654
655         for (i = 0; i < VIEW_DIR_TYPES_COUNT; i++)
656                 {
657                 item = menu_item_add_check(submenu, _(menu_view_dir_radio_entries[i].label),
658                                            ((gint) vd->type == menu_view_dir_radio_entries[i].value),
659                                            G_CALLBACK(vd_pop_submenu_dir_view_as_cb), vd);
660                 g_object_set_data(G_OBJECT(item), VIEW_DIR_AS_SUBMENU_KEY, GINT_TO_POINTER(menu_view_dir_radio_entries[i].value));
661                 }
662
663         menu_item_add_check(menu, _("Show _hidden files"), options->file_filter.show_hidden_files,
664                             G_CALLBACK(vd_toggle_show_hidden_files_cb), vd);
665
666         menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH,
667                             G_CALLBACK(vd_pop_menu_refresh_cb), vd);
668
669         return menu;
670 }
671
672 /*
673  *-----------------------------------------------------------------------------
674  * dnd
675  *-----------------------------------------------------------------------------
676  */
677
678 static GtkTargetEntry vd_dnd_drop_types[] = {
679         { "text/uri-list", 0, TARGET_URI_LIST }
680 };
681 static gint vd_dnd_drop_types_count = 1;
682
683 static void vd_dest_set(ViewDir *vd, gint enable)
684 {
685         if (enable)
686                 {
687                 gtk_drag_dest_set(vd->view,
688                                   GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
689                                   vd_dnd_drop_types, vd_dnd_drop_types_count,
690                                   GDK_ACTION_MOVE | GDK_ACTION_COPY);
691                 }
692         else
693                 {
694                 gtk_drag_dest_unset(vd->view);
695                 }
696 }
697
698 static void vd_dnd_get(GtkWidget *widget, GdkDragContext *context,
699                            GtkSelectionData *selection_data, guint info,
700                            guint time, gpointer data)
701 {
702         ViewDir *vd = data;
703         GList *list;
704         gchar *uritext = NULL;
705         gint length = 0;
706
707         if (!vd->click_fd) return;
708
709         switch (info)
710                 {
711                 case TARGET_URI_LIST:
712                 case TARGET_TEXT_PLAIN:
713                         list = g_list_prepend(NULL, vd->click_fd);
714                         uritext = uri_text_from_filelist(list, &length, (info == TARGET_TEXT_PLAIN));
715                         g_list_free(list);
716                         break;
717                 }
718         if (uritext)
719                 {
720                 gtk_selection_data_set(selection_data, selection_data->target,
721                                        8, (guchar *)uritext, length);
722                 g_free(uritext);
723                 }
724 }
725
726 static void vd_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
727 {
728         ViewDir *vd = data;
729
730         vd_color_set(vd, vd->click_fd, TRUE);
731         vd_dest_set(vd, FALSE);
732 }
733
734 static void vd_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
735 {
736         ViewDir *vd = data;
737
738         vd_color_set(vd, vd->click_fd, FALSE);
739
740         if (vd->type == DIRVIEW_LIST && context->action == GDK_ACTION_MOVE)
741                 {
742                 vd_refresh(vd);
743                 }
744         vd_dest_set(vd, TRUE);
745 }
746
747 static void vd_dnd_drop_receive(GtkWidget *widget,
748                                 GdkDragContext *context, gint x, gint y,
749                                 GtkSelectionData *selection_data, guint info,
750                                 guint time, gpointer data)
751 {
752         ViewDir *vd = data;
753         GtkTreePath *tpath;
754         FileData *fd = NULL;
755
756         vd->click_fd = NULL;
757
758         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y,
759                                           &tpath, NULL, NULL, NULL))
760                 {
761                 fd = vd_get_fd_from_tree_path(vd, GTK_TREE_VIEW(widget), tpath);
762                 gtk_tree_path_free(tpath);
763                 }
764
765         if (!fd) return;
766
767         if (info == TARGET_URI_LIST)
768                 {
769                 GList *list;
770                 gint active;
771                 gint done = FALSE;
772
773                 list = uri_filelist_from_text((gchar *)selection_data->data, TRUE);
774                 if (!list) return;
775
776                 active = access_file(fd->path, W_OK | X_OK);
777
778                 vd_color_set(vd, fd, TRUE);
779
780                 if (active)
781                         {
782                         if (context->actions == GDK_ACTION_COPY)
783                                 {
784                                 file_util_copy_simple(list, fd->path, vd->widget);
785                                 done = TRUE;
786                                 }
787                         else if (context->actions == GDK_ACTION_MOVE)
788                                 {
789                                 file_util_move_simple(list, fd->path, vd->widget);
790                                 done = TRUE;
791                                 }
792                         }
793
794                 if (done == FALSE)
795                         {
796                         vd->popup = vd_drop_menu(vd, active);
797                         gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, NULL, NULL, 0, time);
798                         }
799
800                 vd->drop_fd = fd;
801                 vd->drop_list = list;
802                 }
803 }
804
805 static void vd_dnd_drop_update(ViewDir *vd, gint x, gint y)
806 {
807         GtkTreePath *tpath;
808         FileData *fd = NULL;
809
810         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vd->view), x, y,
811                                           &tpath, NULL, NULL, NULL))
812                 {
813                 fd = vd_get_fd_from_tree_path(vd, GTK_TREE_VIEW(vd->view), tpath);
814                 gtk_tree_path_free(tpath);
815                 }
816
817         if (fd != vd->drop_fd)
818                 {
819                 vd_color_set(vd, vd->drop_fd, FALSE);
820                 vd_color_set(vd, fd, TRUE);
821                 if (fd && vd->dnd_drop_update_func) vd->dnd_drop_update_func(vd);
822                 }
823
824         vd->drop_fd = fd;
825 }
826
827 void vd_dnd_drop_scroll_cancel(ViewDir *vd)
828 {
829         if (vd->drop_scroll_id != -1) g_source_remove(vd->drop_scroll_id);
830         vd->drop_scroll_id = -1;
831 }
832
833 static gint vd_auto_scroll_idle_cb(gpointer data)
834 {
835         ViewDir *vd = data;
836
837         if (vd->drop_fd)
838                 {
839                 GdkWindow *window;
840                 gint x, y;
841                 gint w, h;
842
843                 window = vd->view->window;
844                 gdk_window_get_pointer(window, &x, &y, NULL);
845                 gdk_drawable_get_size(window, &w, &h);
846                 if (x >= 0 && x < w && y >= 0 && y < h)
847                         {
848                         vd_dnd_drop_update(vd, x, y);
849                         }
850                 }
851
852         vd->drop_scroll_id = -1;
853         return FALSE;
854 }
855
856 static gint vd_auto_scroll_notify_cb(GtkWidget *widget, gint x, gint y, gpointer data)
857 {
858         ViewDir *vd = data;
859
860         if (!vd->drop_fd || vd->drop_list) return FALSE;
861
862         if (vd->drop_scroll_id == -1) vd->drop_scroll_id = g_idle_add(vd_auto_scroll_idle_cb, vd);
863
864         return TRUE;
865 }
866
867 static gint vd_dnd_drop_motion(GtkWidget *widget, GdkDragContext *context,
868                                    gint x, gint y, guint time, gpointer data)
869 {
870         ViewDir *vd = data;
871
872         vd->click_fd = NULL;
873
874         if (gtk_drag_get_source_widget(context) == vd->view)
875                 {
876                 /* from same window */
877                 gdk_drag_status(context, 0, time);
878                 return TRUE;
879                 }
880         else
881                 {
882                 gdk_drag_status(context, context->suggested_action, time);
883                 }
884
885         vd_dnd_drop_update(vd, x, y);
886
887         if (vd->drop_fd)
888                 {
889                 GtkAdjustment *adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vd->view));
890                 widget_auto_scroll_start(vd->view, adj, -1, -1, vd_auto_scroll_notify_cb, vd);
891                 }
892
893         return FALSE;
894 }
895
896 static void vd_dnd_drop_leave(GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
897 {
898         ViewDir *vd = data;
899
900         if (vd->drop_fd != vd->click_fd) vd_color_set(vd, vd->drop_fd, FALSE);
901
902         vd->drop_fd = NULL;
903
904         if (vd->dnd_drop_leave_func) vd->dnd_drop_leave_func(vd);
905 }
906
907 void vd_dnd_init(ViewDir *vd)
908 {
909         gtk_drag_source_set(vd->view, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
910                             dnd_file_drag_types, dnd_file_drag_types_count,
911                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
912         g_signal_connect(G_OBJECT(vd->view), "drag_data_get",
913                          G_CALLBACK(vd_dnd_get), vd);
914         g_signal_connect(G_OBJECT(vd->view), "drag_begin",
915                          G_CALLBACK(vd_dnd_begin), vd);
916         g_signal_connect(G_OBJECT(vd->view), "drag_end",
917                          G_CALLBACK(vd_dnd_end), vd);
918
919         vd_dest_set(vd, TRUE);
920         g_signal_connect(G_OBJECT(vd->view), "drag_data_received",
921                          G_CALLBACK(vd_dnd_drop_receive), vd);
922         g_signal_connect(G_OBJECT(vd->view), "drag_motion",
923                          G_CALLBACK(vd_dnd_drop_motion), vd);
924         g_signal_connect(G_OBJECT(vd->view), "drag_leave",
925                          G_CALLBACK(vd_dnd_drop_leave), vd);
926 }
927
928 /*
929  *----------------------------------------------------------------------------
930  * callbacks
931  *----------------------------------------------------------------------------
932  */
933
934 void vd_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
935 {
936         ViewDir *vd = data;
937         GtkTreeModel *store;
938         GtkTreeIter iter;
939         GtkTreePath *tpath;
940         gint cw, ch;
941
942         if (vd_find_row(vd, vd->click_fd, &iter) < 0) return;
943         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
944         tpath = gtk_tree_model_get_path(store, &iter);
945         tree_view_get_cell_clamped(GTK_TREE_VIEW(vd->view), tpath, 0, TRUE, x, y, &cw, &ch);
946         gtk_tree_path_free(tpath);
947         *y += ch;
948         popup_menu_position_clamp(menu, x, y, 0);
949 }
950
951 void vd_activate_cb(GtkTreeView *tview, GtkTreePath *tpath, GtkTreeViewColumn *column, gpointer data)
952 {
953         ViewDir *vd = data;
954         FileData *fd = vd_get_fd_from_tree_path(vd, tview, tpath);
955
956         vd_select_row(vd, fd);
957 }
958
959 static GdkColor *vd_color_shifted(GtkWidget *widget)
960 {
961         static GdkColor color;
962         static GtkWidget *done = NULL;
963
964         if (done != widget)
965                 {
966                 GtkStyle *style;
967
968                 style = gtk_widget_get_style(widget);
969                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
970                 shift_color(&color, -1, 0);
971                 done = widget;
972                 }
973
974         return &color;
975 }
976
977 void vd_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
978                  GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
979 {
980         ViewDir *vd = data;
981         gboolean set;
982
983         gtk_tree_model_get(tree_model, iter, DIR_COLUMN_COLOR, &set, -1);
984         g_object_set(G_OBJECT(cell),
985                      "cell-background-gdk", vd_color_shifted(vd->view),
986                      "cell-background-set", set, NULL);
987 }
988
989 gint vd_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
990 {
991         ViewDir *vd = data;
992         GtkTreePath *tpath;
993         FileData *fd = NULL;
994
995         if (!vd->click_fd) return FALSE;
996         vd_color_set(vd, vd->click_fd, FALSE);
997
998         if (bevent->button != MOUSE_BUTTON_LEFT) return TRUE;
999
1000         if ((bevent->x != 0 || bevent->y != 0) &&
1001             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
1002                                           &tpath, NULL, NULL, NULL))
1003                 {
1004                 fd = vd_get_fd_from_tree_path(vd, GTK_TREE_VIEW(widget), tpath);
1005                 gtk_tree_path_free(tpath);
1006                 }
1007
1008         if (fd && vd->click_fd == fd)
1009                 {
1010                 vdlist_select_row(vd, vd->click_fd);
1011                 }
1012
1013         return FALSE;
1014 }
1015
1016 gint vd_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1017 {
1018         ViewDir *vd = data;
1019         gint ret = FALSE;
1020
1021         switch(vd->type)
1022         {
1023         case DIRVIEW_LIST: ret = vdlist_press_key_cb(widget, event, data); break;
1024         case DIRVIEW_TREE: ret = vdtree_press_key_cb(widget, event, data); break;
1025         }
1026
1027         return ret;
1028 }
1029
1030 gint vd_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1031 {
1032         ViewDir *vd = data;
1033         gint ret = FALSE;
1034
1035         switch(vd->type)
1036         {
1037         case DIRVIEW_LIST: ret = vdlist_press_cb(widget, bevent, data); break;
1038         case DIRVIEW_TREE: ret = vdtree_press_cb(widget, bevent, data); break;
1039         }
1040
1041         return ret;
1042 }