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