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