Fix new folder feature in dirlist mode, it was broken by revision 481.
[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 "dupe.h"
16 #include "filelist.h"
17 #include "layout_image.h"
18 #include "layout_util.h"
19 #include "ui_fileops.h"
20 #include "ui_tree_edit.h"
21 #include "ui_menu.h"
22 #include "utilops.h"
23 #include "view_dir_list.h"
24 #include "view_dir_tree.h"
25
26 GtkRadioActionEntry menu_view_dir_radio_entries[] = {
27   { "FolderList",       NULL,           N_("List"),             "<meta>L",      NULL, DIRVIEW_LIST },
28   { "FolderTree",       NULL,           N_("Tr_ee"),            "<control>T",   NULL, DIRVIEW_TREE },
29 };
30
31 void vd_destroy_cb(GtkWidget *widget, gpointer data)
32 {
33         ViewDir *vd = data;
34
35         if (vd->popup)
36                 {
37                 g_signal_handlers_disconnect_matched(G_OBJECT(vd->popup), G_SIGNAL_MATCH_DATA,
38                                                      0, 0, 0, NULL, vd);
39                 gtk_widget_destroy(vd->popup);
40                 }
41
42         if (vd->widget_destroy_cb) vd->widget_destroy_cb(widget, data);
43
44         if (vd->pf) folder_icons_free(vd->pf);
45         if (vd->drop_list) filelist_free(vd->drop_list);
46
47         if (vd->path) g_free(vd->path);
48         if (vd->info) g_free(vd->info);
49
50         g_free(vd);
51 }
52
53 ViewDir *vd_new(DirViewType type, const gchar *path)
54 {
55         ViewDir *vd = g_new0(ViewDir, 1);
56
57         vd->path = NULL;
58         vd->click_fd = NULL;
59
60         vd->drop_fd = NULL;
61         vd->drop_list = NULL;
62         vd->drop_scroll_id = -1;
63         vd->drop_list = NULL;
64
65         vd->popup = NULL;
66
67         vd->widget = gtk_scrolled_window_new(NULL, NULL);
68         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(vd->widget), GTK_SHADOW_IN);
69         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vd->widget),
70                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
71
72         vd->pf = folder_icons_new();
73
74         switch(type)
75         {
76         case DIRVIEW_LIST: vd = vdlist_new(vd, path); break;
77         case DIRVIEW_TREE: vd = vdtree_new(vd, path); break;
78         }
79
80         g_signal_connect(G_OBJECT(vd->widget), "destroy",
81                          G_CALLBACK(vd_destroy_cb), vd);
82
83         return vd;
84 }
85         
86 void vd_set_select_func(ViewDir *vd,
87                         void (*func)(ViewDir *vd, const gchar *path, gpointer data), gpointer data)
88 {
89         vd->select_func = func;
90         vd->select_data = data;
91 }
92
93 void vd_set_layout(ViewDir *vd, LayoutWindow *layout)
94 {
95         vd->layout = layout;
96 }
97
98 gint vd_set_path(ViewDir *vd, const gchar *path)
99 {
100         gint ret = FALSE;
101
102         switch(vd->type)
103         {
104         case DIRVIEW_LIST: ret = vdlist_set_path(vd, path); break;
105         case DIRVIEW_TREE: ret = vdtree_set_path(vd, path); break;
106         }
107
108         return ret;
109 }
110
111 void vd_refresh(ViewDir *vd)
112 {
113         switch(vd->type)
114         {
115         case DIRVIEW_LIST: return vdlist_refresh(vd);
116         case DIRVIEW_TREE: return vdtree_refresh(vd);
117         }
118 }
119
120 const gchar *vd_row_get_path(ViewDir *vd, gint row)
121 {
122         const gchar *ret = NULL;
123
124         switch(vd->type)
125         {
126         case DIRVIEW_LIST: ret = vdlist_row_get_path(vd, row); break;
127         case DIRVIEW_TREE: ret = vdtree_row_get_path(vd, row); break;
128         }
129
130         return ret;
131 }
132
133 gint vd_find_row(ViewDir *vd, FileData *fd, GtkTreeIter *iter)
134 {
135         gint ret = FALSE;
136
137         switch(vd->type)
138         {
139         case DIRVIEW_LIST: ret = vdlist_find_row(vd, fd, iter); break;
140         case DIRVIEW_TREE: ret = vdtree_find_row(vd, fd, iter, NULL); break;
141         }
142
143         return ret;
144 }
145
146 static gint vd_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
147 {
148         ViewDir *vd = data;
149         GtkTreeModel *store;
150         GtkTreeIter iter;
151         FileData *fd;
152         gchar *old_path;
153         gchar *new_path;
154         gchar *base;
155
156         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
157         if (!gtk_tree_model_get_iter(store, &iter, td->path)) return FALSE;
158
159         switch(vd->type)
160         {
161         case DIRVIEW_LIST:
162                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
163                 break;
164         case DIRVIEW_TREE:
165                 {
166                 NodeData *nd;
167                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1);
168                 if (!nd) return FALSE;
169                 fd = nd->fd;
170                 };
171                 break;
172         }
173
174         if (!fd) return FALSE;
175
176         old_path = g_strdup(fd->path);
177
178         base = remove_level_from_path(old_path);
179         new_path = concat_dir_and_file(base, new);
180         g_free(base);
181
182         if (file_util_rename_dir(fd, new_path, vd->view))
183                 {
184
185                 if (vd->type == DIRVIEW_TREE) vdtree_populate_path(vd, new_path, TRUE, TRUE);
186                 if (vd->layout && strcmp(vd->path, old_path) == 0)
187                         {
188                         layout_set_path(vd->layout, new_path);
189                         }
190                 else
191                         {
192                         if (vd->type == DIRVIEW_LIST) vd_refresh(vd);
193                         }
194                 }
195
196         g_free(old_path);
197         g_free(new_path);
198
199         return FALSE;
200 }
201
202 static void vd_rename_by_data(ViewDir *vd, FileData *fd)
203 {
204         GtkTreeModel *store;
205         GtkTreePath *tpath;
206         GtkTreeIter iter;
207
208         if (!fd || vd_find_row(vd, fd, &iter) < 0) return;
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                           vd_rename_cb, vd);
214         gtk_tree_path_free(tpath);
215 }
216
217
218 void vd_color_set(ViewDir *vd, FileData *fd, gint color_set)
219 {
220         GtkTreeModel *store;
221         GtkTreeIter iter;
222
223         if (vd_find_row(vd, fd, &iter) < 0) return;
224         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
225
226         switch(vd->type)
227         {
228         case DIRVIEW_LIST:
229                 gtk_list_store_set(GTK_LIST_STORE(store), &iter, DIR_COLUMN_COLOR, color_set, -1);
230                 break;
231         case DIRVIEW_TREE:
232                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, DIR_COLUMN_COLOR, color_set, -1);
233                 break;
234         }
235 }
236
237 void vd_popup_destroy_cb(GtkWidget *widget, gpointer data)
238 {
239         ViewDir *vd = data;
240
241         vd_color_set(vd, vd->click_fd, FALSE);
242         vd->click_fd = NULL;
243         vd->popup = NULL;
244
245         vd_color_set(vd, vd->drop_fd, FALSE);
246         filelist_free(vd->drop_list);
247         vd->drop_list = NULL;
248         vd->drop_fd = NULL;
249 }
250
251 /*
252  *-----------------------------------------------------------------------------
253  * drop menu (from dnd)
254  *-----------------------------------------------------------------------------
255  */
256
257 static void vd_drop_menu_copy_cb(GtkWidget *widget, gpointer data)
258 {
259         ViewDir *vd = data;
260         const gchar *path;
261         GList *list;
262
263         if (!vd->drop_fd) return;
264
265         path = vd->drop_fd->path;
266         list = vd->drop_list;
267         vd->drop_list = NULL;
268
269         file_util_copy_simple(list, path);
270 }
271
272 static void vd_drop_menu_move_cb(GtkWidget *widget, gpointer data)
273 {
274         ViewDir *vd = data;
275         const gchar *path;
276         GList *list;
277
278         if (!vd->drop_fd) return;
279
280         path = vd->drop_fd->path;
281         list = vd->drop_list;
282
283         vd->drop_list = NULL;
284
285         file_util_move_simple(list, path);
286 }
287
288 GtkWidget *vd_drop_menu(ViewDir *vd, gint active)
289 {
290         GtkWidget *menu;
291
292         menu = popup_menu_short_lived();
293         g_signal_connect(G_OBJECT(menu), "destroy",
294                          G_CALLBACK(vd_popup_destroy_cb), vd);
295
296         menu_item_add_stock_sensitive(menu, _("_Copy"), GTK_STOCK_COPY, active,
297                                       G_CALLBACK(vd_drop_menu_copy_cb), vd);
298         menu_item_add_sensitive(menu, _("_Move"), active, G_CALLBACK(vd_drop_menu_move_cb), vd);
299
300         menu_item_add_divider(menu);
301         menu_item_add_stock(menu, _("Cancel"), GTK_STOCK_CANCEL, NULL, vd);
302
303         return menu;
304 }
305
306 /*
307  *-----------------------------------------------------------------------------
308  * pop-up menu
309  *-----------------------------------------------------------------------------
310  */ 
311
312 static void vd_pop_menu_up_cb(GtkWidget *widget, gpointer data)
313 {
314         ViewDir *vd = data;
315         gchar *path;
316
317         if (!vd->path || strcmp(vd->path, "/") == 0) return;
318         path = remove_level_from_path(vd->path);
319
320         if (vd->select_func)
321                 {
322                 vd->select_func(vd, path, vd->select_data);
323                 }
324
325         g_free(path);
326 }
327
328 static void vd_pop_menu_slide_cb(GtkWidget *widget, gpointer data)
329 {
330         ViewDir *vd = data;
331         gchar *path;
332
333         if (!vd->layout) return;
334         if (!vd->click_fd) return;
335
336         path = vd->click_fd->path;
337
338         layout_set_path(vd->layout, path);
339         layout_select_none(vd->layout);
340         layout_image_slideshow_stop(vd->layout);
341         layout_image_slideshow_start(vd->layout);
342 }
343
344 static void vd_pop_menu_slide_rec_cb(GtkWidget *widget, gpointer data)
345 {
346         ViewDir *vd = data;
347         gchar *path;
348         GList *list;
349
350         if (!vd->layout) return;
351         if (!vd->click_fd) return;
352
353         path = vd->click_fd->path;
354
355         list = filelist_recursive(path);
356
357         layout_image_slideshow_stop(vd->layout);
358         layout_image_slideshow_start_from_list(vd->layout, list);
359 }
360
361 static void vd_pop_menu_dupe(ViewDir *vd, gint recursive)
362 {
363         DupeWindow *dw;
364         GList *list = NULL;
365
366         if (!vd->click_fd) return;
367
368         if (recursive)
369                 {
370                 list = g_list_append(list, file_data_ref(vd->click_fd));
371                 }
372         else
373                 {
374                 filelist_read(vd->click_fd->path, &list, NULL);
375                 list = filelist_filter(list, FALSE);
376                 }
377
378         dw = dupe_window_new(DUPE_MATCH_NAME);
379         dupe_window_add_files(dw, list, recursive);
380
381         filelist_free(list);
382 }
383
384 static void vd_pop_menu_dupe_cb(GtkWidget *widget, gpointer data)
385 {
386         ViewDir *vd = data;
387         vd_pop_menu_dupe(vd, FALSE);
388 }
389
390 static void vd_pop_menu_dupe_rec_cb(GtkWidget *widget, gpointer data)
391 {
392         ViewDir *vd = data;
393         vd_pop_menu_dupe(vd, TRUE);
394 }
395
396 static void vd_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
397 {
398         ViewDir *vd = data;
399
400         if (!vd->click_fd) return;
401         file_util_delete_dir(vd->click_fd, vd->widget);
402 }
403
404 static void vd_pop_menu_dir_view_as_cb(GtkWidget *widget, gpointer data)
405 {
406         ViewDir *vd = data;
407         DirViewType new_type = DIRVIEW_LIST;
408
409         if (!vd->layout) return;
410
411         switch(vd->type)
412         {
413         case DIRVIEW_LIST: new_type = DIRVIEW_TREE; break;
414         case DIRVIEW_TREE: new_type = DIRVIEW_LIST; break;
415         }
416         
417         layout_views_set(vd->layout, new_type, vd->layout->icon_view);
418 }
419
420 static void vd_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
421 {
422         ViewDir *vd = data;
423
424         if (vd->layout) layout_refresh(vd->layout);
425 }
426
427 static void vd_toggle_show_hidden_files_cb(GtkWidget *widget, gpointer data)
428 {
429         ViewDir *vd = data;
430
431         options->file_filter.show_hidden_files = !options->file_filter.show_hidden_files;
432         if (vd->layout) layout_refresh(vd->layout);
433 }
434
435 static void vd_pop_menu_new_cb(GtkWidget *widget, gpointer data)
436 {
437         ViewDir *vd = data;
438         const gchar *path = NULL;
439         gchar *new_path;
440         gchar *buf;
441
442         switch(vd->type)
443                 {
444                 case DIRVIEW_LIST:
445                         {
446                         if (!vd->path) return;
447                         path = vd->path;
448                         };
449                         break;
450                 case DIRVIEW_TREE:
451                         {
452                         if (!vd->click_fd) return;
453                         path = vd->click_fd->path;
454                         };
455                         break;
456                 }
457
458         buf = concat_dir_and_file(path, _("new_folder"));
459         new_path = unique_filename(buf, NULL, NULL, FALSE);
460         g_free(buf);
461         if (!new_path) return;
462
463         if (!mkdir_utf8(new_path, 0755))
464                 {
465                 gchar *text;
466
467                 text = g_strdup_printf(_("Unable to create folder:\n%s"), new_path);
468                 file_util_warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, vd->view);
469                 g_free(text);
470                 }
471         else
472                 {
473                 FileData *fd = NULL;
474
475                 switch(vd->type)
476                         {
477                         case DIRVIEW_LIST:
478                                 {
479                                 vd_refresh(vd);
480                                 fd = vdlist_row_by_path(vd, new_path, NULL);
481                                 };
482                                 break;
483                         case DIRVIEW_TREE:
484                                 fd = vdtree_populate_path(vd, new_path, TRUE, TRUE);
485                                 break;
486                         }
487                 vd_rename_by_data(vd, fd);
488                 }
489
490         g_free(new_path);
491 }
492
493 static void vd_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
494 {
495         ViewDir *vd = data;
496         
497         vd_rename_by_data(vd, vd->click_fd);
498 }
499
500 GtkWidget *vd_pop_menu(ViewDir *vd, FileData *fd)
501 {
502         GtkWidget *menu;
503         gint active;
504         gint rename_delete_active = FALSE;
505         gint new_folder_active = FALSE;
506
507         active = (fd != NULL);
508         switch(vd->type)
509                 {
510                 case DIRVIEW_LIST:
511                         {
512                         /* check using . (always row 0) */
513                         new_folder_active = (vd->path && access_file(vd->path , W_OK | X_OK));
514
515                         /* ignore .. and . */
516                         rename_delete_active = (new_folder_active && fd &&
517                                 strcmp(fd->name, ".") != 0 &&
518                                 strcmp(fd->name, "..") != 0 &&
519                                 access_file(fd->path, W_OK | X_OK));
520                         };
521                         break;
522                 case DIRVIEW_TREE:
523                         {
524                         if (fd)
525                                 {
526                                 gchar *parent;
527                                 new_folder_active = (fd && access_file(fd->path, W_OK | X_OK));
528                                 parent = remove_level_from_path(fd->path);
529                                 rename_delete_active = access_file(parent, W_OK | X_OK);
530                                 g_free(parent);
531                                 };
532                         }
533                         break;
534                 }
535
536         menu = popup_menu_short_lived();
537         g_signal_connect(G_OBJECT(menu), "destroy",
538                          G_CALLBACK(vd_popup_destroy_cb), vd);
539
540         menu_item_add_stock_sensitive(menu, _("_Up to parent"), GTK_STOCK_GO_UP,
541                                       (vd->path && strcmp(vd->path, "/") != 0),
542                                       G_CALLBACK(vd_pop_menu_up_cb), vd);
543
544         menu_item_add_divider(menu);
545         menu_item_add_sensitive(menu, _("_Slideshow"), active,
546                                 G_CALLBACK(vd_pop_menu_slide_cb), vd);
547         menu_item_add_sensitive(menu, _("Slideshow recursive"), active,
548                                 G_CALLBACK(vd_pop_menu_slide_rec_cb), vd);
549
550         menu_item_add_divider(menu);
551         menu_item_add_stock_sensitive(menu, _("Find _duplicates..."), GTK_STOCK_FIND, active,
552                                       G_CALLBACK(vd_pop_menu_dupe_cb), vd);
553         menu_item_add_stock_sensitive(menu, _("Find duplicates recursive..."), GTK_STOCK_FIND, active,
554                                       G_CALLBACK(vd_pop_menu_dupe_rec_cb), vd);
555
556         menu_item_add_divider(menu);
557
558         menu_item_add_sensitive(menu, _("_New folder..."), new_folder_active,
559                                 G_CALLBACK(vd_pop_menu_new_cb), vd);
560
561         menu_item_add_sensitive(menu, _("_Rename..."), rename_delete_active,
562                                 G_CALLBACK(vd_pop_menu_rename_cb), vd);
563         menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, rename_delete_active,
564                                       G_CALLBACK(vd_pop_menu_delete_cb), vd);
565
566         menu_item_add_divider(menu);
567         /* FIXME */
568         menu_item_add_check(menu, _("View as _tree"), vd->type,
569                             G_CALLBACK(vd_pop_menu_dir_view_as_cb), vd);
570         menu_item_add_check(menu, _("Show _hidden files"), options->file_filter.show_hidden_files,
571                             G_CALLBACK(vd_toggle_show_hidden_files_cb), vd);
572
573         menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH,
574                             G_CALLBACK(vd_pop_menu_refresh_cb), vd);
575
576         return menu;
577 }
578