Make directory view popup menu common and move it to view_dir.{c,h}.
[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_menu.h"
21 #include "utilops.h"
22 #include "view_dir_list.h"
23 #include "view_dir_tree.h"
24
25 GtkRadioActionEntry menu_view_dir_radio_entries[] = {
26   { "FolderList",       NULL,           N_("List"),             "<meta>L",      NULL, DIRVIEW_LIST },
27   { "FolderTree",       NULL,           N_("Tr_ee"),            "<control>T",   NULL, DIRVIEW_TREE },
28 };
29
30 void vd_destroy_cb(GtkWidget *widget, gpointer data)
31 {
32         ViewDir *vd = data;
33
34         if (vd->popup)
35                 {
36                 g_signal_handlers_disconnect_matched(G_OBJECT(vd->popup), G_SIGNAL_MATCH_DATA,
37                                                      0, 0, 0, NULL, vd);
38                 gtk_widget_destroy(vd->popup);
39                 }
40
41         if (vd->widget_destroy_cb) vd->widget_destroy_cb(widget, data);
42
43         if (vd->pf) folder_icons_free(vd->pf);
44         if (vd->drop_list) filelist_free(vd->drop_list);
45
46         if (vd->path) g_free(vd->path);
47         if (vd->info) g_free(vd->info);
48
49         g_free(vd);
50 }
51
52 ViewDir *vd_new(DirViewType type, const gchar *path)
53 {
54         ViewDir *vd = g_new0(ViewDir, 1);
55
56         vd->path = NULL;
57         vd->click_fd = NULL;
58
59         vd->drop_fd = NULL;
60         vd->drop_list = NULL;
61         vd->drop_scroll_id = -1;
62         vd->drop_list = NULL;
63
64         vd->popup = NULL;
65         vd->pf = 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         switch(type)
73         {
74         case DIRVIEW_LIST: vd = vdlist_new(vd, path); break;
75         case DIRVIEW_TREE: vd = vdtree_new(vd, path); break;
76         }
77
78         g_signal_connect(G_OBJECT(vd->widget), "destroy",
79                          G_CALLBACK(vd_destroy_cb), vd);
80
81         vd->pf = folder_icons_new();
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 void vd_color_set(ViewDir *vd, FileData *fd, gint color_set)
134 {
135         GtkTreeModel *store;
136         GtkTreeIter iter;
137
138         switch(vd->type)
139         {
140         case DIRVIEW_LIST:
141                 {
142                 if (vdlist_find_row(vd, fd, &iter) < 0) return;
143                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
144                 gtk_list_store_set(GTK_LIST_STORE(store), &iter, DIR_COLUMN_COLOR, color_set, -1);
145                 }
146                 break;
147         case DIRVIEW_TREE:
148                 {
149                 if (vdtree_find_row(vd, fd, &iter, NULL) < 0) return;
150                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
151                 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, DIR_COLUMN_COLOR, color_set, -1);
152                 }
153                 break;
154         }
155 }
156
157 void vd_popup_destroy_cb(GtkWidget *widget, gpointer data)
158 {
159         ViewDir *vd = data;
160
161         vd_color_set(vd, vd->click_fd, FALSE);
162         vd->click_fd = NULL;
163         vd->popup = NULL;
164
165         vd_color_set(vd, vd->drop_fd, FALSE);
166         filelist_free(vd->drop_list);
167         vd->drop_list = NULL;
168         vd->drop_fd = NULL;
169 }
170
171 /*
172  *-----------------------------------------------------------------------------
173  * drop menu (from dnd)
174  *-----------------------------------------------------------------------------
175  */
176
177 static void vd_drop_menu_copy_cb(GtkWidget *widget, gpointer data)
178 {
179         ViewDir *vd = data;
180         const gchar *path;
181         GList *list;
182
183         if (!vd->drop_fd) return;
184
185         path = vd->drop_fd->path;
186         list = vd->drop_list;
187         vd->drop_list = NULL;
188
189         file_util_copy_simple(list, path);
190 }
191
192 static void vd_drop_menu_move_cb(GtkWidget *widget, gpointer data)
193 {
194         ViewDir *vd = data;
195         const gchar *path;
196         GList *list;
197
198         if (!vd->drop_fd) return;
199
200         path = vd->drop_fd->path;
201         list = vd->drop_list;
202
203         vd->drop_list = NULL;
204
205         file_util_move_simple(list, path);
206 }
207
208 GtkWidget *vd_drop_menu(ViewDir *vd, gint active)
209 {
210         GtkWidget *menu;
211
212         menu = popup_menu_short_lived();
213         g_signal_connect(G_OBJECT(menu), "destroy",
214                          G_CALLBACK(vd_popup_destroy_cb), vd);
215
216         menu_item_add_stock_sensitive(menu, _("_Copy"), GTK_STOCK_COPY, active,
217                                       G_CALLBACK(vd_drop_menu_copy_cb), vd);
218         menu_item_add_sensitive(menu, _("_Move"), active, G_CALLBACK(vd_drop_menu_move_cb), vd);
219
220         menu_item_add_divider(menu);
221         menu_item_add_stock(menu, _("Cancel"), GTK_STOCK_CANCEL, NULL, vd);
222
223         return menu;
224 }
225
226 /*
227  *-----------------------------------------------------------------------------
228  * pop-up menu
229  *-----------------------------------------------------------------------------
230  */ 
231
232 static void vd_pop_menu_up_cb(GtkWidget *widget, gpointer data)
233 {
234         ViewDir *vd = data;
235         gchar *path;
236
237         if (!vd->path || strcmp(vd->path, "/") == 0) return;
238         path = remove_level_from_path(vd->path);
239
240         if (vd->select_func)
241                 {
242                 vd->select_func(vd, path, vd->select_data);
243                 }
244
245         g_free(path);
246 }
247
248 static void vd_pop_menu_slide_cb(GtkWidget *widget, gpointer data)
249 {
250         ViewDir *vd = data;
251         gchar *path;
252
253         if (!vd->layout) return;
254         if (!vd->click_fd) return;
255
256         path = vd->click_fd->path;
257
258         layout_set_path(vd->layout, path);
259         layout_select_none(vd->layout);
260         layout_image_slideshow_stop(vd->layout);
261         layout_image_slideshow_start(vd->layout);
262 }
263
264 static void vd_pop_menu_slide_rec_cb(GtkWidget *widget, gpointer data)
265 {
266         ViewDir *vd = data;
267         gchar *path;
268         GList *list;
269
270         if (!vd->layout) return;
271         if (!vd->click_fd) return;
272
273         path = vd->click_fd->path;
274
275         list = filelist_recursive(path);
276
277         layout_image_slideshow_stop(vd->layout);
278         layout_image_slideshow_start_from_list(vd->layout, list);
279 }
280
281 static void vd_pop_menu_dupe(ViewDir *vd, gint recursive)
282 {
283         DupeWindow *dw;
284         GList *list = NULL;
285
286         if (!vd->click_fd) return;
287
288         if (recursive)
289                 {
290                 list = g_list_append(list, file_data_ref(vd->click_fd));
291                 }
292         else
293                 {
294                 filelist_read(vd->click_fd->path, &list, NULL);
295                 list = filelist_filter(list, FALSE);
296                 }
297
298         dw = dupe_window_new(DUPE_MATCH_NAME);
299         dupe_window_add_files(dw, list, recursive);
300
301         filelist_free(list);
302 }
303
304 static void vd_pop_menu_dupe_cb(GtkWidget *widget, gpointer data)
305 {
306         ViewDir *vd = data;
307         vd_pop_menu_dupe(vd, FALSE);
308 }
309
310 static void vd_pop_menu_dupe_rec_cb(GtkWidget *widget, gpointer data)
311 {
312         ViewDir *vd = data;
313         vd_pop_menu_dupe(vd, TRUE);
314 }
315
316 static void vd_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
317 {
318         ViewDir *vd = data;
319
320         if (!vd->click_fd) return;
321         file_util_delete_dir(vd->click_fd, vd->widget);
322 }
323
324 static void vd_pop_menu_dir_view_as_cb(GtkWidget *widget, gpointer data)
325 {
326         ViewDir *vd = data;
327         DirViewType new_type = DIRVIEW_LIST;
328
329         if (!vd->layout) return;
330
331         switch(vd->type)
332         {
333         case DIRVIEW_LIST: new_type = DIRVIEW_TREE; break;
334         case DIRVIEW_TREE: new_type = DIRVIEW_LIST; break;
335         }
336         
337         layout_views_set(vd->layout, new_type, vd->layout->icon_view);
338 }
339
340 static void vd_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
341 {
342         ViewDir *vd = data;
343
344         if (vd->layout) layout_refresh(vd->layout);
345 }
346
347 static void vd_toggle_show_hidden_files_cb(GtkWidget *widget, gpointer data)
348 {
349         ViewDir *vd = data;
350
351         options->file_filter.show_hidden_files = !options->file_filter.show_hidden_files;
352         if (vd->layout) layout_refresh(vd->layout);
353 }
354
355 static void vd_pop_menu_new_cb(GtkWidget *widget, gpointer data)
356 {
357         ViewDir *vd = data;
358         const gchar *path = NULL;
359         gchar *new_path;
360         gchar *buf;
361
362         switch(vd->type)
363                 {
364                 case DIRVIEW_LIST:
365                         {
366                         if (!vd->path) return;
367                         path = vd->path;
368                         };
369                         break;
370                 case DIRVIEW_TREE:
371                         {
372                         if (!vd->click_fd) return;
373                         path = vd->click_fd->path;
374                         };
375                         break;
376                 }
377
378         buf = concat_dir_and_file(path, _("new_folder"));
379         new_path = unique_filename(buf, NULL, NULL, FALSE);
380         g_free(buf);
381         if (!new_path) return;
382
383         if (!mkdir_utf8(new_path, 0755))
384                 {
385                 gchar *text;
386
387                 text = g_strdup_printf(_("Unable to create folder:\n%s"), new_path);
388                 file_util_warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, vd->view);
389                 g_free(text);
390                 }
391         else
392                 {
393                 switch(vd->type)
394                         {
395                         case DIRVIEW_LIST:
396                                 {
397                                 FileData *fd;
398         
399                                 vd_refresh(vd);
400                                 fd = vdlist_row_by_path(vd, new_path, NULL);
401         
402                                 vdlist_rename_by_row(vd, fd);
403                                 };
404                                 break;
405                         case DIRVIEW_TREE:
406                                 {
407                                 FileData *fd;
408
409                                 fd = vdtree_populate_path(vd, new_path, TRUE, TRUE);
410                                 vdtree_rename_by_data(vd, fd);
411                                 };
412                                 break;
413                         }
414                 }
415
416         g_free(new_path);
417 }
418
419 static void vd_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
420 {
421         ViewDir *vd = data;
422         
423         switch(vd->type)
424         {
425         case DIRVIEW_LIST: vdlist_rename_by_row(vd, vd->click_fd); break;
426         case DIRVIEW_TREE: vdtree_rename_by_data(vd, vd->click_fd); break;
427         }
428 }
429
430 GtkWidget *vd_pop_menu(ViewDir *vd, FileData *fd)
431 {
432         GtkWidget *menu;
433         gint active;
434         gint rename_delete_active = FALSE;
435         gint new_folder_active = FALSE;
436
437         active = (fd != NULL);
438         if (fd)
439                 {
440                 switch(vd->type)
441                         {
442                         case DIRVIEW_LIST:
443                                 {
444                                 /* check using . (always row 0) */
445                                 new_folder_active = (vd->path && access_file(vd->path , W_OK | X_OK));
446
447                                 /* ignore .. and . */
448                                 rename_delete_active = (new_folder_active &&
449                                         strcmp(fd->name, ".") != 0 &&
450                                         strcmp(fd->name, "..") != 0 &&
451                                         access_file(fd->path, W_OK | X_OK));
452                                 };
453                                 break;
454                         case DIRVIEW_TREE:
455                                 {
456                                 gchar *parent;
457
458                                 new_folder_active = (fd && access_file(fd->path, W_OK | X_OK));
459                                 parent = remove_level_from_path(fd->path);
460                                 rename_delete_active = access_file(parent, W_OK | X_OK);
461                                 g_free(parent);
462                                 };
463                                 break;
464                         }
465
466                 }
467         menu = popup_menu_short_lived();
468         g_signal_connect(G_OBJECT(menu), "destroy",
469                          G_CALLBACK(vd_popup_destroy_cb), vd);
470
471         menu_item_add_stock_sensitive(menu, _("_Up to parent"), GTK_STOCK_GO_UP,
472                                       (vd->path && strcmp(vd->path, "/") != 0),
473                                       G_CALLBACK(vd_pop_menu_up_cb), vd);
474
475         menu_item_add_divider(menu);
476         menu_item_add_sensitive(menu, _("_Slideshow"), active,
477                                 G_CALLBACK(vd_pop_menu_slide_cb), vd);
478         menu_item_add_sensitive(menu, _("Slideshow recursive"), active,
479                                 G_CALLBACK(vd_pop_menu_slide_rec_cb), vd);
480
481         menu_item_add_divider(menu);
482         menu_item_add_stock_sensitive(menu, _("Find _duplicates..."), GTK_STOCK_FIND, active,
483                                       G_CALLBACK(vd_pop_menu_dupe_cb), vd);
484         menu_item_add_stock_sensitive(menu, _("Find duplicates recursive..."), GTK_STOCK_FIND, active,
485                                       G_CALLBACK(vd_pop_menu_dupe_rec_cb), vd);
486
487         menu_item_add_divider(menu);
488
489         menu_item_add_sensitive(menu, _("_New folder..."), new_folder_active,
490                                 G_CALLBACK(vd_pop_menu_new_cb), vd);
491
492         menu_item_add_sensitive(menu, _("_Rename..."), rename_delete_active,
493                                 G_CALLBACK(vd_pop_menu_rename_cb), vd);
494         menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, rename_delete_active,
495                                       G_CALLBACK(vd_pop_menu_delete_cb), vd);
496
497         menu_item_add_divider(menu);
498         /* FIXME */
499         menu_item_add_check(menu, _("View as _tree"), vd->type,
500                             G_CALLBACK(vd_pop_menu_dir_view_as_cb), vd);
501         menu_item_add_check(menu, _("Show _hidden files"), options->file_filter.show_hidden_files,
502                             G_CALLBACK(vd_toggle_show_hidden_files_cb), vd);
503
504         menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH,
505                             G_CALLBACK(vd_pop_menu_refresh_cb), vd);
506
507         return menu;
508 }
509