Make directory view popup menu common and move it to view_dir.{c,h}.
[geeqie.git] / src / view_dir_tree.c
1 /*
2  * Geeqie
3  * (C) 2006 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_tree.h"
14
15
16 #include "dnd.h"
17 #include "dupe.h"
18 #include "filelist.h"
19 #include "layout.h"
20 #include "layout_image.h"
21 #include "layout_util.h"
22 #include "utilops.h"
23 #include "ui_bookmark.h"
24 #include "ui_fileops.h"
25 #include "ui_menu.h"
26 #include "ui_tree_edit.h"
27 #include "view_dir.h"
28
29 #include <gdk/gdkkeysyms.h> /* for keyboard values */
30
31
32 #define VDTREE_INDENT 14
33 #define VDTREE_PAD 4
34
35 #define VDTREE_INFO(_vd_, _part_) (((ViewDirInfoTree *)(_vd_->info))->_part_)
36
37
38 typedef struct _PathData PathData;
39 struct _PathData
40 {
41         gchar *name;
42         FileData *node;
43 };
44
45 typedef struct _NodeData NodeData;
46 struct _NodeData
47 {
48         FileData *fd;
49         gint expanded;
50         time_t last_update;
51 };
52
53
54 static gint vdtree_populate_path_by_iter(ViewDir *vd, GtkTreeIter *iter, gint force, const gchar *target_path);
55
56
57 /*
58  *----------------------------------------------------------------------------
59  * utils
60  *----------------------------------------------------------------------------
61  */
62
63 static void set_cursor(GtkWidget *widget, GdkCursorType cursor_type)
64 {
65         GdkCursor *cursor = NULL;
66
67         if (!widget || !widget->window) return;
68
69         if (cursor_type > -1) cursor = gdk_cursor_new (cursor_type);
70         gdk_window_set_cursor (widget->window, cursor);
71         if (cursor) gdk_cursor_unref(cursor);
72         gdk_flush();
73 }
74
75 static void vdtree_busy_push(ViewDir *vd)
76 {
77         if (VDTREE_INFO(vd, busy_ref) == 0) set_cursor(vd->view, GDK_WATCH);
78         VDTREE_INFO(vd, busy_ref)++;
79 }
80
81 static void vdtree_busy_pop(ViewDir *vd)
82 {
83         if (VDTREE_INFO(vd, busy_ref) == 1) set_cursor(vd->view, -1);
84         if (VDTREE_INFO(vd, busy_ref) > 0) VDTREE_INFO(vd, busy_ref)--;
85 }
86
87 gint vdtree_find_row(ViewDir *vd, FileData *fd, GtkTreeIter *iter, GtkTreeIter *parent)
88 {
89         GtkTreeModel *store;
90         gint valid;
91
92         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
93         if (parent)
94                 {
95                 valid = gtk_tree_model_iter_children(store, iter, parent);
96                 }
97         else
98                 {
99                 valid = gtk_tree_model_get_iter_first(store, iter);
100                 }
101         while (valid)
102                 {
103                 NodeData *nd;
104                 GtkTreeIter found;
105
106                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, DIR_COLUMN_POINTER, &nd, -1);
107                 if (nd->fd == fd) return TRUE;
108
109                 if (vdtree_find_row(vd, fd, &found, iter))
110                         {
111                         memcpy(iter, &found, sizeof(found));
112                         return TRUE;
113                         }
114
115                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
116                 }
117
118         return FALSE;
119 }
120
121 static void vdtree_icon_set_by_iter(ViewDir *vd, GtkTreeIter *iter, GdkPixbuf *pixbuf)
122 {
123         GtkTreeModel *store;
124         GdkPixbuf *old;
125
126         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
127         gtk_tree_model_get(store, iter, DIR_COLUMN_ICON, &old, -1);
128         if (old != vd->pf->deny)
129                 {
130                 gtk_tree_store_set(GTK_TREE_STORE(store), iter, DIR_COLUMN_ICON, pixbuf, -1);
131                 }
132 }
133
134 static void vdtree_expand_by_iter(ViewDir *vd, GtkTreeIter *iter, gint expand)
135 {
136         GtkTreeModel *store;
137         GtkTreePath *tpath;
138
139         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
140         tpath = gtk_tree_model_get_path(store, iter);
141         if (expand)
142                 {
143                 gtk_tree_view_expand_row(GTK_TREE_VIEW(vd->view), tpath, FALSE);
144                 vdtree_icon_set_by_iter(vd, iter, vd->pf->open);
145                 }
146         else
147                 {
148                 gtk_tree_view_collapse_row(GTK_TREE_VIEW(vd->view), tpath);
149                 }
150         gtk_tree_path_free(tpath);
151 }
152
153 static void vdtree_expand_by_data(ViewDir *vd, FileData *fd, gint expand)
154 {
155         GtkTreeIter iter;
156
157         if (vdtree_find_row(vd, fd, &iter, NULL))
158                 {
159                 vdtree_expand_by_iter(vd, &iter, expand);
160                 }
161 }
162
163 static gint vdtree_rename_row_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
164 {
165         ViewDir *vd = data;
166         GtkTreeModel *store;
167         GtkTreeIter iter;
168         NodeData *nd;
169         gchar *old_path;
170         gchar *new_path;
171         gchar *base;
172
173         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
174         if (!gtk_tree_model_get_iter(store, &iter, td->path)) return FALSE;
175         gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
176         if (!nd) return FALSE;
177
178         old_path = g_strdup(nd->fd->path);
179
180         base = remove_level_from_path(old_path);
181         new_path = concat_dir_and_file(base, new);
182         g_free(base);
183
184         if (file_util_rename_dir(nd->fd, new_path, vd->view))
185                 {
186                 vdtree_populate_path(vd, new_path, TRUE, TRUE);
187
188                 if (vd->layout && strcmp(vd->path, old_path) == 0)
189                         {
190                         layout_set_path(vd->layout, new_path);
191                         }
192                 }
193
194         g_free(old_path);
195         g_free(new_path);
196
197         return FALSE;
198 }
199
200 void vdtree_rename_by_data(ViewDir *vd, FileData *fd)
201 {
202         GtkTreeModel *store;
203         GtkTreePath *tpath;
204         GtkTreeIter iter;
205
206         if (!fd ||
207             !vdtree_find_row(vd, fd, &iter, NULL)) return;
208
209         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
210         tpath = gtk_tree_model_get_path(store, &iter);
211
212         tree_edit_by_path(GTK_TREE_VIEW(vd->view), tpath, 0, fd->name,
213                           vdtree_rename_row_cb, vd);
214         gtk_tree_path_free(tpath);
215 }
216
217 static void vdtree_node_free(NodeData *nd)
218 {
219         if (!nd) return;
220
221         file_data_unref(nd->fd);
222         g_free(nd);
223 }
224
225 /*
226  *----------------------------------------------------------------------------
227  * dnd
228  *----------------------------------------------------------------------------
229  */
230
231 static GtkTargetEntry vdtree_dnd_drop_types[] = {
232         { "text/uri-list", 0, TARGET_URI_LIST }
233 };
234 static gint vdtree_dnd_drop_types_count = 1;
235
236
237 static void vdtree_dest_set(ViewDir *vd, gint enable)
238 {
239         if (enable)
240                 {
241                 gtk_drag_dest_set(vd->view,
242                                   GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
243                                   vdtree_dnd_drop_types, vdtree_dnd_drop_types_count,
244                                   GDK_ACTION_MOVE | GDK_ACTION_COPY);
245                 }
246         else
247                 {
248                 gtk_drag_dest_unset(vd->view);
249                 }
250 }
251
252 static void vdtree_dnd_get(GtkWidget *widget, GdkDragContext *context,
253                            GtkSelectionData *selection_data, guint info,
254                            guint time, gpointer data)
255 {
256         ViewDir *vd = data;
257         GList *list;
258         gchar *uri_text = NULL;
259         gint length = 0;
260
261         if (!vd->click_fd) return;
262
263         switch (info)
264                 {
265                 case TARGET_URI_LIST:
266                 case TARGET_TEXT_PLAIN:
267                         list = g_list_prepend(NULL, vd->click_fd);
268                         uri_text = uri_text_from_filelist(list, &length, (info == TARGET_TEXT_PLAIN));
269                         g_list_free(list);
270                         break;
271                 }
272
273         if (uri_text)
274                 {
275                 gtk_selection_data_set(selection_data, selection_data->target,
276                                        8, (guchar *)uri_text, length);
277                 g_free(uri_text);
278                 }
279 }
280
281 static void vdtree_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
282 {
283         ViewDir *vd = data;
284
285         vd_color_set(vd, vd->click_fd, TRUE);
286         vdtree_dest_set(vd, FALSE);
287 }
288
289 static void vdtree_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
290 {
291         ViewDir *vd = data;
292
293         vd_color_set(vd, vd->click_fd, FALSE);
294         vdtree_dest_set(vd, TRUE);
295 }
296
297 static void vdtree_dnd_drop_receive(GtkWidget *widget,
298                                     GdkDragContext *context, gint x, gint y,
299                                     GtkSelectionData *selection_data, guint info,
300                                     guint time, gpointer data)
301 {
302         ViewDir *vd = data;
303         GtkTreePath *tpath;
304         GtkTreeIter iter;
305         FileData *fd = NULL;
306
307         vd->click_fd = NULL;
308
309         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y,
310                                           &tpath, NULL, NULL, NULL))
311                 {
312                 GtkTreeModel *store;
313                 NodeData *nd;
314
315                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
316                 gtk_tree_model_get_iter(store, &iter, tpath);
317                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
318                 gtk_tree_path_free(tpath);
319
320                 fd = (nd) ? nd->fd : NULL;
321                 }
322
323         if (!fd) return;
324
325         if (info == TARGET_URI_LIST)
326                 {
327                 GList *list;
328                 gint active;
329
330                 list = uri_filelist_from_text((gchar *)selection_data->data, TRUE);
331                 if (!list) return;
332
333                 active = access_file(fd->path, W_OK | X_OK);
334
335                 vd_color_set(vd, fd, TRUE);
336                 vd->popup = vd_drop_menu(vd, active);
337                 gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, NULL, NULL, 0, time);
338
339                 vd->drop_fd = fd;
340                 vd->drop_list = list;
341                 }
342 }
343
344 static gint vdtree_dnd_drop_expand_cb(gpointer data)
345 {
346         ViewDir *vd = data;
347         GtkTreeIter iter;
348
349         if (vd->drop_fd &&
350             vdtree_find_row(vd, vd->drop_fd, &iter, NULL))
351                 {
352                 vdtree_populate_path_by_iter(vd, &iter, FALSE, vd->path);
353                 vdtree_expand_by_data(vd, vd->drop_fd, TRUE);
354                 }
355
356         VDTREE_INFO(vd, drop_expand_id) = -1;
357         return FALSE;
358 }
359
360 static void vdtree_dnd_drop_expand_cancel(ViewDir *vd)
361 {
362         if (VDTREE_INFO(vd, drop_expand_id) != -1) g_source_remove(VDTREE_INFO(vd, drop_expand_id));
363         VDTREE_INFO(vd, drop_expand_id) = -1;
364 }
365
366 static void vdtree_dnd_drop_expand(ViewDir *vd)
367 {
368         vdtree_dnd_drop_expand_cancel(vd);
369         VDTREE_INFO(vd, drop_expand_id) = g_timeout_add(1000, vdtree_dnd_drop_expand_cb, vd);
370 }
371
372 static void vdtree_drop_update(ViewDir *vd, gint x, gint y)
373 {
374         GtkTreePath *tpath;
375         GtkTreeIter iter;
376         FileData *fd = NULL;
377
378         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vd->view), x, y,
379                                           &tpath, NULL, NULL, NULL))
380                 {
381                 GtkTreeModel *store;
382                 NodeData *nd;
383
384                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
385                 gtk_tree_model_get_iter(store, &iter, tpath);
386                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
387                 gtk_tree_path_free(tpath);
388
389                 fd = (nd) ? nd->fd : NULL;
390                 }
391
392         if (fd != vd->drop_fd)
393                 {
394                 vd_color_set(vd, vd->drop_fd, FALSE);
395                 vd_color_set(vd, fd, TRUE);
396                 if (fd) vdtree_dnd_drop_expand(vd);
397                 }
398
399         vd->drop_fd = fd;
400 }
401
402 static void vdtree_dnd_drop_scroll_cancel(ViewDir *vd)
403 {
404         if (vd->drop_scroll_id != -1) g_source_remove(vd->drop_scroll_id);
405         vd->drop_scroll_id = -1;
406 }
407
408 static gint vdtree_auto_scroll_idle_cb(gpointer data)
409 {
410         ViewDir *vd = data;
411
412         if (vd->drop_fd)
413                 {
414                 GdkWindow *window;
415                 gint x, y;
416                 gint w, h;
417
418                 window = vd->view->window;
419                 gdk_window_get_pointer(window, &x, &y, NULL);
420                 gdk_drawable_get_size(window, &w, &h);
421                 if (x >= 0 && x < w && y >= 0 && y < h)
422                         {
423                         vdtree_drop_update(vd, x, y);
424                         }
425                 }
426
427         vd->drop_scroll_id = -1;
428         return FALSE;
429 }
430
431 static gint vdtree_auto_scroll_notify_cb(GtkWidget *widget, gint x, gint y, gpointer data)
432 {
433         ViewDir *vd = data;
434
435         if (!vd->drop_fd || vd->drop_list) return FALSE;
436
437         if (vd->drop_scroll_id == -1) vd->drop_scroll_id = g_idle_add(vdtree_auto_scroll_idle_cb, vd);
438
439         return TRUE;
440 }
441
442 static gint vdtree_dnd_drop_motion(GtkWidget *widget, GdkDragContext *context,
443                                    gint x, gint y, guint time, gpointer data)
444 {
445         ViewDir *vd = data;
446
447         vd->click_fd = NULL;
448
449         if (gtk_drag_get_source_widget(context) == vd->view)
450                 {
451                 gdk_drag_status(context, 0, time);
452                 return TRUE;
453                 }
454         else
455                 {
456                 gdk_drag_status(context, context->suggested_action, time);
457                 }
458
459         vdtree_drop_update(vd, x, y);
460
461         if (vd->drop_fd)
462                 {
463                 GtkAdjustment *adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vd->view));
464                 widget_auto_scroll_start(vd->view, adj, -1, -1, vdtree_auto_scroll_notify_cb, vd);
465                 }
466
467         return FALSE;
468 }
469
470 static void vdtree_dnd_drop_leave(GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
471 {
472         ViewDir *vd = data;
473
474         if (vd->drop_fd != vd->click_fd) vd_color_set(vd, vd->drop_fd, FALSE);
475
476         vd->drop_fd = NULL;
477
478         vdtree_dnd_drop_expand_cancel(vd);
479 }
480
481 static void vdtree_dnd_init(ViewDir *vd)
482 {
483         gtk_drag_source_set(vd->view, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
484                             dnd_file_drag_types, dnd_file_drag_types_count,
485                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
486         g_signal_connect(G_OBJECT(vd->view), "drag_data_get",
487                          G_CALLBACK(vdtree_dnd_get), vd);
488         g_signal_connect(G_OBJECT(vd->view), "drag_begin",
489                          G_CALLBACK(vdtree_dnd_begin), vd);
490         g_signal_connect(G_OBJECT(vd->view), "drag_end",
491                          G_CALLBACK(vdtree_dnd_end), vd);
492
493         vdtree_dest_set(vd, TRUE);
494         g_signal_connect(G_OBJECT(vd->view), "drag_data_received",
495                          G_CALLBACK(vdtree_dnd_drop_receive), vd);
496         g_signal_connect(G_OBJECT(vd->view), "drag_motion",
497                          G_CALLBACK(vdtree_dnd_drop_motion), vd);
498         g_signal_connect(G_OBJECT(vd->view), "drag_leave",
499                          G_CALLBACK(vdtree_dnd_drop_leave), vd);
500 }
501
502 /*
503  *----------------------------------------------------------------------------
504  * parts lists
505  *----------------------------------------------------------------------------
506  */
507
508 static GList *parts_list(const gchar *path)
509 {
510         GList *list = NULL;
511         const gchar *strb, *strp;
512         gint l;
513
514         strp = path;
515
516         if (*strp != '/') return NULL;
517
518         strp++;
519         strb = strp;
520         l = 0;
521
522         while (*strp != '\0')
523                 {
524                 if (*strp == '/')
525                         {
526                         if (l > 0) list = g_list_prepend(list, g_strndup(strb, l));
527                         strp++;
528                         strb = strp;
529                         l = 0;
530                         }
531                 else
532                         {
533                         strp++;
534                         l++;
535                         }
536                 }
537         if (l > 0) list = g_list_prepend(list, g_strndup(strb, l));
538
539         list = g_list_reverse(list);
540
541         list = g_list_prepend(list, g_strdup("/"));
542
543         return list;
544 }
545
546 static void parts_list_free(GList *list)
547 {
548         GList *work = list;
549         while (work)
550                 {
551                 PathData *pd = work->data;
552                 g_free(pd->name);
553                 g_free(pd);
554                 work = work->next;
555                 }
556
557         g_list_free(list);
558 }
559
560 static GList *parts_list_add_node_points(ViewDir *vd, GList *list)
561 {
562         GList *work;
563         GtkTreeModel *store;
564         GtkTreeIter iter;
565         gint valid;
566
567         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
568         valid = gtk_tree_model_get_iter_first(store, &iter);
569
570         work = list;
571         while (work)
572                 {
573                 PathData *pd;
574                 FileData *fd = NULL;
575
576                 pd = g_new0(PathData, 1);
577                 pd->name = work->data;
578
579                 while (valid && !fd)
580                         {
581                         NodeData *nd;
582
583                         gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
584                         if (strcmp(nd->fd->name, pd->name) == 0)
585                                 {
586                                 fd = nd->fd;
587                                 }
588                         else
589                                 {
590                                 valid = gtk_tree_model_iter_next(store, &iter);
591                                 }
592                         }
593
594                 pd->node = fd;
595                 work->data = pd;
596
597                 if (fd)
598                         {
599                         GtkTreeIter parent;
600                         memcpy(&parent, &iter, sizeof(parent));
601                         valid = gtk_tree_model_iter_children(store, &iter, &parent);
602                         }
603
604                 work = work->next;
605                 }
606
607         return list;
608 }
609
610 /*
611  *----------------------------------------------------------------------------
612  * misc
613  *----------------------------------------------------------------------------
614  */
615
616 #if 0
617 static void vdtree_row_deleted_cb(GtkTreeModel *tree_model, GtkTreePath *tpath, gpointer data)
618 {
619         GtkTreeIter iter;
620         NodeData *nd;
621
622         gtk_tree_model_get_iter(tree_model, &iter, tpath);
623         gtk_tree_model_get(tree_model, &iter, DIR_COLUMN_POINTER, &nd, -1);
624
625         if (!nd) return;
626
627         file_data_unref(nd->fd);
628         g_free(nd);
629 }
630 #endif
631
632 /*
633  *----------------------------------------------------------------------------
634  * node traversal, management
635  *----------------------------------------------------------------------------
636  */
637
638 static gint vdtree_find_iter_by_data(ViewDir *vd, GtkTreeIter *parent, NodeData *nd, GtkTreeIter *iter)
639 {
640         GtkTreeModel *store;
641
642         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
643         if (!nd || !gtk_tree_model_iter_children(store, iter, parent)) return -1;
644         do      {
645                 NodeData *cnd;
646
647                 gtk_tree_model_get(store, iter, DIR_COLUMN_POINTER, &cnd, -1);
648                 if (cnd == nd) return TRUE;
649                 } while (gtk_tree_model_iter_next(store, iter));
650
651         return FALSE;
652 }
653
654 static NodeData *vdtree_find_iter_by_name(ViewDir *vd, GtkTreeIter *parent, const gchar *name, GtkTreeIter *iter)
655 {
656         GtkTreeModel *store;
657
658         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
659         if (!name || !gtk_tree_model_iter_children(store, iter, parent)) return NULL;
660         do      {
661                 NodeData *nd;
662
663                 gtk_tree_model_get(store, iter, DIR_COLUMN_POINTER, &nd, -1);
664                 if (nd && strcmp(nd->fd->name, name) == 0) return nd;
665                 } while (gtk_tree_model_iter_next(store, iter));
666
667         return NULL;
668 }
669
670 static void vdtree_add_by_data(ViewDir *vd, FileData *fd, GtkTreeIter *parent)
671 {
672         GtkTreeStore *store;
673         GtkTreeIter child;
674         NodeData *nd;
675         GdkPixbuf *pixbuf;
676         NodeData *end;
677         GtkTreeIter empty;
678
679         if (!fd) return;
680
681         if (access_file(fd->path, R_OK | X_OK))
682                 {
683                 pixbuf = vd->pf->close;
684                 }
685         else
686                 {
687                 pixbuf = vd->pf->deny;
688                 }
689
690         nd = g_new0(NodeData, 1);
691         nd->fd = fd;
692         nd->expanded = FALSE;
693         nd->last_update = time(NULL);
694
695         store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view)));
696         gtk_tree_store_append(store, &child, parent);
697         gtk_tree_store_set(store, &child, DIR_COLUMN_POINTER, nd,
698                                          DIR_COLUMN_ICON, pixbuf,
699                                          DIR_COLUMN_NAME, nd->fd->name,
700                                          DIR_COLUMN_COLOR, FALSE, -1);
701
702         /* all nodes are created with an "empty" node, so that the expander is shown
703          * this is removed when the child is populated */
704         end = g_new0(NodeData, 1);
705         end->fd = file_data_new_simple("");
706         end->expanded = TRUE;
707
708         gtk_tree_store_append(store, &empty, &child);
709         gtk_tree_store_set(store, &empty, DIR_COLUMN_POINTER, end,
710                                           DIR_COLUMN_NAME, "empty", -1);
711
712         if (parent)
713                 {
714                 NodeData *pnd;
715                 GtkTreePath *tpath;
716
717                 gtk_tree_model_get(GTK_TREE_MODEL(store), parent, DIR_COLUMN_POINTER, &pnd, -1);
718                 tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(store), parent);
719                 if (options->tree_descend_subdirs &&
720                     gtk_tree_view_row_expanded(GTK_TREE_VIEW(vd->view), tpath) &&
721                     !nd->expanded)
722                         {
723                         vdtree_populate_path_by_iter(vd, &child, FALSE, vd->path);
724                         }
725                 gtk_tree_path_free(tpath);
726                 }
727 }
728
729 static gint vdtree_populate_path_by_iter(ViewDir *vd, GtkTreeIter *iter, gint force, const gchar *target_path)
730 {
731         GtkTreeModel *store;
732         GList *list;
733         GList *work;
734         GList *old;
735         time_t current_time;
736         GtkTreeIter child;
737         NodeData *nd;
738
739         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
740         gtk_tree_model_get(store, iter, DIR_COLUMN_POINTER, &nd, -1);
741
742         if (!nd) return FALSE;
743
744         current_time = time(NULL);
745         
746         if (nd->expanded)
747                 {
748                 if (!force && current_time - nd->last_update < 10) return TRUE;
749                 if (!isdir(nd->fd->path))
750                         {
751                         if (vd->click_fd == nd->fd) vd->click_fd = NULL;
752                         if (vd->drop_fd == nd->fd) vd->drop_fd = NULL;
753                         gtk_tree_store_remove(GTK_TREE_STORE(store), iter);
754                         vdtree_node_free(nd);
755                         return FALSE;
756                         }
757                 if (!force && filetime(nd->fd->path) == nd->fd->date) return TRUE;
758                 }
759
760         vdtree_busy_push(vd);
761
762         list = NULL;
763         filelist_read(nd->fd->path, NULL, &list);
764
765         /* when hidden files are not enabled, and the user enters a hidden path,
766          * allow the tree to display that path by specifically inserting the hidden entries
767          */
768         if (!options->file_filter.show_hidden_files &&
769             target_path &&
770             strncmp(nd->fd->path, target_path, strlen(nd->fd->path)) == 0)
771                 {
772                 gint n;
773
774                 n = strlen(nd->fd->path);
775                 if (target_path[n] == '/' && target_path[n+1] == '.')
776                         {
777                         gchar *name8;
778                         struct stat sbuf;
779
780                         n++;
781
782                         while (target_path[n] != '\0' && target_path[n] != '/') n++;
783                         name8 = g_strndup(target_path, n);
784
785                         if (stat_utf8(name8, &sbuf))
786                                 {
787                                 list = g_list_prepend(list, file_data_new_simple(name8));
788                                 }
789
790                         g_free(name8);
791                         }
792                 }
793
794         old = NULL;
795         if (gtk_tree_model_iter_children(store, &child, iter))
796                 {
797                 do      {
798                         NodeData *cnd;
799
800                         gtk_tree_model_get(store, &child, DIR_COLUMN_POINTER, &cnd, -1);
801                         old = g_list_prepend(old, cnd);
802                         } while (gtk_tree_model_iter_next(store, &child));
803                 }
804
805         work = list;
806         while (work)
807                 {
808                 FileData *fd;
809
810                 fd = work->data;
811                 work = work->next;
812
813                 if (strcmp(fd->name, ".") == 0 || strcmp(fd->name, "..") == 0)
814                         {
815                         file_data_unref(fd);
816                         }
817                 else
818                         {
819                         NodeData *cnd;
820
821                         cnd = vdtree_find_iter_by_name(vd, iter, fd->name, &child);
822                         if (cnd)
823                                 {
824                                 old = g_list_remove(old, cnd);
825                                 if (cnd->expanded && cnd->fd->date != fd->date &&
826                                     vdtree_populate_path_by_iter(vd, &child, FALSE, target_path))
827                                         {
828                                         cnd->fd->size = fd->size;
829                                         cnd->fd->date = fd->date;
830                                         }
831
832                                 file_data_unref(fd);
833                                 }
834                         else
835                                 {
836                                 vdtree_add_by_data(vd, fd, iter);
837                                 }
838                         }
839                 }
840
841         work = old;
842         while (work)
843                 {
844                 NodeData *cnd = work->data;
845                 work = work->next;
846
847                 if (vd->click_fd == cnd->fd) vd->click_fd = NULL;
848                 if (vd->drop_fd == cnd->fd) vd->drop_fd = NULL;
849
850                 if (vdtree_find_iter_by_data(vd, iter, cnd, &child))
851                         {
852                         gtk_tree_store_remove(GTK_TREE_STORE(store), &child);
853                         vdtree_node_free(cnd);
854                         }
855                 }
856
857         g_list_free(old);
858         g_list_free(list);
859
860         vdtree_busy_pop(vd);
861
862         nd->expanded = TRUE;
863         nd->last_update = current_time;
864
865         return TRUE;
866 }
867
868 FileData *vdtree_populate_path(ViewDir *vd, const gchar *path, gint expand, gint force)
869 {
870         GList *list;
871         GList *work;
872         FileData *fd = NULL;
873
874         if (!path) return NULL;
875
876         vdtree_busy_push(vd);
877
878         list = parts_list(path);
879         list = parts_list_add_node_points(vd, list);
880
881         work = list;
882         while (work)
883                 {
884                 PathData *pd = work->data;
885                 if (pd->node == NULL)
886                         {
887                         PathData *parent_pd;
888                         GtkTreeIter parent_iter;
889                         GtkTreeIter iter;
890                         NodeData *nd;
891
892                         if (work == list)
893                                 {
894                                 /* should not happen */
895                                 printf("vdtree warning, root node not found\n");
896                                 parts_list_free(list);
897                                 vdtree_busy_pop(vd);
898                                 return NULL;
899                                 }
900
901                         parent_pd = work->prev->data;
902
903                         if (!vdtree_find_row(vd, parent_pd->node, &parent_iter, NULL) ||
904                             !vdtree_populate_path_by_iter(vd, &parent_iter, force, path) ||
905                             (nd = vdtree_find_iter_by_name(vd, &parent_iter, pd->name, &iter)) == NULL)
906                                 {
907                                 printf("vdtree warning, aborted at %s\n", parent_pd->name);
908                                 parts_list_free(list);
909                                 vdtree_busy_pop(vd);
910                                 return NULL;
911                                 }
912
913                         pd->node = nd->fd;
914
915                         if (pd->node)
916                                 {
917                                 if (expand)
918                                         {
919                                         vdtree_expand_by_iter(vd, &parent_iter, TRUE);
920                                         vdtree_expand_by_iter(vd, &iter, TRUE);
921                                         }
922                                 vdtree_populate_path_by_iter(vd, &iter, force, path);
923                                 }
924                         }
925                 else
926                         {
927                         GtkTreeIter iter;
928
929                         if (vdtree_find_row(vd, pd->node, &iter, NULL))
930                                 {
931                                 if (expand) vdtree_expand_by_iter(vd, &iter, TRUE);
932                                 vdtree_populate_path_by_iter(vd, &iter, force, path);
933                                 }
934                         }
935
936                 work = work->next;
937                 }
938
939         work = g_list_last(list);
940         if (work)
941                 {
942                 PathData *pd = work->data;
943                 fd = pd->node;
944                 }
945         parts_list_free(list);
946
947         vdtree_busy_pop(vd);
948
949         return fd;
950 }
951
952 /*
953  *----------------------------------------------------------------------------
954  * access
955  *----------------------------------------------------------------------------
956  */
957
958 static gint selection_is_ok = FALSE;
959
960 static gboolean vdtree_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
961                                  gboolean path_currently_selected, gpointer data)
962 {
963         return selection_is_ok;
964 }
965
966 static void vdtree_select_row(ViewDir *vd, FileData *fd)
967 {
968         GtkTreeSelection *selection;
969         GtkTreeIter iter;
970                                                                                                                                
971         if (!vdtree_find_row(vd, fd, &iter, NULL)) return;
972         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vd->view));
973
974         /* hack, such that selection is only allowed to be changed from here */
975         selection_is_ok = TRUE;
976         gtk_tree_selection_select_iter(selection, &iter);
977         selection_is_ok = FALSE;
978
979         if (!vdtree_populate_path_by_iter(vd, &iter, FALSE, vd->path)) return;
980
981         vdtree_expand_by_iter(vd, &iter, TRUE);
982
983         if (fd && vd->select_func)
984                 {
985                 vd->select_func(vd, fd->path, vd->select_data);
986                 }
987 }
988
989 gint vdtree_set_path(ViewDir *vd, const gchar *path)
990 {
991         FileData *fd;
992         GtkTreeIter iter;
993
994         if (!path) return FALSE;
995         if (vd->path && strcmp(path, vd->path) == 0) return TRUE;
996
997         g_free(vd->path);
998         vd->path = g_strdup(path);
999
1000         fd = vdtree_populate_path(vd, vd->path, TRUE, FALSE);
1001
1002         if (!fd) return FALSE;
1003
1004         if (vdtree_find_row(vd, fd, &iter, NULL))
1005                 {
1006                 GtkTreeModel *store;
1007                 GtkTreePath *tpath;
1008
1009                 tree_view_row_make_visible(GTK_TREE_VIEW(vd->view), &iter, TRUE);
1010
1011                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
1012                 tpath = gtk_tree_model_get_path(store, &iter);
1013                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vd->view), tpath, NULL, FALSE);
1014                 gtk_tree_path_free(tpath);
1015
1016                 vdtree_select_row(vd, fd);
1017                 }
1018
1019         return TRUE;
1020 }
1021
1022 #if 0
1023 const gchar *vdtree_get_path(ViewDir *vd)
1024 {
1025         return vd->path;
1026 }
1027 #endif
1028
1029 void vdtree_refresh(ViewDir *vd)
1030 {
1031         vdtree_populate_path(vd, vd->path, FALSE, TRUE);
1032 }
1033
1034 const gchar *vdtree_row_get_path(ViewDir *vd, gint row)
1035 {
1036         printf("FIXME: no get row path\n");
1037         return NULL;
1038 }
1039
1040 /*
1041  *----------------------------------------------------------------------------
1042  * callbacks
1043  *----------------------------------------------------------------------------
1044  */
1045
1046 static void vdtree_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
1047 {
1048         ViewDir *vd = data;
1049         GtkTreeModel *store;
1050         GtkTreeIter iter;
1051         GtkTreePath *tpath;
1052         gint cw, ch;
1053
1054         if (vdtree_find_row(vd, vd->click_fd, &iter, NULL) < 0) return;
1055         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
1056         tpath = gtk_tree_model_get_path(store, &iter);
1057         tree_view_get_cell_clamped(GTK_TREE_VIEW(vd->view), tpath, 0, TRUE, x, y, &cw, &ch);
1058         gtk_tree_path_free(tpath);
1059         *y += ch;
1060         popup_menu_position_clamp(menu, x, y, 0);
1061 }
1062
1063 static gint vdtree_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1064 {
1065         ViewDir *vd = data;
1066         GtkTreePath *tpath;
1067         GtkTreeIter iter;
1068         FileData *fd = NULL;
1069
1070         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vd->view), &tpath, NULL);
1071         if (tpath)
1072                 {
1073                 GtkTreeModel *store;
1074                 NodeData *nd;
1075
1076                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
1077                 gtk_tree_model_get_iter(store, &iter, tpath);
1078                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
1079
1080                 gtk_tree_path_free(tpath);
1081
1082                 fd = (nd) ? nd->fd : NULL;
1083                 }
1084
1085         switch (event->keyval)
1086                 {
1087                 case GDK_Menu:
1088                         vd->click_fd = fd;
1089                         vd_color_set(vd, vd->click_fd, TRUE);
1090
1091                         vd->popup = vd_pop_menu(vd, vd->click_fd);
1092                         gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, vdtree_menu_position_cb, vd, 0, GDK_CURRENT_TIME);
1093
1094                         return TRUE;
1095                         break;
1096                 case GDK_plus:
1097                 case GDK_Right:
1098                 case GDK_KP_Add:
1099                         if (fd)
1100                                 {
1101                                 vdtree_populate_path_by_iter(vd, &iter, FALSE, vd->path);
1102                                 vdtree_icon_set_by_iter(vd, &iter, vd->pf->open);
1103                                 }
1104                         break;
1105                 }
1106
1107         return FALSE;
1108 }
1109
1110 static gint vdtree_clicked_on_expander(GtkTreeView *treeview, GtkTreePath *tpath,
1111                                        GtkTreeViewColumn *column, gint x, gint y, gint *left_of_expander)
1112 {
1113         gint depth;
1114         gint size;
1115         gint sep;
1116         gint exp_width;
1117
1118         if (column != gtk_tree_view_get_expander_column(treeview)) return FALSE;
1119
1120         gtk_widget_style_get(GTK_WIDGET(treeview), "expander-size", &size, "horizontal-separator", &sep, NULL);
1121         depth = gtk_tree_path_get_depth(tpath);
1122
1123         exp_width = sep + size + sep;
1124
1125         if (x <= depth * exp_width)
1126                 {
1127                 if (left_of_expander) *left_of_expander = !(x >= (depth - 1) * exp_width);
1128                 return TRUE;
1129                 }
1130
1131         return FALSE;
1132 }
1133
1134 static gint vdtree_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1135 {
1136         ViewDir *vd = data;
1137         GtkTreePath *tpath;
1138         GtkTreeViewColumn *column;
1139         GtkTreeIter iter;
1140         NodeData *nd = NULL;
1141
1142         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
1143                                           &tpath, &column, NULL, NULL))
1144                 {
1145                 GtkTreeModel *store;
1146                 gint left_of_expander;
1147
1148                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
1149                 gtk_tree_model_get_iter(store, &iter, tpath);
1150                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
1151                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
1152
1153                 if (vdtree_clicked_on_expander(GTK_TREE_VIEW(widget), tpath, column, bevent->x, bevent->y, &left_of_expander))
1154                         {
1155                         vd->click_fd = NULL;
1156
1157                         /* clicking this region should automatically reveal an expander, if necessary
1158                          * treeview bug: the expander will not expand until a button_motion_event highlights it.
1159                          */
1160                         if (bevent->button == 1 &&
1161                             !left_of_expander &&
1162                             !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vd->view), tpath))
1163                                 {
1164                                 vdtree_populate_path_by_iter(vd, &iter, FALSE, vd->path);
1165                                 vdtree_icon_set_by_iter(vd, &iter, vd->pf->open);
1166                                 }
1167
1168                         gtk_tree_path_free(tpath);
1169                         return FALSE;
1170                         }
1171
1172                 gtk_tree_path_free(tpath);
1173                 }
1174
1175         vd->click_fd = (nd) ? nd->fd : NULL;
1176         vd_color_set(vd, vd->click_fd, TRUE);
1177
1178         if (bevent->button == 3)
1179                 {
1180                 vd->popup = vd_pop_menu(vd, vd->click_fd);
1181                 gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, NULL, NULL,
1182                                bevent->button, bevent->time);
1183                 }
1184
1185         return (bevent->button != 1);
1186 }
1187
1188 static gint vdtree_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1189 {
1190         ViewDir *vd = data;
1191         GtkTreePath *tpath;
1192         GtkTreeIter iter;
1193         NodeData *nd = NULL;
1194
1195         if (!vd->click_fd) return FALSE;
1196         vd_color_set(vd, vd->click_fd, FALSE);
1197
1198         if (bevent->button != 1) return TRUE;
1199
1200         if ((bevent->x != 0 || bevent->y != 0) &&
1201             gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
1202                                           &tpath, NULL, NULL, NULL))
1203                 {
1204                 GtkTreeModel *store;
1205
1206                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
1207                 gtk_tree_model_get_iter(store, &iter, tpath);
1208                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
1209                 gtk_tree_path_free(tpath);
1210                 }
1211
1212         if (nd && vd->click_fd == nd->fd)
1213                 {
1214                 vdtree_select_row(vd, vd->click_fd);
1215                 }
1216
1217         return FALSE;
1218 }
1219
1220 static void vdtree_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *tpath, gpointer data)
1221 {
1222         ViewDir *vd = data;
1223
1224         vdtree_populate_path_by_iter(vd, iter, FALSE, NULL);
1225         vdtree_icon_set_by_iter(vd, iter, vd->pf->open);
1226 }
1227
1228 static void vdtree_row_collapsed(GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *tpath, gpointer data)
1229 {
1230         ViewDir *vd = data;
1231
1232         vdtree_icon_set_by_iter(vd, iter, vd->pf->close);
1233 }
1234
1235 static gint vdtree_sort_cb(GtkTreeModel *store, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
1236 {
1237         NodeData *nda;
1238         NodeData *ndb;
1239
1240         gtk_tree_model_get(store, a, DIR_COLUMN_POINTER, &nda, -1);
1241         gtk_tree_model_get(store, b, DIR_COLUMN_POINTER, &ndb, -1);
1242
1243         return CASE_SORT(nda->fd->name, ndb->fd->name);
1244 }
1245
1246 /*
1247  *----------------------------------------------------------------------------
1248  * core
1249  *----------------------------------------------------------------------------
1250  */
1251
1252 static void vdtree_setup_root(ViewDir *vd)
1253 {
1254         const gchar *path = "/";
1255         FileData *fd;
1256
1257
1258         fd = file_data_new_simple(path);
1259         vdtree_add_by_data(vd, fd, NULL);
1260
1261         vdtree_expand_by_data(vd, fd, TRUE);
1262         vdtree_populate_path(vd, path, FALSE, FALSE);
1263 }
1264
1265 static void vdtree_activate_cb(GtkTreeView *tview, GtkTreePath *tpath, GtkTreeViewColumn *column, gpointer data)
1266 {
1267         ViewDir *vd = data;
1268         GtkTreeModel *store;
1269         GtkTreeIter iter;
1270         NodeData *nd;
1271
1272         store = gtk_tree_view_get_model(tview);
1273         gtk_tree_model_get_iter(store, &iter, tpath);
1274         gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
1275
1276         vdtree_select_row(vd, nd->fd);
1277 }
1278
1279 static GdkColor *vdtree_color_shifted(GtkWidget *widget)
1280 {
1281         static GdkColor color;
1282         static GtkWidget *done = NULL;
1283
1284         if (done != widget)
1285                 {
1286                 GtkStyle *style;
1287
1288                 style = gtk_widget_get_style(widget);
1289                 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1290                 shift_color(&color, -1, 0);
1291                 done = widget;
1292                 }
1293
1294         return &color;
1295 }
1296
1297 static void vdtree_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1298                             GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1299 {
1300         ViewDir *vd = data;
1301         gboolean set;
1302
1303         gtk_tree_model_get(tree_model, iter, DIR_COLUMN_COLOR, &set, -1);
1304         g_object_set(G_OBJECT(cell),
1305                      "cell-background-gdk", vdtree_color_shifted(vd->view),
1306                      "cell-background-set", set, NULL);
1307 }
1308
1309 static gboolean vdtree_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data)
1310 {
1311         NodeData *nd;
1312
1313         gtk_tree_model_get(store, iter, DIR_COLUMN_POINTER, &nd, -1);
1314         vdtree_node_free(nd);
1315
1316         return FALSE;
1317 }
1318
1319 static void vdtree_destroy_cb(GtkWidget *widget, gpointer data)
1320 {
1321         ViewDir *vd = data;
1322         GtkTreeModel *store;
1323
1324         vdtree_dnd_drop_expand_cancel(vd);
1325         vdtree_dnd_drop_scroll_cancel(vd);
1326         widget_auto_scroll_stop(vd->view);
1327
1328         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
1329         gtk_tree_model_foreach(store, vdtree_destroy_node_cb, vd);
1330 }
1331
1332 ViewDir *vdtree_new(ViewDir *vd, const gchar *path)
1333 {
1334         GtkTreeStore *store;
1335         GtkTreeSelection *selection;
1336         GtkTreeViewColumn *column;
1337         GtkCellRenderer *renderer;
1338
1339         vd->info = g_new0(ViewDirInfoTree, 1);
1340         vd->type = DIRVIEW_TREE;
1341         vd->widget_destroy_cb = vdtree_destroy_cb;
1342
1343         VDTREE_INFO(vd, drop_expand_id) = -1;
1344
1345         VDTREE_INFO(vd, busy_ref) = 0;
1346
1347         store = gtk_tree_store_new(4, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
1348         vd->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1349         g_object_unref(store);
1350
1351         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vd->view), FALSE);
1352         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vd->view), FALSE);
1353         gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), vdtree_sort_cb, vd, NULL);
1354         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
1355                                              GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
1356
1357         g_signal_connect(G_OBJECT(vd->view), "row_activated",
1358                          G_CALLBACK(vdtree_activate_cb), vd);
1359         g_signal_connect(G_OBJECT(vd->view), "row_expanded",
1360                          G_CALLBACK(vdtree_row_expanded), vd);
1361         g_signal_connect(G_OBJECT(vd->view), "row_collapsed",
1362                          G_CALLBACK(vdtree_row_collapsed), vd);
1363 #if 0
1364         g_signal_connect(G_OBJECT(store), "row_deleted",
1365                          G_CALLBACK(vdtree_row_deleted_cb), vd);
1366 #endif
1367
1368         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vd->view));
1369         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1370         gtk_tree_selection_set_select_function(selection, vdtree_select_cb, vd, NULL);
1371
1372         column = gtk_tree_view_column_new();
1373         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1374
1375         renderer = gtk_cell_renderer_pixbuf_new();
1376         gtk_tree_view_column_pack_start(column, renderer, FALSE);
1377         gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", DIR_COLUMN_ICON);
1378         gtk_tree_view_column_set_cell_data_func(column, renderer, vdtree_color_cb, vd, NULL);
1379
1380         renderer = gtk_cell_renderer_text_new();
1381         gtk_tree_view_column_pack_start(column, renderer, TRUE);
1382         gtk_tree_view_column_add_attribute(column, renderer, "text", DIR_COLUMN_NAME);
1383         gtk_tree_view_column_set_cell_data_func(column, renderer, vdtree_color_cb, vd, NULL);
1384
1385         gtk_tree_view_append_column(GTK_TREE_VIEW(vd->view), column);
1386
1387         g_signal_connect(G_OBJECT(vd->view), "key_press_event",
1388                          G_CALLBACK(vdtree_press_key_cb), vd);
1389
1390         gtk_container_add(GTK_CONTAINER(vd->widget), vd->view);
1391         gtk_widget_show(vd->view);
1392
1393         vd->pf = folder_icons_new();
1394
1395         vdtree_setup_root(vd);
1396
1397         vdtree_dnd_init(vd);
1398
1399         g_signal_connect(G_OBJECT(vd->view), "button_press_event",
1400                          G_CALLBACK(vdtree_press_cb), vd);
1401         g_signal_connect(G_OBJECT(vd->view), "button_release_event",
1402                          G_CALLBACK(vdtree_release_cb), vd);
1403
1404         vdtree_set_path(vd, path);
1405
1406         return vd;
1407 }
1408
1409 #if 0
1410 void vdtree_set_click_func(ViewDir *vd,
1411                            void (*func)(ViewDir *vd, GdkEventButton *event, FileData *fd, gpointer), gpointer data)
1412 {
1413         if (!td) return;
1414         vd->click_func = func;
1415         vd->click_data = data;
1416 }
1417 #endif
1418
1419