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