Move some dnd common code from view_dir_list.c and view_dir_tree.c
[geeqie.git] / src / view_dir_list.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12 #include "main.h"
13 #include "view_dir_list.h"
14
15 #include "dnd.h"
16 #include "dupe.h"
17 #include "filelist.h"
18 #include "layout.h"
19 #include "layout_image.h"
20 #include "layout_util.h"
21 #include "utilops.h"
22 #include "ui_bookmark.h"
23 #include "ui_fileops.h"
24 #include "ui_menu.h"
25 #include "ui_tree_edit.h"
26 #include "view_dir.h"
27
28 #include <gdk/gdkkeysyms.h> /* for keyboard values */
29
30
31 #define VDLIST_PAD 4
32
33 #define VDLIST_INFO(_vd_, _part_) (((ViewDirInfoList *)(_vd_->info))->_part_)
34
35
36 static void vdlist_popup_destroy_cb(GtkWidget *widget, gpointer data);
37 static gint vdlist_auto_scroll_notify_cb(GtkWidget *widget, gint x, gint y, gpointer data);
38
39 /*
40  *-----------------------------------------------------------------------------
41  * misc
42  *-----------------------------------------------------------------------------
43  */
44
45 gint vdlist_find_row(ViewDir *vd, FileData *fd, GtkTreeIter *iter)
46 {
47         GtkTreeModel *store;
48         gint valid;
49         gint row = 0;
50
51         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
52         valid = gtk_tree_model_get_iter_first(store, iter);
53         while (valid)
54                 {
55                 FileData *fd_n;
56                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, DIR_COLUMN_POINTER, &fd_n, -1);
57                 if (fd_n == fd) return row;
58
59                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
60                 row++;
61                 }
62
63         return -1;
64 }
65
66 static gint vdlist_rename_row_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
67 {
68         ViewDir *vd = data;
69         GtkTreeModel *store;
70         GtkTreeIter iter;
71         FileData *fd;
72         gchar *old_path;
73         gchar *new_path;
74         gchar *base;
75
76         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
77         if (!gtk_tree_model_get_iter(store, &iter, td->path)) return FALSE;
78         gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
79         if (!fd) return FALSE;
80         
81         old_path = g_strdup(fd->path);
82
83         base = remove_level_from_path(old_path);
84         new_path = concat_dir_and_file(base, new);
85         g_free(base);
86
87         if (file_util_rename_dir(fd, new_path, vd->view))
88                 {
89                 if (vd->layout && strcmp(vd->path, old_path) == 0)
90                         {
91                         layout_set_path(vd->layout, new_path);
92                         }
93                 else
94                         {
95                         vdlist_refresh(vd);
96                         }
97                 }
98
99         g_free(old_path);
100         g_free(new_path);
101         return FALSE;
102 }
103
104 static void vdlist_rename_by_row(ViewDir *vd, FileData *fd)
105 {
106         GtkTreeModel *store;
107         GtkTreePath *tpath;
108         GtkTreeIter iter;
109
110         if (vdlist_find_row(vd, fd, &iter) < 0) return;
111         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
112         tpath = gtk_tree_model_get_path(store, &iter);
113
114         tree_edit_by_path(GTK_TREE_VIEW(vd->view), tpath, 0, fd->name,
115                           vdlist_rename_row_cb, vd);
116         gtk_tree_path_free(tpath);
117 }
118
119 static FileData *vdlist_row_by_path(ViewDir *vd, const gchar *path, gint *row)
120 {
121         GList *work;
122         gint n;
123
124         if (!path)
125                 {
126                 if (row) *row = -1;
127                 return NULL;
128                 }
129
130         n = 0;
131         work = VDLIST_INFO(vd, list);
132         while (work)
133                 {
134                 FileData *fd = work->data;
135                 if (strcmp(fd->path, path) == 0)
136                         {
137                         if (row) *row = n;
138                         return fd;
139                         }
140                 work = work->next;
141                 n++;
142                 }
143
144         if (row) *row = -1;
145         return NULL;
146 }
147
148 /*
149  *-----------------------------------------------------------------------------
150  * pop-up menu
151  *-----------------------------------------------------------------------------
152  */ 
153
154 static void vdlist_pop_menu_up_cb(GtkWidget *widget, gpointer data)
155 {
156         ViewDir *vd = data;
157         gchar *path;
158
159         if (!vd->path || strcmp(vd->path, "/") == 0) return;
160         path = remove_level_from_path(vd->path);
161
162         if (vd->select_func)
163                 {
164                 vd->select_func(vd, path, vd->select_data);
165                 }
166
167         g_free(path);
168 }
169
170 static void vdlist_pop_menu_slide_cb(GtkWidget *widget, gpointer data)
171 {
172         ViewDir *vd = data;
173         gchar *path;
174
175         if (!vd->layout || !vd->click_fd) return;
176
177         path = g_strdup(vd->click_fd->path);
178
179         layout_set_path(vd->layout, path);
180         layout_select_none(vd->layout);
181         layout_image_slideshow_stop(vd->layout);
182         layout_image_slideshow_start(vd->layout);
183
184         g_free(path);
185 }
186
187 static void vdlist_pop_menu_slide_rec_cb(GtkWidget *widget, gpointer data)
188 {
189         ViewDir *vd = data;
190         gchar *path;
191         GList *list;
192
193         if (!vd->layout || !vd->click_fd) return;
194
195         path = g_strdup(vd->click_fd->path);
196
197         list = filelist_recursive(path);
198
199         layout_image_slideshow_stop(vd->layout);
200         layout_image_slideshow_start_from_list(vd->layout, list);
201
202         g_free(path);
203 }
204
205 static void vdlist_pop_menu_dupe(ViewDir *vd, gint recursive)
206 {
207         DupeWindow *dw;
208         GList *list = NULL;
209
210         if (!vd->click_fd) return;
211
212         if (recursive)
213                 {
214                 list = g_list_append(list, file_data_ref(vd->click_fd));
215                 }
216         else
217                 {
218                 filelist_read(vd->click_fd->path, &list, NULL);
219                 list = filelist_filter(list, FALSE);
220                 }
221
222         dw = dupe_window_new(DUPE_MATCH_NAME);
223         dupe_window_add_files(dw, list, recursive);
224
225         filelist_free(list);
226 }
227
228 static void vdlist_pop_menu_dupe_cb(GtkWidget *widget, gpointer data)
229 {
230         ViewDir *vd = data;
231         vdlist_pop_menu_dupe(vd, FALSE);
232 }
233
234 static void vdlist_pop_menu_dupe_rec_cb(GtkWidget *widget, gpointer data)
235 {
236         ViewDir *vd = data;
237         vdlist_pop_menu_dupe(vd, TRUE);
238 }
239
240 static void vdlist_pop_menu_new_cb(GtkWidget *widget, gpointer data)
241 {
242         ViewDir *vd = data;
243         gchar *new_path;
244         gchar *buf;
245
246         if (!vd->path) return;
247
248         buf = concat_dir_and_file(vd->path, _("new_folder"));
249         new_path = unique_filename(buf, NULL, NULL, FALSE);
250         g_free(buf);
251         if (!new_path) return;
252
253         if (!mkdir_utf8(new_path, 0755))
254                 {
255                 gchar *text;
256
257                 text = g_strdup_printf(_("Unable to create folder:\n%s"), new_path);
258                 file_util_warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, vd->view);
259                 g_free(text);
260                 }
261         else
262                 {
263                 FileData *fd;
264
265                 vdlist_refresh(vd);
266                 fd = vdlist_row_by_path(vd, new_path, NULL);
267
268                 vdlist_rename_by_row(vd, fd);
269                 }
270
271         g_free(new_path);
272 }
273
274 static void vdlist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
275 {
276         ViewDir *vd = data;
277
278         vdlist_rename_by_row(vd, vd->click_fd);
279 }
280
281 static void vdlist_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
282 {
283         ViewDir *vd = data;
284
285         if (!vd->click_fd) return;
286         file_util_delete_dir(vd->click_fd, vd->widget);
287 }
288
289 static void vdlist_pop_menu_dir_view_as_cb(GtkWidget *widget, gpointer data)
290 {
291         ViewDir *vd = data;
292
293         if (vd->layout) layout_views_set(vd->layout, DIRVIEW_TREE, vd->layout->icon_view);
294 }
295
296 static void vdlist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
297 {
298         ViewDir *vd = data;
299
300         if (vd->layout) layout_refresh(vd->layout);
301 }
302
303 static void vdlist_toggle_show_hidden_files_cb(GtkWidget *widget, gpointer data)
304 {
305         ViewDir *vd = data;
306
307         options->file_filter.show_hidden_files = !options->file_filter.show_hidden_files;
308         if (vd->layout) layout_refresh(vd->layout);
309 }
310
311 static GtkWidget *vdlist_pop_menu(ViewDir *vd, FileData *fd)
312 {
313         GtkWidget *menu;
314         gint active;
315
316         active = (fd != NULL);
317
318         menu = popup_menu_short_lived();
319         g_signal_connect(G_OBJECT(menu), "destroy",
320                          G_CALLBACK(vdlist_popup_destroy_cb), vd);
321
322         menu_item_add_stock_sensitive(menu, _("_Up to parent"), GTK_STOCK_GO_UP,
323                                       (vd->path && strcmp(vd->path, "/") != 0),
324                                       G_CALLBACK(vdlist_pop_menu_up_cb), vd);
325
326         menu_item_add_divider(menu);
327         menu_item_add_sensitive(menu, _("_Slideshow"), active,
328                                 G_CALLBACK(vdlist_pop_menu_slide_cb), vd);
329         menu_item_add_sensitive(menu, _("Slideshow recursive"), active,
330                                 G_CALLBACK(vdlist_pop_menu_slide_rec_cb), vd);
331
332         menu_item_add_divider(menu);
333         menu_item_add_stock_sensitive(menu, _("Find _duplicates..."), GTK_STOCK_FIND, active,
334                                       G_CALLBACK(vdlist_pop_menu_dupe_cb), vd);
335         menu_item_add_stock_sensitive(menu, _("Find duplicates recursive..."), GTK_STOCK_FIND, active,
336                                       G_CALLBACK(vdlist_pop_menu_dupe_rec_cb), vd);
337
338         menu_item_add_divider(menu);
339
340         /* check using . (always row 0) */
341         active = (vd->path && access_file(vd->path , W_OK | X_OK));
342         menu_item_add_sensitive(menu, _("_New folder..."), active,
343                                 G_CALLBACK(vdlist_pop_menu_new_cb), vd);
344
345         /* ignore .. and . */
346         active = (active && fd &&
347                   strcmp(fd->name, ".") != 0 &&
348                   strcmp(fd->name, "..") != 0 &&
349                   access_file(fd->path, W_OK | X_OK));
350         menu_item_add_sensitive(menu, _("_Rename..."), active,
351                                 G_CALLBACK(vdlist_pop_menu_rename_cb), vd);
352         menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
353                                       G_CALLBACK(vdlist_pop_menu_delete_cb), vd);
354
355         menu_item_add_divider(menu);
356         menu_item_add_check(menu, _("View as _tree"), FALSE,
357                             G_CALLBACK(vdlist_pop_menu_dir_view_as_cb), vd);
358         menu_item_add_check(menu, _("Show _hidden files"), options->file_filter.show_hidden_files,
359                             G_CALLBACK(vdlist_toggle_show_hidden_files_cb), vd);
360
361         menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH,
362                             G_CALLBACK(vdlist_pop_menu_refresh_cb), vd);
363
364         return menu;
365 }
366
367 static void vdlist_popup_destroy_cb(GtkWidget *widget, gpointer data)
368 {
369         ViewDir *vd = data;
370
371         vd_color_set(vd, vd->click_fd, FALSE);
372         vd->click_fd = NULL;
373         vd->popup = NULL;
374
375         vd_color_set(vd, vd->drop_fd, FALSE);
376         filelist_free(vd->drop_list);
377         vd->drop_list = NULL;
378         vd->drop_fd = NULL;
379 }
380
381 /*
382  *-----------------------------------------------------------------------------
383  * dnd
384  *-----------------------------------------------------------------------------
385  */
386
387 static GtkTargetEntry vdlist_dnd_drop_types[] = {
388         { "text/uri-list", 0, TARGET_URI_LIST }
389 };
390 static gint vdlist_dnd_drop_types_count = 1;
391
392 static void vdlist_dest_set(ViewDir *vd, gint enable)
393 {
394         if (enable)
395                 {
396                 gtk_drag_dest_set(vd->view,
397                                   GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
398                                   vdlist_dnd_drop_types, vdlist_dnd_drop_types_count,
399                                   GDK_ACTION_MOVE | GDK_ACTION_COPY);
400                 }
401         else
402                 {
403                 gtk_drag_dest_unset(vd->view);
404                 }
405 }
406
407 static void vdlist_dnd_get(GtkWidget *widget, GdkDragContext *context,
408                            GtkSelectionData *selection_data, guint info,
409                            guint time, gpointer data)
410 {
411         ViewDir *vd = data;
412         GList *list;
413         gchar *text = NULL;
414         gint length = 0;
415
416         if (!vd->click_fd) return;
417
418         switch (info)
419                 {
420                 case TARGET_URI_LIST:
421                 case TARGET_TEXT_PLAIN:
422                         list = g_list_prepend(NULL, vd->click_fd);
423                         text = uri_text_from_filelist(list, &length, (info == TARGET_TEXT_PLAIN));
424                         g_list_free(list);
425                         break;
426                 }
427         if (text)
428                 {
429                 gtk_selection_data_set (selection_data, selection_data->target,
430                                 8, (guchar *)text, length);
431                 g_free(text);
432                 }
433 }
434
435 static void vdlist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
436 {
437         ViewDir *vd = data;
438
439         vd_color_set(vd, vd->click_fd, TRUE);
440         vdlist_dest_set(vd, FALSE);
441 }
442
443 static void vdlist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
444 {
445         ViewDir *vd = data;
446
447         vd_color_set(vd, vd->click_fd, FALSE);
448
449         if (context->action == GDK_ACTION_MOVE)
450                 {
451                 vdlist_refresh(vd);
452                 }
453         vdlist_dest_set(vd, TRUE);
454 }
455
456 static void vdlist_dnd_drop_receive(GtkWidget *widget,
457                                     GdkDragContext *context, gint x, gint y,
458                                     GtkSelectionData *selection_data, guint info,
459                                     guint time, gpointer data)
460 {
461         ViewDir *vd = data;
462         GtkTreePath *tpath;
463         GtkTreeIter iter;
464         FileData *fd = NULL;
465
466         vd->click_fd = NULL;
467
468         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y,
469                                           &tpath, NULL, NULL, NULL))
470                 {
471                 GtkTreeModel *store;
472
473                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
474                 gtk_tree_model_get_iter(store, &iter, tpath);
475                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
476                 gtk_tree_path_free(tpath);
477                 }
478
479         if (!fd) return;
480
481         if (info == TARGET_URI_LIST)
482                 {
483                 GList *list;
484                 gint active;
485
486                 list = uri_filelist_from_text((gchar *)selection_data->data, TRUE);
487                 if (!list) return;
488
489                 active = access_file(fd->path, W_OK | X_OK);
490
491                 vd_color_set(vd, fd, TRUE);
492                 vd->popup = vd_drop_menu(vd, active);
493                 gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, NULL, NULL, 0, time);
494
495                 vd->drop_fd = fd;
496                 vd->drop_list = list;
497                 }
498 }
499
500 #if 0
501 static gint vdlist_get_row_visibility(ViewDir *vd, FileData *fd)
502 {
503         GtkTreeModel *store;
504         GtkTreeViewColumn *column;
505         GtkTreePath *tpath;
506         GtkTreeIter iter;
507
508         GdkRectangle vrect;
509         GdkRectangle crect;
510
511         if (!fd || vdlist_find_row(vd, fd, &iter) < 0) return 0;
512
513         column = gtk_tree_view_get_column(GTK_TREE_VIEW(vd->view), 0);
514         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
515         tpath = gtk_tree_model_get_path(store, &iter);
516
517         gtk_tree_view_get_visible_rect(GTK_TREE_VIEW(vd->view), &vrect);
518         gtk_tree_view_get_cell_area(GTK_TREE_VIEW(vd->view), tpath, column, &crect);
519 printf("window: %d + %d; cell: %d + %d\n", vrect.y, vrect.height, crect.y, crect.height);
520         gtk_tree_path_free(tpath);
521
522         if (crect.y + crect.height < vrect.y) return -1;
523         if (crect.y > vrect.y + vrect.height) return 1;
524         return 0;
525 }
526 #endif
527
528 static void vdlist_scroll_to_row(ViewDir *vd, FileData *fd, gfloat y_align)
529 {
530         GtkTreeIter iter;
531
532         if (GTK_WIDGET_REALIZED(vd->view) &&
533             vdlist_find_row(vd, fd, &iter) >= 0)
534                 {
535                 GtkTreeModel *store;
536                 GtkTreePath *tpath;
537
538                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
539                 tpath = gtk_tree_model_get_path(store, &iter);
540                 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vd->view), tpath, NULL, TRUE, y_align, 0.0);
541                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vd->view), tpath, NULL, FALSE);
542                 gtk_tree_path_free(tpath);
543
544                 if (!GTK_WIDGET_HAS_FOCUS(vd->view)) gtk_widget_grab_focus(vd->view);
545                 }
546 }
547
548 static void vdlist_drop_update(ViewDir *vd, gint x, gint y)
549 {
550         GtkTreePath *tpath;
551         GtkTreeIter iter;
552         FileData *fd = NULL;
553
554         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vd->view), x, y,
555                                           &tpath, NULL, NULL, NULL))
556                 {
557                 GtkTreeModel *store;
558
559                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
560                 gtk_tree_model_get_iter(store, &iter, tpath);
561                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
562                 gtk_tree_path_free(tpath);
563                 }
564
565         if (fd != vd->drop_fd)
566                 {
567                 vd_color_set(vd, vd->drop_fd, FALSE);
568                 vd_color_set(vd, fd, TRUE);
569                 }
570
571         vd->drop_fd = fd;
572 }
573
574 static void vdlist_dnd_drop_scroll_cancel(ViewDir *vd)
575 {
576         if (vd->drop_scroll_id != -1) g_source_remove(vd->drop_scroll_id);
577         vd->drop_scroll_id = -1;
578 }
579
580 static gint vdlist_auto_scroll_idle_cb(gpointer data)
581 {
582         ViewDir *vd = data;
583
584         if (vd->drop_fd)
585                 {
586                 GdkWindow *window;
587                 gint x, y;
588                 gint w, h;
589
590                 window = vd->view->window;
591                 gdk_window_get_pointer(window, &x, &y, NULL);
592                 gdk_drawable_get_size(window, &w, &h);
593                 if (x >= 0 && x < w && y >= 0 && y < h)
594                         {
595                         vdlist_drop_update(vd, x, y);
596                         }
597                 }
598
599         vd->drop_scroll_id = -1;
600         return FALSE;
601 }
602
603 static gint vdlist_auto_scroll_notify_cb(GtkWidget *widget, gint x, gint y, gpointer data)
604 {
605         ViewDir *vd = data;
606
607         if (!vd->drop_fd || vd->drop_list) return FALSE;
608
609         if (vd->drop_scroll_id == -1) vd->drop_scroll_id = g_idle_add(vdlist_auto_scroll_idle_cb, vd);
610
611         return TRUE;
612 }
613
614 static gint vdlist_dnd_drop_motion(GtkWidget *widget, GdkDragContext *context,
615                                    gint x, gint y, guint time, gpointer data)
616 {
617         ViewDir *vd = data;
618
619         vd->click_fd = NULL;
620
621         if (gtk_drag_get_source_widget(context) == vd->view)
622                 {
623                 /* from same window */
624                 gdk_drag_status(context, 0, time);
625                 return TRUE;
626                 }
627         else
628                 {
629                 gdk_drag_status(context, context->suggested_action, time);
630                 }
631
632         vdlist_drop_update(vd, x, y);
633
634         if (vd->drop_fd)
635                 {
636                 GtkAdjustment *adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vd->view));
637                 widget_auto_scroll_start(vd->view, adj, -1, -1, vdlist_auto_scroll_notify_cb, vd);
638                 }
639
640         return FALSE;
641 }
642
643 static void vdlist_dnd_drop_leave(GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
644 {
645         ViewDir *vd = data;
646
647         if (vd->drop_fd != vd->click_fd) vd_color_set(vd, vd->drop_fd, FALSE);
648
649         vd->drop_fd = NULL;
650 }
651
652 static void vdlist_dnd_init(ViewDir *vd)
653 {
654         gtk_drag_source_set(vd->view, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
655                             dnd_file_drag_types, dnd_file_drag_types_count,
656                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
657         g_signal_connect(G_OBJECT(vd->view), "drag_data_get",
658                          G_CALLBACK(vdlist_dnd_get), vd);
659         g_signal_connect(G_OBJECT(vd->view), "drag_begin",
660                          G_CALLBACK(vdlist_dnd_begin), vd);
661         g_signal_connect(G_OBJECT(vd->view), "drag_end",
662                          G_CALLBACK(vdlist_dnd_end), vd);
663
664         vdlist_dest_set(vd, TRUE);
665         g_signal_connect(G_OBJECT(vd->view), "drag_data_received",
666                          G_CALLBACK(vdlist_dnd_drop_receive), vd);
667         g_signal_connect(G_OBJECT(vd->view), "drag_motion",
668                          G_CALLBACK(vdlist_dnd_drop_motion), vd);
669         g_signal_connect(G_OBJECT(vd->view), "drag_leave",
670                          G_CALLBACK(vdlist_dnd_drop_leave), vd);
671 }
672
673 /*
674  *-----------------------------------------------------------------------------
675  * main
676  *-----------------------------------------------------------------------------
677  */ 
678
679 static void vdlist_select_row(ViewDir *vd, FileData *fd)
680 {
681         if (fd && vd->select_func)
682                 {
683                 gchar *path;
684
685                 path = g_strdup(fd->path);
686                 vd->select_func(vd, path, vd->select_data);
687                 g_free(path);
688                 }
689 }
690
691 const gchar *vdlist_row_get_path(ViewDir *vd, gint row)
692 {
693         FileData *fd;
694
695         fd = g_list_nth_data(VDLIST_INFO(vd, list), row);
696
697         if (fd) return fd->path;
698
699         return NULL;
700 }
701
702 static void vdlist_populate(ViewDir *vd)
703 {
704         GtkListStore *store;
705         GList *work;
706
707         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view)));
708         gtk_list_store_clear(store);
709
710         work = VDLIST_INFO(vd, list);
711         while (work)
712                 {
713                 FileData *fd;
714                 GtkTreeIter iter;
715                 GdkPixbuf *pixbuf;
716
717                 fd = work->data;
718
719                 if (access_file(fd->path, R_OK | X_OK) && fd->name)
720                         {
721                         if (fd->name[0] == '.' && fd->name[1] == '\0')
722                                 {
723                                 pixbuf = vd->pf->open;
724                                 }
725                         else if (fd->name[0] == '.' && fd->name[1] == '.' && fd->name[2] == '\0')
726                                 {
727                                 pixbuf = vd->pf->parent;
728                                 }
729                         else
730                                 {
731                                 pixbuf = vd->pf->close;
732                                 }
733                         }
734                 else
735                         {
736                         pixbuf = vd->pf->deny;
737                         }
738
739                 gtk_list_store_append(store, &iter);
740                 gtk_list_store_set(store, &iter,
741                                    DIR_COLUMN_POINTER, fd,
742                                    DIR_COLUMN_ICON, pixbuf,
743                                    DIR_COLUMN_NAME, fd->name, -1);
744
745                 work = work->next;
746                 }
747
748         vd->click_fd = NULL;
749         vd->drop_fd = NULL;
750 }
751
752 gint vdlist_set_path(ViewDir *vd, const gchar *path)
753 {
754         gint ret;
755         FileData *fd;
756         gchar *old_path = NULL;
757         gchar *filepath;
758
759         if (!path) return FALSE;
760         if (vd->path && strcmp(path, vd->path) == 0) return TRUE;
761
762         if (vd->path)
763                 {
764                 gchar *base;
765
766                 base = remove_level_from_path(vd->path);
767                 if (strcmp(base, path) == 0)
768                         {
769                         old_path = g_strdup(filename_from_path(vd->path));
770                         }
771                 g_free(base);
772                 }
773
774         g_free(vd->path);
775         vd->path = g_strdup(path);
776
777         filelist_free(VDLIST_INFO(vd, list));
778         VDLIST_INFO(vd, list) = NULL;
779
780         ret = filelist_read(vd->path, NULL, &VDLIST_INFO(vd, list));
781
782         VDLIST_INFO(vd, list) = filelist_sort(VDLIST_INFO(vd, list), SORT_NAME, TRUE);
783
784         /* add . and .. */
785
786         if (strcmp(vd->path, "/") != 0)
787                 {
788                 filepath = g_strconcat(vd->path, "/", "..", NULL); 
789                 fd = file_data_new_simple(filepath);
790                 VDLIST_INFO(vd, list) = g_list_prepend(VDLIST_INFO(vd, list), fd);
791                 g_free(filepath);
792                 }
793         
794         if (options->file_filter.show_dot_directory)
795                 {
796                 filepath = g_strconcat(vd->path, "/", ".", NULL); 
797                 fd = file_data_new_simple(filepath);
798                 VDLIST_INFO(vd, list) = g_list_prepend(VDLIST_INFO(vd, list), fd);
799                 g_free(filepath);
800         }
801
802         vdlist_populate(vd);
803
804         if (old_path)
805                 {
806                 /* scroll to make last path visible */
807                 FileData *found = NULL;
808                 GList *work;
809
810                 work = VDLIST_INFO(vd, list);
811                 while (work && !found)
812                         {
813                         FileData *fd = work->data;
814                         if (strcmp(old_path, fd->name) == 0) found = fd;
815                         work = work->next;
816                         }
817
818                 if (found) vdlist_scroll_to_row(vd, found, 0.5);
819
820                 g_free(old_path);
821                 return ret;
822                 }
823
824         if (GTK_WIDGET_REALIZED(vd->view))
825                 {
826                 gtk_tree_view_scroll_to_point(GTK_TREE_VIEW(vd->view), 0, 0);
827                 }
828
829         return ret;
830 }
831
832 void vdlist_refresh(ViewDir *vd)
833 {
834         gchar *path;
835
836         path = g_strdup(vd->path);
837         vd->path = NULL;
838         vdlist_set_path(vd, path);
839         g_free(path);
840 }
841
842 static void vdlist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
843 {
844         ViewDir *vd = data;
845         GtkTreeModel *store;
846         GtkTreeIter iter;
847         GtkTreePath *tpath;
848         gint cw, ch;
849
850         if (vdlist_find_row(vd, vd->click_fd, &iter) < 0) return;
851         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
852         tpath = gtk_tree_model_get_path(store, &iter);
853         tree_view_get_cell_clamped(GTK_TREE_VIEW(vd->view), tpath, 0, TRUE, x, y, &cw, &ch);
854         gtk_tree_path_free(tpath);
855         *y += ch;
856         popup_menu_position_clamp(menu, x, y, 0);
857 }
858
859 static gint vdlist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
860 {
861         ViewDir *vd = data;
862         GtkTreePath *tpath;
863         
864         if (event->keyval != GDK_Menu) return FALSE;
865
866         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vd->view), &tpath, NULL);
867         if (tpath)
868                 {
869                 GtkTreeModel *store;
870                 GtkTreeIter iter;
871
872                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
873                 gtk_tree_model_get_iter(store, &iter, tpath);
874                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &vd->click_fd, -1);
875                 
876                 gtk_tree_path_free(tpath);
877                 }
878         else
879                 {
880                 vd->click_fd = NULL;
881                 }
882
883         vd_color_set(vd, vd->click_fd, TRUE);
884
885         vd->popup = vdlist_pop_menu(vd, vd->click_fd);
886
887         gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, vdlist_menu_position_cb, vd, 0, GDK_CURRENT_TIME);
888
889         return TRUE;
890 }
891
892 static gint vdlist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
893 {
894         ViewDir *vd = data;
895         GtkTreePath *tpath;
896         GtkTreeIter iter;
897         FileData *fd = NULL;
898
899         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
900                                           &tpath, NULL, NULL, NULL))
901                 {
902                 GtkTreeModel *store;
903
904                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
905                 gtk_tree_model_get_iter(store, &iter, tpath);
906                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
907                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
908                 gtk_tree_path_free(tpath);
909                 }
910
911         vd->click_fd = fd;
912         vd_color_set(vd, vd->click_fd, TRUE);
913
914         if (bevent->button == 3)
915                 {
916                 vd->popup = vdlist_pop_menu(vd, vd->click_fd);
917                 gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, NULL, NULL,
918                                bevent->button, bevent->time);
919                 }
920
921         return TRUE;
922 }
923
924 static gint vdlist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
925 {
926         ViewDir *vd = data;
927         GtkTreePath *tpath;
928         GtkTreeIter iter;
929         FileData *fd = NULL;
930
931         vd_color_set(vd, vd->click_fd, FALSE);
932
933         if (bevent->button != 1) return TRUE;
934
935         if ((bevent->x != 0 || bevent->y != 0) &&
936             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
937                                           &tpath, NULL, NULL, NULL))
938                 {
939                 GtkTreeModel *store;
940
941                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
942                 gtk_tree_model_get_iter(store, &iter, tpath);
943                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
944                 gtk_tree_path_free(tpath);
945                 }
946
947         if (fd && vd->click_fd == fd)
948                 {
949                 vdlist_select_row(vd, vd->click_fd);
950                 }
951
952         return TRUE;
953 }
954
955 static void vdlist_select_cb(GtkTreeView *tview, GtkTreePath *tpath, GtkTreeViewColumn *column, gpointer data)
956 {
957         ViewDir *vd = data;
958         GtkTreeModel *store;
959         GtkTreeIter iter;
960         FileData *fd;
961
962         store = gtk_tree_view_get_model(tview);
963         gtk_tree_model_get_iter(store, &iter, tpath);
964         gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
965
966         vdlist_select_row(vd, fd);
967 }
968
969 static GdkColor *vdlist_color_shifted(GtkWidget *widget)
970 {
971         static GdkColor color;
972         static GtkWidget *done = NULL;
973
974         if (done != widget)
975                 {
976                 GtkStyle *style;
977
978                 style = gtk_widget_get_style(widget);
979                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
980                 shift_color(&color, -1, 0);
981                 done = widget;
982                 }
983
984         return &color;
985 }
986
987 static void vdlist_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
988                             GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
989 {
990         ViewDir *vd = data;
991         gboolean set;
992
993         gtk_tree_model_get(tree_model, iter, DIR_COLUMN_COLOR, &set, -1);
994         g_object_set(G_OBJECT(cell),
995                      "cell-background-gdk", vdlist_color_shifted(vd->view),
996                      "cell-background-set", set, NULL);
997 }
998
999 static void vdlist_destroy_cb(GtkWidget *widget, gpointer data)
1000 {
1001         ViewDir *vd = data;
1002
1003         if (vd->popup)
1004                 {
1005                 g_signal_handlers_disconnect_matched(G_OBJECT(vd->popup), G_SIGNAL_MATCH_DATA,
1006                                                      0, 0, 0, NULL, vd);
1007                 gtk_widget_destroy(vd->popup);
1008                 }
1009
1010         vdlist_dnd_drop_scroll_cancel(vd);
1011         widget_auto_scroll_stop(vd->view);
1012
1013         filelist_free(vd->drop_list);
1014
1015         folder_icons_free(vd->pf);
1016
1017         g_free(vd->path);
1018         filelist_free(VDLIST_INFO(vd, list));
1019         g_free(vd->info);
1020         g_free(vd);
1021 }
1022
1023 ViewDir *vdlist_new(const gchar *path)
1024 {
1025         ViewDir *vd;
1026         GtkListStore *store;
1027         GtkTreeSelection *selection;
1028         GtkTreeViewColumn *column;
1029         GtkCellRenderer *renderer;
1030
1031         vd = g_new0(ViewDir, 1);
1032         vd->info = g_new0(ViewDirInfoList, 1);
1033         vd->type = DIRVIEW_LIST;
1034
1035         vd->path = NULL;
1036         VDLIST_INFO(vd, list) = NULL;
1037         vd->click_fd = NULL;
1038
1039         vd->drop_fd = NULL;
1040         vd->drop_list = NULL;
1041
1042         vd->drop_scroll_id = -1;
1043
1044         vd->popup = NULL;
1045
1046         vd->widget = gtk_scrolled_window_new(NULL, NULL);
1047         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(vd->widget), GTK_SHADOW_IN);
1048         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vd->widget),
1049                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1050         g_signal_connect(G_OBJECT(vd->widget), "destroy",
1051                          G_CALLBACK(vdlist_destroy_cb), vd);
1052
1053         store = gtk_list_store_new(4, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN);
1054         vd->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1055         g_object_unref(store);
1056
1057         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vd->view), FALSE);
1058         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vd->view), FALSE);
1059         g_signal_connect(G_OBJECT(vd->view), "row_activated",
1060
1061                          G_CALLBACK(vdlist_select_cb), vd);
1062
1063         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vd->view));
1064         gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);
1065
1066         column = gtk_tree_view_column_new();
1067         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1068
1069         renderer = gtk_cell_renderer_pixbuf_new();
1070         gtk_tree_view_column_pack_start(column, renderer, FALSE);
1071         gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", DIR_COLUMN_ICON);
1072         gtk_tree_view_column_set_cell_data_func(column, renderer, vdlist_color_cb, vd, NULL);
1073
1074         renderer = gtk_cell_renderer_text_new();
1075         gtk_tree_view_column_pack_start(column, renderer, TRUE);
1076         gtk_tree_view_column_add_attribute(column, renderer, "text", DIR_COLUMN_NAME);
1077         gtk_tree_view_column_set_cell_data_func(column, renderer, vdlist_color_cb, vd, NULL);
1078
1079         gtk_tree_view_append_column(GTK_TREE_VIEW(vd->view), column);
1080
1081         g_signal_connect(G_OBJECT(vd->view), "key_press_event",
1082                            G_CALLBACK(vdlist_press_key_cb), vd);
1083         gtk_container_add(GTK_CONTAINER(vd->widget), vd->view);
1084         gtk_widget_show(vd->view);
1085
1086         vd->pf = folder_icons_new();
1087
1088         vdlist_dnd_init(vd);
1089
1090         g_signal_connect(G_OBJECT(vd->view), "button_press_event",
1091                          G_CALLBACK(vdlist_press_cb), vd);
1092         g_signal_connect(G_OBJECT(vd->view), "button_release_event",
1093                          G_CALLBACK(vdlist_release_cb), vd);
1094
1095         if (path) vdlist_set_path(vd, path);
1096
1097         return vd;
1098 }