64e2ba352ac64a38c6807bd9f95876cea261e406
[geeqie.git] / src / utilops.c
1 /*
2  * GQview
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
13 #include "gqview.h"
14 #include "utilops.h"
15
16
17 #include "cache.h"
18 #include "cache_maint.h"
19 #include "collect.h"
20 #include "dupe.h"
21 #include "filelist.h"
22 #include "image.h"
23 #include "img-view.h"
24 #include "layout.h"
25 #include "search.h"
26 #include "thumb_standard.h"
27 #include "ui_bookmark.h"
28 #include "ui_fileops.h"
29 #include "ui_misc.h"
30 #include "ui_tabcomp.h"
31
32
33 /*
34  *--------------------------------------------------------------------------
35  * call these when names change, files move, deleted, etc.
36  * so that any open windows are also updated
37  *--------------------------------------------------------------------------
38  */
39
40 void file_maint_renamed(const gchar *source, const gchar *dest)
41 {
42         cache_maint_moved(source, dest);
43         collection_maint_renamed(source, dest);
44
45         layout_maint_renamed(source, dest);
46         view_window_maint_moved(source, dest);
47         dupe_maint_renamed(source, dest);
48         search_maint_renamed(source, dest);
49 }
50
51 /* under most cases ignore_list should be NULL */
52 void file_maint_removed(const gchar *path, GList *ignore_list)
53 {
54         layout_maint_removed(path, ignore_list);
55         view_window_maint_removed(path, ignore_list);
56         dupe_maint_removed(path);
57         search_maint_removed(path);
58
59         collection_maint_removed(path);
60         cache_maint_removed(path);
61 }
62
63 /* special case for correct main window behavior */
64 void file_maint_moved(const gchar *source, const gchar *dest, GList *ignore_list)
65 {
66         cache_maint_moved(source, dest);
67         collection_maint_renamed(source, dest);
68
69         layout_maint_moved(source, dest, ignore_list);
70         view_window_maint_moved(source, dest);
71         dupe_maint_renamed(source, dest);
72         search_maint_renamed(source, dest);
73 }
74
75 void file_maint_copied(const gchar *source, const gchar *dest)
76 {
77         cache_maint_copied(source, dest);
78 }
79
80 /*
81  *--------------------------------------------------------------------------
82  * The file manipulation dialogs
83  *--------------------------------------------------------------------------
84  */
85
86
87 enum {
88         DIALOG_NEW_DIR,
89         DIALOG_COPY,
90         DIALOG_MOVE,
91         DIALOG_DELETE,
92         DIALOG_RENAME
93 };
94
95 typedef struct _FileDataMult FileDataMult;
96 struct _FileDataMult
97 {
98         gint confirm_all;
99         gint confirmed;
100         gint skip;
101         GList *source_list;
102         GList *source_next;
103         gchar *dest_base;
104         gchar *source;
105         gchar *dest;
106         gint copy;
107
108         gint rename;
109         gint rename_auto;
110         gint rename_all;
111
112         GtkWidget *rename_box;
113         GtkWidget *rename_entry;
114         GtkWidget *rename_auto_box;
115
116         GtkWidget *yes_all_button;
117 };
118
119 typedef struct _FileDataSingle FileDataSingle;
120 struct _FileDataSingle
121 {
122         gint confirmed;
123         gchar *source;
124         gchar *dest;
125         gint copy;
126
127         gint rename;
128         gint rename_auto;
129
130         GtkWidget *rename_box;
131         GtkWidget *rename_entry;
132         GtkWidget *rename_auto_box;
133 };
134
135 /*
136  *--------------------------------------------------------------------------
137  * Adds 1 or 2 images (if 2, side by side) to a GenericDialog
138  *--------------------------------------------------------------------------
139  */
140
141 #define DIALOG_DEF_IMAGE_DIM_X 200
142 #define DIALOG_DEF_IMAGE_DIM_Y 150
143
144 static void generic_dialog_add_image(GenericDialog *gd, GtkWidget *box,
145                                      const gchar *path1, const gchar *header1,
146                                      const gchar *path2, const gchar *header2,
147                                      gint show_filename)
148 {
149         ImageWindow *imd;
150         GtkWidget *hbox = NULL;
151         GtkWidget *vbox;
152         GtkWidget *label = NULL;
153
154         if (!box) box = gd->vbox;
155
156         if (path2)
157                 {
158                 hbox = pref_box_new(box, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
159                 }
160
161         /* image 1 */
162
163         vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP);
164         if (hbox)
165                 {
166                 GtkWidget *sep;
167
168                 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
169
170                 sep = gtk_vseparator_new();
171                 gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
172                 gtk_widget_show(sep);
173                 }
174         else
175                 {
176                 gtk_box_pack_start(GTK_BOX(box), vbox, TRUE, TRUE, PREF_PAD_GAP);
177                 }
178         gtk_widget_show(vbox);
179
180         if (header1)
181                 {
182                 GtkWidget *head;
183
184                 head = pref_label_new(vbox, header1);
185                 pref_label_bold(head, TRUE, FALSE);
186                 gtk_misc_set_alignment(GTK_MISC(head), 0.0, 0.5);
187                 }
188
189         imd = image_new(FALSE);
190         g_object_set(G_OBJECT(imd->pr), "zoom_expand", FALSE, NULL);
191         gtk_widget_set_size_request(imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
192         gtk_box_pack_start(GTK_BOX(vbox), imd->widget, TRUE, TRUE, 0);
193         image_change_path(imd, path1, 0.0);
194         gtk_widget_show(imd->widget);
195
196         if (show_filename)
197                 {
198                 label = pref_label_new(vbox, (path1 == NULL) ? "" : filename_from_path(path1));
199                 }
200
201         /* only the first image is stored (for use in gd_image_set) */
202         g_object_set_data(G_OBJECT(gd->dialog), "img_image", imd);
203         g_object_set_data(G_OBJECT(gd->dialog), "img_label", label);
204                 
205
206         /* image 2 */
207
208         if (hbox && path2)
209                 {
210                 vbox = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
211
212                 if (header2)
213                         {
214                         GtkWidget *head;
215
216                         head = pref_label_new(vbox, header2);
217                         pref_label_bold(head, TRUE, FALSE);
218                         gtk_misc_set_alignment(GTK_MISC(head), 0.0, 0.5);
219                         }
220
221                 imd = image_new(FALSE);
222                 g_object_set(G_OBJECT(imd->pr), "zoom_expand", FALSE, NULL);
223                 gtk_widget_set_size_request(imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
224                 gtk_box_pack_start(GTK_BOX(vbox), imd->widget, TRUE, TRUE, 0);
225                 image_change_path(imd, path2, 0.0);
226                 gtk_widget_show(imd->widget);
227
228                 pref_label_new(vbox, filename_from_path(path2));
229                 }
230 }
231
232 static void generic_dialog_image_set(GenericDialog *gd, const gchar *path)
233 {
234         ImageWindow *imd;
235         GtkWidget *label;
236         
237         imd = g_object_get_data(G_OBJECT(gd->dialog), "img_image");
238         label = g_object_get_data(G_OBJECT(gd->dialog), "img_label");
239
240         if (!imd) return;
241
242         image_change_path(imd, path, 0.0);
243         if (label) gtk_label_set_text(GTK_LABEL(label), filename_from_path(path));
244 }
245
246 /*
247  *--------------------------------------------------------------------------
248  * Wrappers to aid in setting additional dialog properties (unde mouse, etc.)
249  *--------------------------------------------------------------------------
250  */
251
252 GenericDialog *file_util_gen_dlg(const gchar *title,
253                                  const gchar *wmclass, const gchar *wmsubclass,
254                                  GtkWidget *parent, gint auto_close,
255                                  void (*cancel_cb)(GenericDialog *, gpointer), gpointer data)
256 {
257         GenericDialog *gd;
258
259         gd = generic_dialog_new(title, wmclass, wmsubclass, parent, auto_close, cancel_cb, data);
260         if (place_dialogs_under_mouse)
261                 {
262                 gtk_window_set_position(GTK_WINDOW(gd->dialog), GTK_WIN_POS_MOUSE);
263                 }
264
265         return gd;
266 }
267
268 FileDialog *file_util_file_dlg(const gchar *title,
269                                const gchar *wmclass, const gchar *wmsubclass,
270                                GtkWidget *parent,
271                                void (*cancel_cb)(FileDialog *, gpointer), gpointer data)
272 {
273         FileDialog *fd;
274
275         fd = file_dialog_new(title, wmclass, wmsubclass, parent, cancel_cb, data);
276         if (place_dialogs_under_mouse)
277                 {
278                 gtk_window_set_position(GTK_WINDOW(GENERIC_DIALOG(fd)->dialog), GTK_WIN_POS_MOUSE);
279                 }
280
281         return fd;
282 }
283
284 /* this warning dialog is copied from SLIK's ui_utildg.c,
285  * because it does not have a mouse center option,
286  * and we must center it before show, implement it here.
287  */
288 static void file_util_warning_dialog_ok_cb(GenericDialog *gd, gpointer data)
289 {
290         /* no op */
291 }
292
293 GenericDialog *file_util_warning_dialog(const gchar *heading, const gchar *message,
294                                         const gchar *icon_stock_id, GtkWidget *parent)
295 {
296         GenericDialog *gd;
297
298         gd = file_util_gen_dlg(heading, "GQview", "warning", parent, TRUE, NULL, NULL);
299         generic_dialog_add_message(gd, icon_stock_id, heading, message);
300         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, file_util_warning_dialog_ok_cb, TRUE);
301         if (place_dialogs_under_mouse)
302                 {
303                 gtk_window_set_position(GTK_WINDOW(gd->dialog), GTK_WIN_POS_MOUSE);
304                 }
305         gtk_widget_show(gd->dialog);
306
307         return gd;
308 }
309
310 static gint filename_base_length(const gchar *name)
311 {
312         gint n;
313
314         if (!name) return 0;
315
316         n = strlen(name);
317
318         if (filter_name_exists(name))
319                 {
320                 const gchar *ext;
321
322                 ext = extension_from_path(name);
323                 if (ext) n -= strlen(ext);
324                 }
325
326         return n;
327 }
328
329
330 /*
331  *--------------------------------------------------------------------------
332  * Move and Copy routines
333  *--------------------------------------------------------------------------
334  */
335
336 /*
337  * Multi file move
338  */
339
340 static FileDataMult *file_data_multiple_new(GList *source_list, const gchar *dest, gint copy)
341 {
342         FileDataMult *fdm = g_new0(FileDataMult, 1);
343         fdm->confirm_all = FALSE;
344         fdm->confirmed = FALSE;
345         fdm->skip = FALSE;
346         fdm->source_list = source_list;
347         fdm->source_next = fdm->source_list;
348         fdm->dest_base = g_strdup(dest);
349         fdm->source = NULL;
350         fdm->dest = NULL;
351         fdm->copy = copy;
352         return fdm;
353 }
354
355 static void file_data_multiple_free(FileDataMult *fdm)
356 {
357         path_list_free(fdm->source_list);
358         g_free(fdm->dest_base);
359         g_free(fdm->dest);
360         g_free(fdm);
361 }
362
363 static void file_util_move_multiple(FileDataMult *fdm);
364
365 static void file_util_move_multiple_ok_cb(GenericDialog *gd, gpointer data)
366 {
367         FileDataMult *fdm = data;
368
369         fdm->confirmed = TRUE;
370
371         if (fdm->rename_auto)
372                 {
373                 gchar *buf;
374
375                 buf = unique_filename_simple(fdm->dest);
376                 if (buf)
377                         {
378                         g_free(fdm->dest);
379                         fdm->dest = buf;
380                         }
381                 else
382                         {
383                         /* unique failed? well, return to the overwrite prompt :( */
384                         fdm->confirmed = FALSE;
385                         }
386                 }
387         else if (fdm->rename)
388                 {
389                 const gchar *name;
390
391                 name = gtk_entry_get_text(GTK_ENTRY(fdm->rename_entry));
392                 if (strlen(name) == 0 ||
393                     strcmp(name, filename_from_path(fdm->source)) == 0)
394                         {
395                         fdm->confirmed = FALSE;
396                         }
397                 else
398                         {
399                         g_free(fdm->dest);
400                         fdm->dest = concat_dir_and_file(fdm->dest_base, name);
401                         fdm->confirmed = !isname(fdm->dest);
402                         }
403                 }
404
405         file_util_move_multiple(fdm);
406 }
407
408 static void file_util_move_multiple_all_cb(GenericDialog *gd, gpointer data)
409 {
410         FileDataMult *fdm = data;
411
412         fdm->confirm_all = TRUE;
413
414         if (fdm->rename_auto) fdm->rename_all = TRUE;
415
416         file_util_move_multiple(fdm);
417 }
418
419 static void file_util_move_multiple_skip_cb(GenericDialog *gd, gpointer data)
420 {
421         FileDataMult *fdm = data;
422
423         fdm->skip = TRUE;
424         fdm->confirmed = TRUE;
425
426         file_util_move_multiple(fdm);
427 }
428
429 static void file_util_move_multiple_skip_all_cb(GenericDialog *gd, gpointer data)
430 {
431         FileDataMult *fdm = data;
432
433         fdm->skip = TRUE;
434         fdm->confirm_all = TRUE;
435         file_util_move_multiple(fdm);
436 }
437
438 static void file_util_move_multiple_continue_cb(GenericDialog *gd, gpointer data)
439 {
440         FileDataMult *fdm = data;
441
442         fdm->confirmed = TRUE;
443         file_util_move_multiple(fdm);
444 }
445
446 static void file_util_move_multiple_cancel_cb(GenericDialog *gd, gpointer data)
447 {
448         FileDataMult *fdm = data;
449
450         file_data_multiple_free(fdm);
451 }
452
453 /* rename option */
454
455 static void file_util_move_multiple_rename_auto_cb(GtkWidget *widget, gpointer data)
456 {
457         GenericDialog *gd = data;
458         FileDataMult *fdm;
459
460         fdm = gd->data;
461
462         fdm->rename_auto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
463         gtk_widget_set_sensitive(fdm->rename_box, !fdm->rename_auto);
464         gtk_widget_set_sensitive(fdm->rename_entry, (!fdm->rename_auto && fdm->rename));
465
466         if (fdm->rename_auto)
467                 {
468                 gchar *preview;
469
470                 preview = unique_filename_simple(fdm->dest);
471                 if (preview) gtk_entry_set_text(GTK_ENTRY(fdm->rename_entry), filename_from_path(preview));
472                 g_free(preview);
473                 }
474
475         gtk_widget_set_sensitive(fdm->yes_all_button, (fdm->rename_auto || !fdm->rename));
476 }
477
478 static void file_util_move_multiple_rename_cb(GtkWidget *widget, gpointer data)
479 {
480         GenericDialog *gd = data;
481         FileDataMult *fdm;
482
483         fdm = gd->data;
484
485         fdm->rename = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
486         gtk_widget_set_sensitive(fdm->rename_entry, fdm->rename);
487         gtk_widget_set_sensitive(fdm->yes_all_button, !fdm->rename);
488
489         if (fdm->rename)
490                 {
491                 const gchar *name;
492
493                 gtk_widget_grab_focus(fdm->rename_entry);
494
495                 name = gtk_entry_get_text(GTK_ENTRY(fdm->rename_entry));
496                 gtk_editable_select_region(GTK_EDITABLE(fdm->rename_entry), 0, filename_base_length(name));
497                 }
498 }
499
500 static GenericDialog *file_util_move_multiple_confirm_dialog(FileDataMult *fdm)
501 {
502         GenericDialog *gd;
503         GtkWidget *hbox;
504
505         gd = file_util_gen_dlg(_("Overwrite file"), "GQview", "dlg_confirm",
506                                 NULL, TRUE,
507                                 file_util_move_multiple_cancel_cb, fdm);
508
509         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
510                                    _("Overwrite file?"),
511                                    _("Replace existing file with new file."));
512         pref_spacer(gd->vbox, 0);
513
514         generic_dialog_add_button(gd, GTK_STOCK_YES, _("_Overwrite"), file_util_move_multiple_ok_cb, TRUE);
515         fdm->yes_all_button = generic_dialog_add_button(gd, NULL, _("Overwrite _all"),
516                                                         file_util_move_multiple_all_cb, FALSE);
517         generic_dialog_add_button(gd, GTK_STOCK_GOTO_LAST, _("S_kip all"), file_util_move_multiple_skip_all_cb, FALSE);
518         generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("_Skip"), file_util_move_multiple_skip_cb, FALSE);
519         generic_dialog_add_image(gd, NULL, fdm->dest, _("Existing file"), fdm->source, _("New file"), TRUE);
520
521         /* rename option */
522
523         fdm->rename = FALSE;
524         fdm->rename_all = FALSE;
525         fdm->rename_auto = FALSE;
526
527         hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
528
529         fdm->rename_auto_box = gtk_check_button_new_with_label(_("Auto rename"));
530         g_signal_connect(G_OBJECT(fdm->rename_auto_box), "clicked",
531                          G_CALLBACK(file_util_move_multiple_rename_auto_cb), gd);
532         gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_auto_box, FALSE, FALSE, 0);
533         gtk_widget_show(fdm->rename_auto_box);
534
535         hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
536
537         fdm->rename_box = gtk_check_button_new_with_label(_("Rename"));
538         g_signal_connect(G_OBJECT(fdm->rename_box), "clicked",
539                          G_CALLBACK(file_util_move_multiple_rename_cb), gd);
540         gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_box, FALSE, FALSE, 0);
541         gtk_widget_show(fdm->rename_box);
542
543         fdm->rename_entry = gtk_entry_new();
544         gtk_entry_set_text(GTK_ENTRY(fdm->rename_entry), filename_from_path(fdm->dest));
545         gtk_widget_set_sensitive(fdm->rename_entry, FALSE);
546         gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_entry, TRUE, TRUE, 0);
547         gtk_widget_show(fdm->rename_entry);
548
549         return gd;
550 }
551
552 static void file_util_move_multiple(FileDataMult *fdm)
553 {
554         while (fdm->dest || fdm->source_next)
555                 {
556                 gint success = FALSE;
557                 gint skip_file = FALSE;
558
559                 if (!fdm->dest)
560                         {
561                         GList *work = fdm->source_next;
562                         fdm->source = work->data;
563                         fdm->dest = concat_dir_and_file(fdm->dest_base, filename_from_path(fdm->source));
564                         fdm->source_next = work->next;
565                         fdm->confirmed = FALSE;
566                         }
567
568                 if (fdm->dest && fdm->source && strcmp(fdm->dest, fdm->source) == 0)
569                         {
570                         if (!fdm->confirmed)
571                                 {
572                                 GenericDialog *gd;
573                                 const gchar *title;
574                                 gchar *text;
575
576                                 if (fdm->copy)
577                                         {
578                                         title = _("Source to copy matches destination");
579                                         text = g_strdup_printf(_("Unable to copy file:\n%s\nto itself."), fdm->dest);
580                                         }
581                                 else
582                                         {
583                                         title = _("Source to move matches destination");
584                                         text = g_strdup_printf(_("Unable to move file:\n%s\nto itself."), fdm->dest);
585                                         }
586
587                                 gd = file_util_gen_dlg(title, "GQview", "dlg_confirm",
588                                                         NULL, TRUE,
589                                                         file_util_move_multiple_cancel_cb, fdm);
590                                 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING, title, text);
591                                 g_free(text);
592                                 generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
593                                                          file_util_move_multiple_continue_cb, TRUE);
594
595                                 gtk_widget_show(gd->dialog);
596                                 return;
597                                 }
598                         skip_file = TRUE;
599                         }
600                 else if (isfile(fdm->dest))
601                         {
602                         if (!fdm->confirmed && !fdm->confirm_all)
603                                 {
604                                 GenericDialog *gd;
605
606                                 gd = file_util_move_multiple_confirm_dialog(fdm);
607                                 gtk_widget_show(gd->dialog);
608                                 return;
609                                 }
610                         if (fdm->skip) skip_file = TRUE;
611                         }
612
613                 if (skip_file)
614                         {
615                         success = TRUE;
616                         if (!fdm->confirm_all) fdm->skip = FALSE;
617                         }
618                 else
619                         {
620                         gint try = TRUE;
621
622                         if (fdm->confirm_all && fdm->rename_all && isfile(fdm->dest))
623                                 {
624                                 gchar *buf;
625                                 buf = unique_filename_simple(fdm->dest);
626                                 if (buf)
627                                         {
628                                         g_free(fdm->dest);
629                                         fdm->dest = buf;
630                                         }
631                                 else
632                                         {
633                                         try = FALSE;
634                                         }
635                                 }
636                         if (try)
637                                 {
638                                 if (fdm->copy)
639                                         {
640                                         if (copy_file(fdm->source, fdm->dest))
641                                                 {
642                                                 success = TRUE;
643                                                 file_maint_copied(fdm->source, fdm->dest);
644                                                 }
645                                         }
646                                 else
647                                         {
648                                         if (move_file(fdm->source, fdm->dest))
649                                                 {
650                                                 success = TRUE;
651                                                 file_maint_moved(fdm->source, fdm->dest, fdm->source_list);
652                                                 }
653                                         }
654                                 }
655                         }
656
657                 if (!success)
658                         {
659                         GenericDialog *gd;
660                         const gchar *title;
661                         gchar *text;
662
663                         if (fdm->copy)
664                                 {
665                                 title = _("Error copying file");
666                                 text = g_strdup_printf(_("Unable to copy file:\n%s\nto:\n%s\nduring multiple file copy."), fdm->source, fdm->dest);
667                                 }
668                         else
669                                 {
670                                 title = _("Error moving file");
671                                 text = g_strdup_printf(_("Unable to move file:\n%s\nto:\n%s\nduring multiple file move."), fdm->source, fdm->dest);
672                                 }
673                         gd = file_util_gen_dlg(title, "GQview", "dlg_confirm",
674                                                 NULL, TRUE,
675                                                 file_util_move_multiple_cancel_cb, fdm);
676                         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING, title, text);
677                         g_free(text);
678
679                         generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
680                                                   file_util_move_multiple_continue_cb, TRUE);
681                         gtk_widget_show(gd->dialog);
682                         }
683
684                 g_free(fdm->dest);
685                 fdm->dest = NULL;
686
687                 if (!success) return;
688                 }
689
690         file_data_multiple_free(fdm);
691 }
692
693 /*
694  * Single file move
695  */
696
697 static FileDataSingle *file_data_single_new(const gchar *source, const gchar *dest, gint copy)
698 {
699         FileDataSingle *fds = g_new0(FileDataSingle, 1);
700         fds->confirmed = FALSE;
701         fds->source = g_strdup(source);
702         fds->dest = g_strdup(dest);
703         fds->copy = copy;
704         return fds;
705 }
706
707 static void file_data_single_free(FileDataSingle *fds)
708 {
709         g_free(fds->source);
710         g_free(fds->dest);
711         g_free(fds);
712 }
713
714 static void file_util_move_single(FileDataSingle *fds);
715
716 static void file_util_move_single_ok_cb(GenericDialog *gd, gpointer data)
717 {
718         FileDataSingle *fds = data;
719
720         fds->confirmed = TRUE;
721
722         if (fds->rename_auto)
723                 {
724                 gchar *buf;
725
726                 buf = unique_filename_simple(fds->dest);
727                 if (buf)
728                         {
729                         g_free(fds->dest);
730                         fds->dest = buf;
731                         }
732                 else
733                         {
734                         /* unique failed? well, return to the overwrite prompt :( */
735                         fds->confirmed = FALSE;
736                         }
737                 }
738         else if (fds->rename)
739                 {
740                 const gchar *name;
741
742                 name = gtk_entry_get_text(GTK_ENTRY(fds->rename_entry));
743                 if (strlen(name) == 0 ||
744                     strcmp(name, filename_from_path(fds->source)) == 0)
745                         {
746                         fds->confirmed = FALSE;
747                         }
748                 else
749                         {
750                         gchar *base;
751
752                         base = remove_level_from_path(fds->dest);
753                         g_free(fds->dest);
754                         fds->dest = concat_dir_and_file(base, name);
755                         fds->confirmed = !isname(fds->dest);
756
757                         g_free(base);
758                         }
759                 }
760
761         file_util_move_single(fds);
762 }
763
764 static void file_util_move_single_cancel_cb(GenericDialog *gd, gpointer data)
765 {
766         FileDataSingle *fds = data;
767
768         file_data_single_free(fds);
769 }
770
771 static void file_util_move_single_rename_auto_cb(GtkWidget *widget, gpointer data)
772 {
773         GenericDialog *gd = data;
774         FileDataSingle *fds;
775
776         fds = gd->data;
777
778         fds->rename_auto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
779         gtk_widget_set_sensitive(fds->rename_box, !fds->rename_auto);
780         gtk_widget_set_sensitive(fds->rename_entry, (!fds->rename_auto && fds->rename));
781
782         if (fds->rename_auto)
783                 {
784                 gchar *preview;
785
786                 preview = unique_filename_simple(fds->dest);
787                 if (preview) gtk_entry_set_text(GTK_ENTRY(fds->rename_entry), filename_from_path(preview));
788                 g_free(preview);
789                 }
790 }
791
792 static void file_util_move_single_rename_cb(GtkWidget *widget, gpointer data)
793 {
794         GenericDialog *gd = data;
795         FileDataSingle *fds;
796
797         fds = gd->data;
798
799         fds->rename = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
800         gtk_widget_set_sensitive(fds->rename_entry, fds->rename);
801
802         if (fds->rename)
803                 {
804                 const gchar *name;
805
806                 gtk_widget_grab_focus(fds->rename_entry);
807
808                 name = gtk_entry_get_text(GTK_ENTRY(fds->rename_entry));
809                 gtk_editable_select_region(GTK_EDITABLE(fds->rename_entry), 0, filename_base_length(name));
810                 }
811 }
812
813 static void file_util_move_single(FileDataSingle *fds)
814 {
815         if (fds->dest && fds->source && strcmp(fds->dest, fds->source) == 0)
816                 {
817                 file_util_warning_dialog(_("Source matches destination"),
818                                          _("Source and destination are the same, operation cancelled."),
819                                          GTK_STOCK_DIALOG_INFO, NULL);
820                 }
821         else if (isfile(fds->dest) && !fds->confirmed)
822                 {
823                 GenericDialog *gd;
824                 GtkWidget *hbox;
825
826                 gd = file_util_gen_dlg(_("Overwrite file"), "GQview", "dlg_confirm",
827                                         NULL, TRUE,
828                                         file_util_move_single_cancel_cb, fds);
829
830                 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
831                                            _("Overwrite file?"),
832                                            _("Replace existing file with new file."));
833                 pref_spacer(gd->vbox, 0);
834
835                 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_move_single_ok_cb, TRUE);
836                 generic_dialog_add_image(gd, NULL, fds->dest, _("Existing file"), fds->source, _("New file"), TRUE);
837
838                 /* rename option */
839
840                 fds->rename = FALSE;
841                 fds->rename_auto = FALSE;
842
843                 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
844
845                 fds->rename_auto_box = gtk_check_button_new_with_label(_("Auto rename"));
846                 g_signal_connect(G_OBJECT(fds->rename_auto_box), "clicked",
847                                  G_CALLBACK(file_util_move_single_rename_auto_cb), gd);
848                 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_auto_box, FALSE, FALSE, 0);
849                 gtk_widget_show(fds->rename_auto_box);
850
851                 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
852
853                 fds->rename_box = gtk_check_button_new_with_label(_("Rename"));
854                 g_signal_connect(G_OBJECT(fds->rename_box), "clicked",
855                                  G_CALLBACK(file_util_move_single_rename_cb), gd);
856                 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_box, FALSE, FALSE, 0);
857                 gtk_widget_show(fds->rename_box);
858
859                 fds->rename_entry = gtk_entry_new();
860                 gtk_entry_set_text(GTK_ENTRY(fds->rename_entry), filename_from_path(fds->dest));
861                 gtk_widget_set_sensitive(fds->rename_entry, FALSE);
862                 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_entry, TRUE, TRUE, 0);
863                 gtk_widget_show(fds->rename_entry);
864
865                 gtk_widget_show(gd->dialog);
866                 return;
867                 }
868         else
869                 {
870                 gint success = FALSE;
871                 if (fds->copy)
872                         {
873                         if (copy_file(fds->source, fds->dest))
874                                 {
875                                 success = TRUE;
876                                 file_maint_copied(fds->source, fds->dest);
877                                 }
878                         }
879                 else
880                         {
881                         if (move_file(fds->source, fds->dest))
882                                 {
883                                 success = TRUE;
884                                 file_maint_moved(fds->source, fds->dest, NULL);
885                                 }
886                         }
887                 if (!success)
888                         {
889                         gchar *title;
890                         gchar *text;
891                         if (fds->copy)
892                                 {
893                                 title = _("Error copying file");
894                                 text = g_strdup_printf(_("Unable to copy file:\n%s\nto:\n%s"), fds->source, fds->dest);
895                                 }
896                         else
897                                 {
898                                 title = _("Error moving file");
899                                 text = g_strdup_printf(_("Unable to move file:\n%s\nto:\n%s"), fds->source, fds->dest);
900                                 }
901                         file_util_warning_dialog(title, text, GTK_STOCK_DIALOG_ERROR, NULL);
902                         g_free(text);
903                         }
904                 }
905
906         file_data_single_free(fds);
907 }
908
909 /*
910  * file move dialog
911  */
912
913 static void file_util_move_do(FileDialog *fd)
914 {
915         file_dialog_sync_history(fd, TRUE);
916
917         if (fd->multiple_files)
918                 {
919                 file_util_move_multiple(file_data_multiple_new(fd->source_list, fd->dest_path, fd->type));
920                 fd->source_list = NULL;
921                 }
922         else
923                 {
924                 if (isdir(fd->dest_path))
925                         {
926                         gchar *buf = concat_dir_and_file(fd->dest_path, filename_from_path(fd->source_path));
927                         gtk_entry_set_text(GTK_ENTRY(fd->entry), buf);
928                         g_free(buf);
929                         }
930                 file_util_move_single(file_data_single_new(fd->source_path, fd->dest_path, fd->type));
931                 }
932
933         file_dialog_close(fd);
934 }
935
936 static void file_util_move_check(FileDialog *fd)
937 {
938         if (fd->dest_path && strcmp(fd->dest_path, "~") == 0)
939                 {
940                 gtk_entry_set_text(GTK_ENTRY(fd->entry), homedir());
941                 }
942
943         if (fd->multiple_files && !isdir(fd->dest_path))
944                 {
945                 if (isfile(fd->dest_path))
946                         {
947                         file_util_warning_dialog(_("Invalid destination"),
948                                                  _("When operating with multiple files, please select\na folder, not a file."),
949                                                  GTK_STOCK_DIALOG_INFO, NULL);
950                         }
951                 else
952                         file_util_warning_dialog(_("Invalid folder"),
953                                                  _("Please select an existing folder."),
954                                                  GTK_STOCK_DIALOG_INFO, NULL);
955                 return;
956                 }
957
958         if (!fd->dest_path || fd->dest_path[0] != '/')
959                 {
960                 if (fd->source_path)
961                         {
962                         gchar *base;
963                         gchar *path;
964
965                         base = remove_level_from_path(fd->source_path);
966                         path = concat_dir_and_file(base, fd->dest_path);
967
968                         gtk_entry_set_text(GTK_ENTRY(fd->entry), path);
969
970                         g_free(path);
971                         g_free(base);
972                         }
973                 return;
974                 }
975
976         file_util_move_do(fd);
977 }
978
979 static void file_util_move_cb(FileDialog *fd, gpointer data)
980 {
981         file_util_move_check(fd);
982 }
983
984 static void file_util_move_cancel_cb(FileDialog *fd, gpointer data)
985 {
986         file_dialog_close(fd);
987 }
988
989 static void real_file_util_move(const gchar *source_path, GList *source_list,
990                                 const gchar *dest_path, gint copy, GtkWidget *parent)
991 {
992         FileDialog *fd;
993         GtkWidget *label;
994         gchar *path = NULL;
995         gint multiple;
996         const gchar *text;
997         const gchar *title;
998         const gchar *op_text;
999         const gchar *stock_id;
1000
1001         if (!source_path && !source_list) return;
1002
1003         if (source_path)
1004                 {
1005                 path = g_strdup(source_path);
1006                 multiple = FALSE;
1007                 }
1008         else if (source_list->next)
1009                 {
1010                 multiple = TRUE;
1011                 }
1012         else
1013                 {
1014                 path = g_strdup(source_list->data);
1015                 path_list_free(source_list);
1016                 source_list = NULL;
1017                 multiple = FALSE;
1018                 }
1019
1020         if (copy)
1021                 {
1022                 title = _("Copy - GQview");
1023                 op_text = _("_Copy");
1024                 if (path)
1025                         {
1026                         text = _("Copy file");
1027                         }
1028                 else
1029                         {
1030                         text = _("Copy multiple files");
1031                         }
1032                 stock_id = GTK_STOCK_COPY;
1033                 }
1034         else
1035                 {
1036                 title = _("Move - GQview");
1037                 op_text = _("_Move");
1038                 if (path)
1039                         {
1040                         text = _("Move file");
1041                         }
1042                 else
1043                         {
1044                         text = _("Move multiple files");
1045                         }
1046                 stock_id = GTK_STOCK_OK;
1047                 }
1048
1049         fd = file_util_file_dlg(title, "GQview", "dlg_copymove", parent,
1050                                 file_util_move_cancel_cb, NULL);
1051         generic_dialog_add_message(GENERIC_DIALOG(fd), NULL, text, NULL);
1052
1053         if (path)
1054                 {
1055                 GtkWidget *box;
1056
1057                 box = pref_box_new(GENERIC_DIALOG(fd)->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1058                 pref_label_new(box, _("File name:"));
1059                 pref_label_new(box, filename_from_path(path));
1060                 }
1061
1062         label = pref_label_new(GENERIC_DIALOG(fd)->vbox, _("Choose the destination folder."));
1063         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1064         pref_spacer(GENERIC_DIALOG(fd)->vbox, 0);
1065
1066         file_dialog_add_button(fd, stock_id, op_text, file_util_move_cb, TRUE);
1067
1068         file_dialog_add_path_widgets(fd, NULL, dest_path, "move_copy", NULL, NULL);
1069
1070         fd->type = copy;
1071         fd->source_path = path;
1072         fd->source_list = source_list;
1073         fd->multiple_files = multiple;
1074
1075         gtk_widget_show(GENERIC_DIALOG(fd)->dialog);
1076 }
1077
1078 void file_util_move(const gchar *source_path, GList *source_list, const gchar *dest_path, GtkWidget *parent)
1079 {
1080         real_file_util_move(source_path, source_list, dest_path, FALSE, parent);
1081 }
1082
1083 void file_util_copy(const gchar *source_path, GList *source_list, const gchar *dest_path, GtkWidget *parent)
1084 {
1085         real_file_util_move(source_path, source_list, dest_path, TRUE, parent);
1086 }
1087
1088 void file_util_move_simple(GList *list, const gchar *dest_path)
1089 {
1090         if (!list) return;
1091         if (!dest_path)
1092                 {
1093                 path_list_free(list);
1094                 return;
1095                 }
1096
1097         if (!list->next)
1098                 {
1099                 const gchar *source;
1100                 gchar *dest;
1101
1102                 source = list->data;
1103                 dest = concat_dir_and_file(dest_path, filename_from_path(source));
1104
1105                 file_util_move_single(file_data_single_new(source, dest, FALSE));
1106                 g_free(dest);
1107                 path_list_free(list);
1108                 return;
1109                 }
1110
1111         file_util_move_multiple(file_data_multiple_new(list, dest_path, FALSE));
1112 }
1113
1114 void file_util_copy_simple(GList *list, const gchar *dest_path)
1115 {
1116         if (!list) return;
1117         if (!dest_path)
1118                 {
1119                 path_list_free(list);
1120                 return;
1121                 }
1122
1123         if (!list->next)
1124                 {
1125                 const gchar *source;
1126                 gchar *dest;
1127
1128                 source = list->data;
1129                 dest = concat_dir_and_file(dest_path, filename_from_path(source));
1130
1131                 file_util_move_single(file_data_single_new(source, dest, TRUE));
1132                 g_free(dest);
1133                 path_list_free(list);
1134                 return;
1135                 }
1136
1137         file_util_move_multiple(file_data_multiple_new(list, dest_path, TRUE));
1138 }
1139
1140 /*
1141  *--------------------------------------------------------------------------
1142  * Safe Delete
1143  *--------------------------------------------------------------------------
1144  */
1145
1146 static gint file_util_safe_number(gint64 free_space)
1147 {
1148         gint n = 0;
1149         gint64 total = 0;
1150         GList *list;
1151         GList *work;
1152         gint sorted = FALSE;
1153         gint warned = FALSE;
1154
1155         if (!filelist_read(safe_delete_path, &list, NULL)) return 0;
1156
1157         work = list;
1158         while (work)
1159                 {
1160                 FileData *fd;
1161                 gint v;
1162                
1163                 fd = work->data;
1164                 work = work->next;
1165
1166                 v = (gint)strtol(fd->name, NULL, 10);
1167                 if (v >= n) n = v + 1;
1168
1169                 total += fd->size;
1170                 }
1171
1172         while (list &&
1173                (free_space < 0 || total + free_space > (gint64)safe_delete_size * 1048576) )
1174                 {
1175                 FileData *fd;
1176
1177                 if (!sorted)
1178                         {
1179                         list = filelist_sort(list, SORT_NAME, TRUE);
1180                         sorted = TRUE;
1181                         }
1182
1183                 fd = list->data;
1184                 list = g_list_remove(list, fd);
1185
1186                 if (debug) printf("expunging from trash for space: %s\n", fd->name);
1187                 if (!unlink_file(fd->path) && !warned)
1188                         {
1189                         file_util_warning_dialog(_("Delete failed"),
1190                                                  _("Unable to remove old file from trash folder"),
1191                                                  GTK_STOCK_DIALOG_WARNING, NULL);
1192                         warned = TRUE;
1193                         }
1194                 total -= fd->size;
1195                 file_data_free(fd);
1196                 }
1197
1198         filelist_free(list);
1199
1200         return n;
1201 }
1202
1203 void file_util_trash_clear(void)
1204 {
1205         file_util_safe_number(-1);
1206 }
1207
1208 static gchar *file_util_safe_dest(const gchar *path)
1209 {
1210         gint n;
1211
1212         n = file_util_safe_number(filesize(path));
1213         return g_strdup_printf("%s/%06d_%s", safe_delete_path, n, filename_from_path(path));
1214 }
1215
1216 static void file_util_safe_del_toggle_cb(GtkWidget *button, gpointer data)
1217 {
1218         safe_delete_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1219 }
1220
1221 static void file_util_safe_del_close_cb(GtkWidget *dialog, gpointer data)
1222 {
1223         GenericDialog **gd = data;
1224
1225         *gd = NULL;
1226 }
1227
1228 static gint file_util_unlink(const gchar *path)
1229 {
1230         static GenericDialog *gd = NULL;
1231         gchar *result = NULL;
1232         gint success = TRUE;
1233
1234         if (!isfile(path)) return FALSE;
1235
1236         if (!safe_delete_enable)
1237                 {
1238                 return unlink_file(path);
1239                 }
1240
1241         if (!isdir(safe_delete_path))
1242                 {
1243                 if (debug) printf("creating trash: %s\n", safe_delete_path);
1244                 if (!safe_delete_path || !mkdir_utf8(safe_delete_path, 0755))
1245                         {
1246                         result = _("Could not create folder");
1247                         success = FALSE;
1248                         }
1249                 }
1250
1251         if (success)
1252                 {
1253                 gchar *dest;
1254
1255                 dest = file_util_safe_dest(path);
1256                 if (dest)
1257                         {
1258                         if (debug) printf("safe deleting %s to %s\n", path, dest);
1259                         success = move_file(path, dest);
1260                         }
1261                 else
1262                         {
1263                         success = FALSE;
1264                         }
1265
1266                 if (!success && !access_file(path, W_OK))
1267                         {
1268                         result = _("Permission denied");
1269                         }
1270                 g_free(dest);
1271                 }
1272
1273         if (result && !gd)
1274                 {
1275                 GtkWidget *button;
1276                 gchar *buf;
1277
1278                 buf = g_strdup_printf(_("Unable to access or create the trash folder.\n\"%s\""), safe_delete_path);
1279                 gd = file_util_warning_dialog(result, buf, GTK_STOCK_DIALOG_WARNING, NULL);
1280                 g_free(buf);
1281
1282                 button = gtk_check_button_new_with_label(_("Turn off safe delete"));
1283                 g_signal_connect(G_OBJECT(button), "toggled",
1284                                  G_CALLBACK(file_util_safe_del_toggle_cb), NULL);
1285                 gtk_box_pack_start(GTK_BOX(gd->vbox), button, FALSE, FALSE, 0);
1286                 gtk_widget_show(button);
1287
1288                 g_signal_connect(G_OBJECT(gd->dialog), "destroy",
1289                                  G_CALLBACK(file_util_safe_del_close_cb), &gd);
1290                 }
1291
1292         return success;
1293 }
1294
1295 static void box_append_safe_delete_status(GenericDialog *gd)
1296 {
1297         GtkWidget *label;
1298         gchar *buf;
1299
1300         buf = g_strdup_printf(_("Safe delete: %s"), (safe_delete_enable) ? _("on") : _("off"));
1301         label = pref_label_new(gd->vbox, buf);
1302         g_free(buf);
1303
1304         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1305         gtk_widget_set_sensitive(label, FALSE);
1306 }
1307
1308 /*
1309  *--------------------------------------------------------------------------
1310  * Delete routines
1311  *--------------------------------------------------------------------------
1312  */
1313
1314 /*
1315  * delete multiple files
1316  */
1317
1318 static void file_util_delete_multiple_ok_cb(GenericDialog *gd, gpointer data);
1319 static void file_util_delete_multiple_cancel_cb(GenericDialog *gd, gpointer data);
1320
1321 static void file_util_delete_multiple_ok_cb(GenericDialog *gd, gpointer data)
1322 {
1323         GList *source_list = data;
1324
1325         while (source_list)
1326                 {
1327                 gchar *path = source_list->data;
1328
1329                 source_list = g_list_remove(source_list, path);
1330
1331                 if (!file_util_unlink(path))
1332                         {
1333                         if (source_list)
1334                                 {
1335                                 GenericDialog *d;
1336                                 gchar *text;
1337
1338                                 d = file_util_gen_dlg(_("Delete failed"), "GQview", "dlg_confirm",
1339                                                       NULL, TRUE,
1340                                                       file_util_delete_multiple_cancel_cb, source_list);
1341
1342                                 text = g_strdup_printf(_("Unable to delete file:\n %s\n Continue multiple delete operation?"), path);
1343                                 generic_dialog_add_message(d, GTK_STOCK_DIALOG_WARNING, NULL, text);
1344                                 g_free(text);
1345
1346                                 generic_dialog_add_button(d, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
1347                                                           file_util_delete_multiple_ok_cb, TRUE);
1348                                 gtk_widget_show(d->dialog);
1349                                 }
1350                         else
1351                                 {
1352                                 gchar *text;
1353                                 
1354                                 text = g_strdup_printf(_("Unable to delete file:\n%s"), path);
1355                                 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1356                                 g_free(text);
1357                                 }
1358                         g_free(path);
1359                         return;
1360                         }
1361                 else
1362                         {
1363                         file_maint_removed(path, source_list);
1364                         }
1365                 g_free(path);
1366                 }
1367 }
1368
1369 static void file_util_delete_multiple_cancel_cb(GenericDialog *gd, gpointer data)
1370 {
1371         GList *source_list = data;
1372
1373         path_list_free(source_list);
1374 }
1375
1376 static void file_util_delete_multiple_review_skip(GenericDialog *gd, gint next)
1377 {
1378         GtkWidget *button_back;
1379         GtkWidget *button_next;
1380         GtkWidget *button_label;
1381         GList *list;
1382         GList *list_point;
1383         const gchar *path;
1384         gchar *buf;
1385
1386         list = gd->data;
1387         button_back = g_object_get_data(G_OBJECT(gd->dialog), "button_back");
1388         button_next = g_object_get_data(G_OBJECT(gd->dialog), "button_next");
1389         button_label = g_object_get_data(G_OBJECT(gd->dialog), "button_label");
1390         list_point = g_object_get_data(G_OBJECT(gd->dialog), "list_point");
1391
1392         if (!list || !button_label) return;
1393
1394         if (list_point)
1395                 {
1396                 if (next)
1397                         {
1398                         if (list_point->next) list_point = list_point->next;
1399                         }
1400                 else
1401                         {
1402                         if (list_point->prev) list_point = list_point->prev;
1403                         }
1404                 }
1405         else
1406                 {
1407                 list_point = list;
1408                 }
1409
1410         if (!list_point) return;
1411
1412         path = list_point->data;
1413         buf = g_strdup_printf(_("File %d of %d"),
1414                               g_list_index(list, (gpointer)path) + 1,
1415                               g_list_length(list));
1416         gtk_label_set_text(GTK_LABEL(button_label), buf);
1417         g_free(buf);
1418
1419         gtk_widget_set_sensitive(button_back, (list_point->prev != NULL) );
1420         gtk_widget_set_sensitive(button_next, (list_point->next != NULL) );
1421
1422         generic_dialog_image_set(gd, path);
1423
1424         g_object_set_data(G_OBJECT(gd->dialog), "list_point", list_point);
1425 }
1426
1427 static void file_util_delete_multiple_review_back(GtkWidget *button, gpointer data)
1428 {
1429         GenericDialog *gd = data;
1430
1431         file_util_delete_multiple_review_skip(gd, FALSE);
1432 }
1433
1434 static void file_util_delete_multiple_review_next(GtkWidget *button, gpointer data)
1435 {
1436         GenericDialog *gd = data;
1437
1438         file_util_delete_multiple_review_skip(gd, TRUE);
1439 }
1440
1441 static void file_util_delete_multiple_review_button_cb(ImageWindow *imd, gint button, guint32 time,
1442                                                        gdouble x, gdouble y, guint state, gpointer data)
1443 {
1444         if (button == 1)
1445                 {
1446                 file_util_delete_multiple_review_next(NULL, data);
1447                 }
1448         else if (button == 2 || button == 3)
1449                 {
1450                 file_util_delete_multiple_review_back(NULL, data);
1451                 }
1452 }
1453
1454 static void file_util_delete_multiple_review_scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
1455                                                        gdouble x, gdouble y, guint state, gpointer data)
1456 {
1457         if (direction == GDK_SCROLL_UP)
1458                 {
1459                 file_util_delete_multiple_review_back(NULL, data);
1460                 }
1461         else if (direction == GDK_SCROLL_DOWN)
1462                 {
1463                 file_util_delete_multiple_review_next(NULL, data);
1464                 }
1465 }
1466
1467 static void file_util_delete_multiple(GList *source_list, GtkWidget *parent)
1468 {
1469         if (!confirm_delete)
1470                 {
1471                 file_util_delete_multiple_ok_cb(NULL, source_list);
1472                 }
1473         else
1474                 {
1475                 GenericDialog *gd;
1476                 GtkWidget *hbox;
1477                 GtkWidget *button;
1478                 GtkWidget *label;
1479                 ImageWindow *imd;
1480                 gchar *buf;
1481
1482                 gd = file_util_gen_dlg(_("Delete files - GQview"),
1483                                         "GQview", "dlg_confirm", parent, TRUE,
1484                                         file_util_delete_multiple_cancel_cb, source_list);
1485
1486                 generic_dialog_add_message(gd, NULL, _("Delete multiple files"), NULL);
1487
1488                 generic_dialog_add_image(gd, NULL, NULL, NULL, NULL, NULL, TRUE);
1489                 imd = g_object_get_data(G_OBJECT(gd->dialog), "img_image");
1490                 image_set_button_func(imd, file_util_delete_multiple_review_button_cb, gd);
1491                 image_set_scroll_func(imd, file_util_delete_multiple_review_scroll_cb, gd);
1492
1493                 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
1494
1495                 button = pref_button_new(hbox, GTK_STOCK_GO_BACK, NULL, TRUE,
1496                                          G_CALLBACK(file_util_delete_multiple_review_back), gd);
1497                 gtk_widget_set_sensitive(button, FALSE);
1498                 g_object_set_data(G_OBJECT(gd->dialog), "button_back", button);
1499
1500                 button = pref_button_new(hbox, GTK_STOCK_GO_FORWARD, NULL, TRUE,
1501                                          G_CALLBACK(file_util_delete_multiple_review_next), gd);
1502                 g_object_set_data(G_OBJECT(gd->dialog), "button_next", button);
1503
1504                 buf = g_strdup_printf(_("Review %d files"), g_list_length(source_list) );
1505                 label = pref_label_new(hbox, buf);
1506                 g_free(buf);
1507                 g_object_set_data(G_OBJECT(gd->dialog), "button_label", label);
1508
1509                 box_append_safe_delete_status(gd);
1510
1511                 generic_dialog_add_button(gd, GTK_STOCK_DELETE, NULL, file_util_delete_multiple_ok_cb, TRUE);
1512
1513                 gtk_widget_show(gd->dialog);
1514                 }
1515 }
1516
1517 /*
1518  * delete single file
1519  */
1520
1521 static void file_util_delete_ok_cb(GenericDialog *gd, gpointer data)
1522 {
1523         gchar *path = data;
1524
1525         if (!file_util_unlink(path))
1526                 {
1527                 gchar *text = g_strdup_printf(_("Unable to delete file:\n%s"), path);
1528                 file_util_warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1529                 g_free(text);
1530                 }
1531         else
1532                 {
1533                 file_maint_removed(path, NULL);
1534                 }
1535
1536         g_free(path);
1537 }
1538
1539 static void file_util_delete_cancel_cb(GenericDialog *gd, gpointer data)
1540 {
1541         gchar *path = data;
1542
1543         g_free(path);
1544 }
1545
1546 static void file_util_delete_single(const gchar *path, GtkWidget *parent)
1547 {
1548         gchar *buf = g_strdup(path);
1549
1550         if (!confirm_delete)
1551                 {
1552                 file_util_delete_ok_cb(NULL, buf);
1553                 }
1554         else
1555                 {
1556                 GenericDialog *gd;
1557                 GtkWidget *table;
1558                 gchar *base;
1559
1560                 gd = file_util_gen_dlg(_("Delete file - GQview"), "GQview", "dlg_confirm",
1561                                         parent, TRUE,
1562                                         file_util_delete_cancel_cb, buf);
1563
1564                 generic_dialog_add_message(gd, NULL, _("Delete file?"), NULL);
1565
1566                 table = pref_table_new(gd->vbox, 2, 2, FALSE, FALSE);
1567
1568                 pref_table_label(table, 0, 0, _("File name:"), 1.0);
1569                 pref_table_label(table, 1, 0, filename_from_path(path), 0.0);
1570
1571                 pref_table_label(table, 0, 1, _("Location:"), 1.0);
1572
1573                 base = remove_level_from_path(path);
1574                 pref_table_label(table, 1, 1, base, 0.0);
1575                 g_free(base);
1576
1577                 generic_dialog_add_image(gd, NULL, path, NULL, NULL, NULL, FALSE);
1578
1579                 box_append_safe_delete_status(gd);
1580
1581                 generic_dialog_add_button(gd, GTK_STOCK_DELETE, NULL, file_util_delete_ok_cb, TRUE);
1582
1583                 gtk_widget_show(gd->dialog);
1584                 }
1585 }
1586
1587 void file_util_delete(const gchar *source_path, GList *source_list, GtkWidget *parent)
1588 {
1589         if (!source_path && !source_list) return;
1590
1591         if (source_path)
1592                 {
1593                 file_util_delete_single(source_path, parent);
1594                 }
1595         else if (!source_list->next)
1596                 {
1597                 file_util_delete_single(source_list->data, parent);
1598                 path_list_free(source_list);
1599                 }
1600         else
1601                 {
1602                 file_util_delete_multiple(source_list, parent);
1603                 }
1604 }
1605
1606 /*
1607  *--------------------------------------------------------------------------
1608  * Rename routines
1609  *--------------------------------------------------------------------------
1610  */
1611
1612 /*
1613  * rename multiple files
1614  */
1615
1616 enum {
1617         RENAME_COLUMN_PATH = 0,
1618         RENAME_COLUMN_NAME,
1619         RENAME_COLUMN_PREVIEW,
1620         RENAME_COLUMN_COUNT
1621 };
1622
1623 typedef enum {
1624         RENAME_TYPE_MANUAL = 0,
1625         RENAME_TYPE_FORMATTED,
1626         RENAME_TYPE_AUTO
1627 } RenameType;
1628
1629 typedef struct _RenameDataMult RenameDataMult;
1630 struct _RenameDataMult
1631 {
1632         FileDialog *fd;
1633
1634         RenameType rename_type;
1635
1636         GtkWidget *listview;
1637         GtkWidget *combo_type;
1638
1639         GtkWidget *rename_box;
1640         GtkWidget *rename_label;
1641         GtkWidget *rename_entry;
1642
1643         GtkWidget *auto_box;
1644         GtkWidget *auto_entry_front;
1645         GtkWidget *auto_spin_start;
1646         GtkWidget *auto_spin_pad;
1647         GtkWidget *auto_entry_end;
1648
1649         GtkWidget *format_box;
1650         GtkWidget *format_entry;
1651         GtkWidget *format_spin;
1652
1653         ImageWindow *imd;
1654
1655         gint update_idle_id;
1656 };
1657
1658 static void file_util_rename_multiple(RenameDataMult *rd);
1659
1660 static void file_util_rename_multiple_ok_cb(GenericDialog *gd, gpointer data)
1661 {
1662         RenameDataMult *rd = data;
1663         GtkWidget *dialog;
1664
1665         dialog = GENERIC_DIALOG(rd->fd)->dialog;
1666         if (!GTK_WIDGET_VISIBLE(dialog)) gtk_widget_show(dialog);
1667
1668         rd->fd->type = TRUE;
1669         file_util_rename_multiple(rd);
1670 }
1671
1672 static void file_util_rename_multiple_cancel_cb(GenericDialog *gd, gpointer data)
1673 {
1674         RenameDataMult *rd = data;
1675         GtkWidget *dialog;
1676
1677         dialog = GENERIC_DIALOG(rd->fd)->dialog;
1678         if (!GTK_WIDGET_VISIBLE(dialog)) gtk_widget_show(dialog);
1679 }
1680
1681 static gint file_util_rename_multiple_find_row(RenameDataMult *rd, const gchar *path, GtkTreeIter *iter)
1682 {
1683         GtkTreeModel *store;
1684         gint valid;
1685         gint row = 0;
1686
1687         store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1688         valid = gtk_tree_model_get_iter_first(store, iter);
1689         while (valid)
1690                 {
1691                 gchar *path_n;
1692                 gint ret;
1693
1694                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, RENAME_COLUMN_PATH, &path_n, -1);
1695                 ret = (strcmp(path_n, path) == 0);
1696                 g_free(path_n);
1697                 if (ret) return row;
1698
1699                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
1700                 row++;
1701                 }
1702
1703         return -1;
1704 }
1705
1706 static void file_util_rename_multiple(RenameDataMult *rd)
1707 {
1708         FileDialog *fd;
1709
1710         fd = rd->fd;
1711
1712         if (isfile(fd->dest_path) && !fd->type)
1713                 {
1714                 GenericDialog *gd;
1715
1716                 gd = file_util_gen_dlg(_("Overwrite file"), "GQview", "dlg_confirm",
1717                                         NULL, TRUE,
1718                                         file_util_rename_multiple_cancel_cb, rd);
1719
1720                 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
1721                                            _("Overwrite file?"),
1722                                            _("Replace existing file by renaming new file."));
1723                 pref_spacer(gd->vbox, 0);
1724
1725                 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_rename_multiple_ok_cb, TRUE);
1726                 generic_dialog_add_image(gd, NULL,
1727                                          fd->dest_path, _("Existing file"),
1728                                          fd->source_path, _("New file"), TRUE);
1729
1730                 gtk_widget_hide(GENERIC_DIALOG(fd)->dialog);
1731
1732                 gtk_widget_show(gd->dialog);
1733                 return;
1734                 }
1735         else
1736                 {
1737                 if (!rename_file(fd->source_path, fd->dest_path))
1738                         {
1739                         gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\n to:\n%s"),
1740                                                       filename_from_path(fd->source_path),
1741                                                       filename_from_path(fd->dest_path));
1742                         file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1743                         g_free(text);
1744                         }
1745                 else
1746                         {
1747                         GtkTreeModel *store;
1748                         GtkTreeIter iter;
1749                         GtkTreeIter next;
1750                         gint row;
1751
1752                         file_maint_renamed(fd->source_path, fd->dest_path);
1753
1754                         store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1755                         row = file_util_rename_multiple_find_row(rd, rd->fd->source_path, &iter);
1756
1757                         if (row >= 0 &&
1758                             (gtk_tree_model_iter_nth_child(store, &next, NULL, row + 1) ||
1759                             (row > 0 && gtk_tree_model_iter_nth_child(store, &next, NULL, row - 1)) ) )
1760                                 {
1761                                 GtkTreeSelection *selection;
1762
1763                                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
1764                                 gtk_tree_selection_select_iter(selection, &next);
1765                                 gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1766                                 }
1767                         else
1768                                 {
1769                                 if (debug) printf("closed by #%d\n", row);
1770
1771                                 file_dialog_close(rd->fd);
1772                                 }
1773                         }
1774                 }
1775 }
1776
1777 /* format: * = filename without extension, ## = number position, extension is kept */
1778 static gchar *file_util_rename_multiple_auto_format_name(const gchar *format, const gchar *name, gint n)
1779 {
1780         gchar *new_name;
1781         gchar *parsed;
1782         const gchar *ext;
1783         gchar *middle;
1784         gchar *tmp;
1785         gchar *pad_start;
1786         gchar *pad_end;
1787         gint padding;
1788
1789         if (!format || !name) return NULL;
1790
1791         tmp = g_strdup(format);
1792         pad_start = strchr(tmp, '#');
1793         if (pad_start)
1794                 {
1795                 pad_end = pad_start;
1796                 padding = 0;
1797                 while (*pad_end == '#')
1798                         {
1799                         pad_end++;
1800                         padding++;
1801                         }
1802                 *pad_start = '\0';
1803
1804                 parsed = g_strdup_printf("%s%0*d%s", tmp, padding, n, pad_end);
1805                 g_free(tmp);
1806                 }
1807         else
1808                 {
1809                 parsed = tmp;
1810                 }
1811
1812         ext = extension_from_path(name);
1813
1814         middle = strchr(parsed, '*');
1815         if (middle)
1816                 {
1817                 gchar *base;
1818
1819                 *middle = '\0';
1820                 middle++;
1821
1822                 base = remove_extension_from_path(name);
1823                 new_name = g_strconcat(parsed, base, middle, ext, NULL);
1824                 g_free(base);
1825                 }
1826         else
1827                 {
1828                 new_name = g_strconcat(parsed, ext, NULL);
1829                 }
1830
1831         g_free(parsed);
1832
1833         return new_name;
1834 }
1835
1836 static void file_util_rename_multiple_auto(RenameDataMult *rd)
1837 {
1838         const gchar *front;
1839         const gchar *end;
1840         const gchar *format;
1841         gint start_n;
1842         gint padding;
1843         gint n;
1844         GtkTreeModel *store;
1845         GtkTreeIter iter;
1846         gint valid;
1847         gint success;
1848
1849         front = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_front));
1850         end = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_end));
1851         padding = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_pad));
1852
1853         format = gtk_entry_get_text(GTK_ENTRY(rd->format_entry));
1854
1855         if (rd->rename_type == RENAME_TYPE_FORMATTED)
1856                 {
1857                 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->format_spin));
1858
1859                 if (!format ||
1860                     (strchr(format, '*') == NULL && strchr(format, '#') == NULL))
1861                         {
1862                         file_util_warning_dialog(_("Auto rename"),
1863                                _("Format must include at least one of the symbol characters '*' or '#'.\n"),
1864                                GTK_STOCK_DIALOG_WARNING, NULL);
1865                         return;
1866                         }
1867
1868                 history_combo_append_history(rd->format_entry, NULL);
1869                 }
1870         else
1871                 {
1872                 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_start));
1873
1874                 history_combo_append_history(rd->auto_entry_front, NULL);
1875                 history_combo_append_history(rd->auto_entry_end, NULL);
1876                 }
1877
1878         store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1879
1880         /* first check for name conflicts */
1881         success = TRUE;
1882         n = start_n;
1883         valid = gtk_tree_model_get_iter_first(store, &iter);
1884         while (valid && success)
1885                 {
1886                 gchar *dest;
1887                 gchar *base;
1888                 gchar *path;
1889
1890                 gtk_tree_model_get(store, &iter, RENAME_COLUMN_PATH, &path, -1);
1891                 base = remove_level_from_path(path);
1892
1893                 if (rd->rename_type == RENAME_TYPE_FORMATTED)
1894                         {
1895                         gchar *new_name;
1896
1897                         new_name = file_util_rename_multiple_auto_format_name(format, filename_from_path(path), n);
1898                         dest = g_strconcat(base, "/", new_name, NULL);
1899                         g_free(new_name);
1900                         }
1901                 else
1902                         {
1903                         dest = g_strdup_printf("%s/%s%0*d%s", base, front, padding, n, end);
1904                         }
1905
1906                 if (isname(dest)) success = FALSE;
1907
1908                 g_free(dest);
1909                 g_free(base);
1910                 g_free(path);
1911
1912                 n++;
1913                 valid = gtk_tree_model_iter_next(store, &iter);
1914                 }
1915
1916         if (!success)
1917                 {
1918                 file_util_warning_dialog(_("Auto rename"),
1919                                _("Can not auto rename with the selected\nnumber set, one or more files exist that\nmatch the resulting name list.\n"),
1920                                GTK_STOCK_DIALOG_WARNING, NULL);
1921                 return;
1922                 }
1923
1924         /* select the first iter, so that on fail the correct info is given to user */
1925         if (gtk_tree_model_get_iter_first(store, &iter))
1926                 {
1927                 GtkTreeSelection *selection;
1928
1929                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
1930                 gtk_tree_selection_select_iter(selection, &iter);
1931                 }
1932
1933         /* now do it for real */
1934         success = TRUE;
1935         n = start_n;
1936         while (success && gtk_tree_model_get_iter_first(store, &iter))
1937                 {
1938                 gchar *dest;
1939                 gchar *base;
1940                 gchar *path;
1941
1942                 gtk_tree_model_get(store, &iter, RENAME_COLUMN_PATH, &path, -1);
1943                 base = remove_level_from_path(path);
1944
1945                 if (rd->rename_type == RENAME_TYPE_FORMATTED)
1946                         {
1947                         gchar *new_name;
1948
1949                         new_name = file_util_rename_multiple_auto_format_name(format, filename_from_path(path), n);
1950                         dest = g_strconcat(base, "/", new_name, NULL);
1951                         g_free(new_name);
1952                         }
1953                 else
1954                         {
1955                         dest = g_strdup_printf("%s/%s%0*d%s", base, front, padding, n, end);
1956                         }
1957
1958                 if (!rename_file(path, dest))
1959                         {
1960                         success = FALSE;
1961                         }
1962                 else
1963                         {
1964                         file_maint_renamed(path, dest);
1965                         }
1966
1967                 g_free(dest);
1968                 g_free(base);
1969                 g_free(path);
1970
1971                 if (success)
1972                         {
1973                         gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1974                         if (gtk_tree_model_get_iter_first(store, &iter))
1975                                 {
1976                                 GtkTreeSelection *selection;
1977
1978                                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
1979                                 gtk_tree_selection_select_iter(selection, &iter);
1980                                 }
1981                         }
1982
1983                 n++;
1984                 }
1985
1986         if (!success)
1987                 {
1988                 gchar *buf;
1989
1990                 n--;
1991                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(rd->auto_spin_start), (gdouble)n);
1992
1993                 buf = g_strdup_printf(_("Failed to rename\n%s\nThe number was %d."), filename_from_path(rd->fd->source_path), n);
1994                 file_util_warning_dialog(_("Auto rename"), buf, GTK_STOCK_DIALOG_ERROR, NULL);
1995                 g_free(buf);
1996
1997                 return;
1998                 }
1999
2000         file_dialog_close(rd->fd);
2001 }
2002
2003 static void file_util_rename_multiple_cb(FileDialog *fd, gpointer data)
2004 {
2005         RenameDataMult *rd = data;
2006         gchar *base;
2007         const gchar *name;
2008
2009         if (rd->rename_type != RENAME_TYPE_MANUAL)
2010                 {
2011                 file_util_rename_multiple_auto(rd);
2012                 return;
2013                 }
2014
2015         name = gtk_entry_get_text(GTK_ENTRY(rd->rename_entry));
2016         base = remove_level_from_path(fd->source_path);
2017
2018         g_free(fd->dest_path);
2019         fd->dest_path = concat_dir_and_file(base, name);
2020         g_free(base);
2021
2022         if (strlen(name) == 0 || strcmp(fd->source_path, fd->dest_path) == 0)
2023                 {
2024                 return;
2025                 }
2026
2027         fd->type = FALSE;
2028         file_util_rename_multiple(rd);
2029 }
2030
2031 static void file_util_rename_multiple_close_cb(FileDialog *fd, gpointer data)
2032 {
2033         RenameDataMult *rd = data;
2034
2035         file_dialog_close(rd->fd);
2036 }
2037
2038 static gboolean file_util_rename_multiple_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
2039                                                     gboolean path_currently_selected, gpointer data)
2040 {
2041         RenameDataMult *rd = data;
2042         GtkTreeIter iter;
2043         const gchar *name;
2044         gchar *path = NULL;
2045
2046         if (path_currently_selected ||
2047             !gtk_tree_model_get_iter(store, &iter, tpath)) return TRUE;
2048         gtk_tree_model_get(store, &iter, RENAME_COLUMN_PATH, &path, -1);
2049
2050         g_free(rd->fd->source_path);
2051         rd->fd->source_path = path;
2052
2053         name = filename_from_path(rd->fd->source_path);
2054         gtk_label_set_text(GTK_LABEL(rd->rename_label), name);
2055         gtk_entry_set_text(GTK_ENTRY(rd->rename_entry), name);
2056
2057         image_change_path(rd->imd, rd->fd->source_path, 0.0);
2058
2059         if (GTK_WIDGET_VISIBLE(rd->rename_box))
2060                 {
2061                 gtk_widget_grab_focus(rd->rename_entry);
2062                 gtk_editable_select_region(GTK_EDITABLE(rd->rename_entry), 0, filename_base_length(name));
2063                 }
2064
2065         return TRUE;
2066 }
2067
2068 static void file_util_rename_multiple_preview_update(RenameDataMult *rd)
2069 {
2070         GtkTreeModel *store;
2071         GtkTreeIter iter;
2072         const gchar *front;
2073         const gchar *end;
2074         const gchar *format;
2075         gint valid;
2076         gint start_n;
2077         gint padding;
2078         gint n;
2079
2080         front = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_front));
2081         end = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_end));
2082         padding = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_pad));
2083
2084         format = gtk_entry_get_text(GTK_ENTRY(rd->format_entry));
2085
2086         if (rd->rename_type == RENAME_TYPE_FORMATTED)
2087                 {
2088                 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->format_spin));
2089                 }
2090         else
2091                 {
2092                 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_start));
2093                 }
2094
2095         store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
2096         n = start_n;
2097         valid = gtk_tree_model_get_iter_first(store, &iter);
2098         while (valid)
2099                 {
2100                 gchar *dest;
2101
2102                 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2103                         {
2104                         gchar *path;
2105         
2106                         gtk_tree_model_get(store, &iter, RENAME_COLUMN_PATH, &path, -1);
2107                         dest = file_util_rename_multiple_auto_format_name(format, filename_from_path(path), n);
2108                         g_free(path);
2109                         }
2110                 else
2111                         {
2112                         dest = g_strdup_printf("%s%0*d%s", front, padding, n, end);
2113                         }
2114                 gtk_list_store_set(GTK_LIST_STORE(store), &iter, RENAME_COLUMN_PREVIEW, dest, -1);
2115                 g_free(dest);
2116
2117                 n++;
2118                 valid = gtk_tree_model_iter_next(store, &iter);
2119                 }
2120
2121 }
2122
2123 static gboolean file_util_rename_multiple_idle_cb(gpointer data)
2124 {
2125         RenameDataMult *rd = data;
2126
2127         file_util_rename_multiple_preview_update(rd);
2128
2129         rd->update_idle_id = -1;
2130         return FALSE;
2131 }
2132
2133 static void file_util_rename_multiple_preview_order_cb(GtkTreeModel *treemodel, GtkTreePath *tpath,
2134                                                        GtkTreeIter *iter, gpointer data)
2135 {
2136         RenameDataMult *rd = data;
2137
2138         if (rd->rename_type != RENAME_TYPE_MANUAL && rd->update_idle_id == -1)
2139                 {
2140                 rd->update_idle_id = g_idle_add(file_util_rename_multiple_idle_cb, rd);
2141                 }
2142 }
2143
2144 static void file_util_rename_multiple_preview_entry_cb(GtkWidget *entry, gpointer data)
2145 {
2146         RenameDataMult *rd = data;
2147         file_util_rename_multiple_preview_update(rd);
2148 }
2149
2150 static void file_util_rename_multiple_preview_adj_cb(GtkWidget *spin, gpointer data)
2151 {
2152         RenameDataMult *rd = data;
2153         file_util_rename_multiple_preview_update(rd);
2154 }
2155
2156 static void file_util_rename_multiple_auto_change(GtkWidget *widget, gpointer data)
2157 {
2158         RenameDataMult *rd = data;
2159         GtkTreeViewColumn *column;
2160
2161         rd->rename_type = gtk_combo_box_get_active(GTK_COMBO_BOX(rd->combo_type));
2162
2163         switch (rd->rename_type)
2164                 {
2165                 case RENAME_TYPE_FORMATTED:
2166                         if (GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_hide(rd->rename_box);
2167                         if (GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_hide(rd->auto_box);
2168                         if (!GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_show(rd->format_box);
2169                         file_util_rename_multiple_preview_update(rd);
2170                         break;
2171                 case RENAME_TYPE_AUTO:
2172                         if (GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_hide(rd->format_box);
2173                         if (GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_hide(rd->rename_box);
2174                         if (!GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_show(rd->auto_box);
2175                         file_util_rename_multiple_preview_update(rd);
2176                         break;
2177                 case RENAME_TYPE_MANUAL:
2178                 default:
2179                         if (GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_hide(rd->format_box);
2180                         if (GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_hide(rd->auto_box);
2181                         if (!GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_show(rd->rename_box);
2182                         break;
2183                 }
2184
2185         column = gtk_tree_view_get_column(GTK_TREE_VIEW(rd->listview), RENAME_COLUMN_PREVIEW - 1);
2186         gtk_tree_view_column_set_visible(column, (rd->rename_type != RENAME_TYPE_MANUAL));
2187 }
2188
2189 static GtkWidget *furm_simple_vlabel(GtkWidget *box, const gchar *text, gint expand)
2190 {
2191         GtkWidget *vbox;
2192         GtkWidget *label;
2193
2194         vbox = gtk_vbox_new(FALSE, 0);
2195         gtk_box_pack_start(GTK_BOX(box), vbox, expand, expand, 0);
2196         gtk_widget_show(vbox);
2197
2198         label = gtk_label_new(text);
2199         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
2200         gtk_widget_show(label);
2201
2202         return vbox;
2203 }
2204
2205 static GtkTreeViewColumn *file_util_rename_multiple_add_column(RenameDataMult *rd, const gchar *text, gint n)
2206 {
2207         GtkTreeViewColumn *column;
2208         GtkCellRenderer *renderer;
2209
2210         column = gtk_tree_view_column_new();
2211         gtk_tree_view_column_set_title(column, text);
2212         gtk_tree_view_column_set_min_width(column, 4);
2213         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2214         renderer = gtk_cell_renderer_text_new();
2215         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2216         gtk_tree_view_column_add_attribute(column, renderer, "text", n);
2217         gtk_tree_view_append_column(GTK_TREE_VIEW(rd->listview), column);
2218
2219         return column;
2220 }
2221
2222 static void file_util_rename_multiple_destroy_cb(GtkWidget *widget, gpointer data)
2223 {
2224         RenameDataMult *rd = data;
2225
2226         if (rd->update_idle_id != -1) g_source_remove(rd->update_idle_id);
2227
2228         g_free(rd);
2229 }
2230
2231 static void file_util_rename_multiple_do(GList *source_list, GtkWidget *parent)
2232 {
2233         RenameDataMult *rd;
2234         GtkWidget *pane;
2235         GtkWidget *scrolled;
2236         GtkListStore *store;
2237         GtkTreeSelection *selection;
2238         GtkTreeViewColumn *column;
2239         GtkWidget *hbox;
2240         GtkWidget *vbox;
2241         GtkWidget *box2;
2242         GtkWidget *table;
2243         GtkWidget *combo;
2244         GList *work;
2245         const gchar *name;
2246
2247         rd = g_new0(RenameDataMult, 1);
2248
2249         rd->fd = file_util_file_dlg(_("Rename - GQview"),
2250                                     "GQview", "dlg_rename", parent,
2251                                     file_util_rename_multiple_close_cb, rd);
2252         generic_dialog_add_message(GENERIC_DIALOG(rd->fd), NULL, _("Rename multiple files"), NULL);
2253         file_dialog_add_button(rd->fd, GTK_STOCK_OK, _("_Rename"), file_util_rename_multiple_cb, TRUE);
2254
2255         rd->fd->source_path = g_strdup(source_list->data);
2256         rd->fd->dest_path = NULL;
2257
2258         rd->rename_type = RENAME_TYPE_MANUAL;
2259
2260         rd->update_idle_id = -1;
2261
2262         vbox = GENERIC_DIALOG(rd->fd)->vbox;
2263
2264         pane = gtk_hpaned_new();
2265         gtk_box_pack_start(GTK_BOX(vbox), pane, TRUE, TRUE, 0);
2266         gtk_widget_show(pane);
2267         
2268         scrolled = gtk_scrolled_window_new(NULL, NULL);
2269         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2270         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
2271                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2272         gtk_paned_pack1(GTK_PANED(pane), scrolled, TRUE, TRUE);
2273         gtk_widget_show(scrolled);
2274
2275         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
2276         rd->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2277         g_object_unref(store);
2278
2279         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(rd->listview), TRUE);
2280         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(rd->listview), FALSE);
2281
2282         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2283         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2284         gtk_tree_selection_set_select_function(selection, file_util_rename_multiple_select_cb, rd, NULL);
2285
2286         file_util_rename_multiple_add_column(rd, _("Original Name"), RENAME_COLUMN_NAME);
2287         column = file_util_rename_multiple_add_column(rd, _("Preview"), RENAME_COLUMN_PREVIEW);
2288         gtk_tree_view_column_set_visible(column, FALSE);
2289         
2290         gtk_tree_view_set_reorderable(GTK_TREE_VIEW(rd->listview), TRUE);
2291         g_signal_connect(G_OBJECT(store), "row_changed",
2292                          G_CALLBACK(file_util_rename_multiple_preview_order_cb), rd);
2293         gtk_widget_set_size_request(rd->listview, 250, 150);
2294
2295         gtk_container_add(GTK_CONTAINER(scrolled), rd->listview);
2296         gtk_widget_show(rd->listview);
2297
2298         work = source_list;
2299         while (work)
2300                 {
2301                 gchar *path = work->data;
2302                 GtkTreeIter iter;
2303
2304                 gtk_list_store_append(store, &iter);
2305                 gtk_list_store_set(store, &iter, RENAME_COLUMN_PATH, path, RENAME_COLUMN_NAME, filename_from_path(path), -1);
2306
2307                 work = work->next;
2308                 }
2309
2310         path_list_free(source_list);
2311
2312         rd->imd = image_new(TRUE);
2313         g_object_set(G_OBJECT(rd->imd->pr), "zoom_expand", FALSE, NULL);
2314         gtk_widget_set_size_request(rd->imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
2315         gtk_paned_pack2(GTK_PANED(pane), rd->imd->widget, FALSE, TRUE);
2316         gtk_widget_show(rd->imd->widget);
2317
2318         hbox = gtk_hbox_new(FALSE, 0);
2319         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2320         gtk_widget_show(hbox);
2321
2322         rd->combo_type = gtk_combo_box_new_text();
2323
2324         gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Manual rename"));
2325         gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Formatted rename"));
2326         gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Auto rename"));
2327
2328         gtk_combo_box_set_active(GTK_COMBO_BOX(rd->combo_type), rd->rename_type);
2329
2330         g_signal_connect(G_OBJECT(rd->combo_type), "changed",
2331                          G_CALLBACK(file_util_rename_multiple_auto_change), rd);
2332         gtk_box_pack_end(GTK_BOX(hbox), rd->combo_type, FALSE, FALSE, 0);
2333         gtk_widget_show(rd->combo_type);
2334
2335         rd->rename_box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, 0);
2336         table = pref_table_new(rd->rename_box, 2, 2, FALSE, FALSE);
2337
2338         pref_table_label(table, 0, 0, _("Original name:"), 1.0);
2339         rd->rename_label = pref_table_label(table, 1, 0, filename_from_path(rd->fd->source_path), 0.0);
2340
2341         pref_table_label(table, 0, 1, _("New name:"), 1.0);
2342
2343         rd->rename_entry = gtk_entry_new();
2344         gtk_table_attach(GTK_TABLE(table), rd->rename_entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
2345         generic_dialog_attach_default(GENERIC_DIALOG(rd->fd), rd->rename_entry);
2346         gtk_widget_grab_focus(rd->rename_entry);
2347
2348         name = filename_from_path(rd->fd->source_path);
2349         gtk_entry_set_text(GTK_ENTRY(rd->rename_entry), name);
2350         gtk_editable_select_region(GTK_EDITABLE(rd->rename_entry), 0, filename_base_length(name));
2351         gtk_widget_show(rd->rename_entry);
2352
2353         rd->auto_box = gtk_vbox_new(FALSE, PREF_PAD_GAP);
2354         gtk_box_pack_start(GTK_BOX(vbox), rd->auto_box, FALSE, FALSE, 0);
2355         /* do not show it here */
2356
2357         hbox = pref_box_new(rd->auto_box, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
2358
2359         box2 = furm_simple_vlabel(hbox, _("Begin text"), TRUE);
2360
2361         combo = history_combo_new(&rd->auto_entry_front, "", "numerical_rename_prefix", -1);
2362         g_signal_connect(G_OBJECT(rd->auto_entry_front), "changed",
2363                          G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2364         gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2365         gtk_widget_show(combo);
2366         
2367         box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
2368
2369         rd->auto_spin_start = pref_spin_new(box2, NULL, NULL,
2370                                             0.0, 1000000.0, 1.0, 0, 1.0,
2371                                             G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2372
2373         box2 = furm_simple_vlabel(hbox, _("End text"), TRUE);
2374
2375         combo = history_combo_new(&rd->auto_entry_end, "", "numerical_rename_suffix", -1);
2376         g_signal_connect(G_OBJECT(rd->auto_entry_end), "changed",
2377                          G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2378         gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2379         gtk_widget_show(combo);
2380
2381         rd->auto_spin_pad = pref_spin_new(rd->auto_box, _("Padding:"), NULL,
2382                                           1.0, 8.0, 1.0, 0, 1.0,
2383                                           G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2384
2385         rd->format_box = gtk_vbox_new(FALSE, PREF_PAD_GAP);
2386         gtk_box_pack_start(GTK_BOX(vbox), rd->format_box, FALSE, FALSE, 0);
2387         /* do not show it here */
2388
2389         hbox = pref_box_new(rd->format_box, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
2390
2391         box2 = furm_simple_vlabel(hbox, _("Format (* = original name, ## = numbers)"), TRUE);
2392
2393         combo = history_combo_new(&rd->format_entry, "", "auto_rename_format", -1);
2394         g_signal_connect(G_OBJECT(rd->format_entry), "changed",
2395                          G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2396         gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2397         gtk_widget_show(combo);
2398         
2399         box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
2400
2401         rd->format_spin = pref_spin_new(box2, NULL, NULL,
2402                                         0.0, 1000000.0, 1.0, 0, 1.0,
2403                                         G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2404
2405         image_change_path(rd->imd, rd->fd->source_path, 0.0);
2406
2407         g_signal_connect(G_OBJECT(GENERIC_DIALOG(rd->fd)->dialog), "destroy",
2408                          G_CALLBACK(file_util_rename_multiple_destroy_cb), rd);
2409
2410         gtk_widget_show(GENERIC_DIALOG(rd->fd)->dialog);
2411 }
2412
2413 /*
2414  * rename single file
2415  */
2416
2417 static void file_util_rename_single(FileDataSingle *fds);
2418
2419 static void file_util_rename_single_ok_cb(GenericDialog *gd, gpointer data)
2420 {
2421         FileDataSingle *fds = data;
2422         fds->confirmed = TRUE;
2423         file_util_rename_single(fds);
2424 }
2425
2426 static void file_util_rename_single_cancel_cb(GenericDialog *gd, gpointer data)
2427 {
2428         FileDataSingle *fds = data;
2429         file_data_single_free(fds);
2430 }
2431
2432 static void file_util_rename_single(FileDataSingle *fds)
2433 {
2434         if (isfile(fds->dest) && !fds->confirmed)
2435                 {
2436                 GenericDialog *gd;
2437
2438                 gd = file_util_gen_dlg(_("Overwrite file"), "GQview", "dlg_confirm",
2439                                         NULL, TRUE,
2440                                         file_util_rename_single_cancel_cb, fds);
2441
2442                 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
2443                                            _("Overwrite file?"),
2444                                            _("Replace existing file by renaming new file."));
2445                 pref_spacer(gd->vbox, 0);
2446
2447                 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_rename_single_ok_cb, TRUE);
2448                 generic_dialog_add_image(gd, NULL,
2449                                          fds->dest, _("Existing file"),
2450                                          fds->source, _("New file"), TRUE);
2451
2452                 gtk_widget_show(gd->dialog);
2453
2454                 return;
2455                 }
2456         else
2457                 {
2458                 if (!rename_file(fds->source, fds->dest))
2459                         {
2460                         gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), filename_from_path(fds->source), filename_from_path(fds->dest));
2461                         file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2462                         g_free(text);
2463                         }
2464                 else
2465                         {
2466                         file_maint_renamed(fds->source, fds->dest);
2467                         }
2468                 }
2469         file_data_single_free(fds);
2470 }
2471
2472 static void file_util_rename_single_cb(FileDialog *fd, gpointer data)
2473 {
2474         const gchar *name;
2475         gchar *path;
2476
2477         name = gtk_entry_get_text(GTK_ENTRY(fd->entry));
2478         path = concat_dir_and_file(fd->dest_path, name);
2479
2480         if (strlen(name) == 0 || strcmp(fd->source_path, path) == 0)
2481                 {
2482                 g_free(path);
2483                 return;
2484                 }
2485
2486         file_util_rename_single(file_data_single_new(fd->source_path, path, fd->type));
2487
2488         g_free(path);
2489         file_dialog_close(fd);
2490 }
2491
2492 static void file_util_rename_single_close_cb(FileDialog *fd, gpointer data)
2493 {
2494         file_dialog_close(fd);
2495 }
2496
2497 static void file_util_rename_single_do(const gchar *source_path, GtkWidget *parent)
2498 {
2499         FileDialog *fd;
2500         GtkWidget *table;
2501         const gchar *name;
2502
2503         fd = file_util_file_dlg(_("Rename - GQview"), "GQview", "dlg_rename", parent,
2504                              file_util_rename_single_close_cb, NULL);
2505
2506         generic_dialog_add_message(GENERIC_DIALOG(fd), NULL, _("Rename file"), NULL);
2507         generic_dialog_add_image(GENERIC_DIALOG(fd), NULL, source_path, NULL, NULL, NULL, FALSE);
2508
2509         file_dialog_add_button(fd, GTK_STOCK_OK, _("_Rename"), file_util_rename_single_cb, TRUE);
2510
2511         fd->source_path = g_strdup(source_path);
2512         fd->dest_path = remove_level_from_path(source_path);
2513
2514         table = pref_table_new(GENERIC_DIALOG(fd)->vbox, 2, 2, FALSE, FALSE);
2515
2516         pref_table_label(table, 0, 0, _("Original name:"), 1.0);
2517         pref_table_label(table, 1, 0, filename_from_path(fd->source_path), 0.0);
2518
2519         pref_table_label(table, 0, 1, _("New name:"), 1.0);
2520
2521         fd->entry = gtk_entry_new();
2522         gtk_table_attach(GTK_TABLE(table), fd->entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
2523         generic_dialog_attach_default(GENERIC_DIALOG(fd), fd->entry);
2524         gtk_widget_grab_focus(fd->entry);
2525
2526         name = filename_from_path(fd->source_path);
2527         gtk_entry_set_text(GTK_ENTRY(fd->entry), name);
2528         gtk_editable_select_region(GTK_EDITABLE(fd->entry), 0, filename_base_length(name));
2529         gtk_widget_show(fd->entry);
2530
2531         gtk_widget_show(GENERIC_DIALOG(fd)->dialog);
2532 }
2533
2534 void file_util_rename(const gchar *source_path, GList *source_list, GtkWidget *parent)
2535 {
2536         if (!source_path && !source_list) return;
2537
2538         if (source_path)
2539                 {
2540                 file_util_rename_single_do(source_path, parent);
2541                 }
2542         else if (!source_list->next)
2543                 {
2544                 file_util_rename_single_do(source_list->data, parent);
2545                 path_list_free(source_list);
2546                 }
2547         else
2548                 {
2549                 file_util_rename_multiple_do(source_list, parent);
2550                 }
2551 }
2552
2553 /*
2554  *--------------------------------------------------------------------------
2555  * Create directory routines
2556  *--------------------------------------------------------------------------
2557  */
2558
2559 static void file_util_create_dir_do(const gchar *base, const gchar *name)
2560 {
2561         gchar *path;
2562
2563         path = concat_dir_and_file(base, name);
2564
2565         if (isdir(path))
2566                 {
2567                 gchar *text = g_strdup_printf(_("The folder:\n%s\nalready exists."), name);
2568                 file_util_warning_dialog(_("Folder exists"), text, GTK_STOCK_DIALOG_INFO, NULL);
2569                 g_free(text);
2570                 }
2571         else if (isname(path))
2572                 {
2573                 gchar *text = g_strdup_printf(_("The path:\n%s\nalready exists as a file."), name);
2574                 file_util_warning_dialog(_("Could not create folder"), text, GTK_STOCK_DIALOG_INFO, NULL);
2575                 g_free(text);
2576                 }
2577         else
2578                 {
2579                 if (!mkdir_utf8(path, 0755))
2580                         {
2581                         gchar *text = g_strdup_printf(_("Unable to create folder:\n%s"), name);
2582                         file_util_warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2583                         g_free(text);
2584                         }
2585                 }
2586
2587         g_free(path);
2588 }
2589
2590 static void file_util_create_dir_cb(FileDialog *fd, gpointer data)
2591 {
2592         const gchar *name;
2593
2594         name = gtk_entry_get_text(GTK_ENTRY(fd->entry));
2595
2596         if (strlen(name) == 0) return;
2597
2598         if (name[0] == '/')
2599                 {
2600                 gchar *buf;
2601                 buf  = remove_level_from_path(name);
2602                 file_util_create_dir_do(buf, filename_from_path(name));
2603                 g_free(buf);
2604                 }
2605         else
2606                 {
2607                 file_util_create_dir_do(fd->dest_path, name);
2608                 }
2609
2610         file_dialog_close(fd);
2611 }
2612
2613 static void file_util_create_dir_close_cb(FileDialog *fd, gpointer data)
2614 {
2615         file_dialog_close(fd);
2616 }
2617
2618 void file_util_create_dir(const gchar *path, GtkWidget *parent)
2619 {
2620         FileDialog *fd;
2621         gchar *text;
2622
2623         if (!isdir(path)) return;
2624
2625         fd = file_util_file_dlg(_("New folder - GQview"), "GQview", "dlg_newdir", parent,
2626                              file_util_create_dir_close_cb, NULL);
2627
2628         text = g_strdup_printf(_("Create folder in:\n%s\nnamed:"), path);
2629         generic_dialog_add_message(GENERIC_DIALOG(fd), NULL, NULL, text);
2630         g_free(text);
2631
2632         file_dialog_add_button(fd, GTK_STOCK_OK, NULL, file_util_create_dir_cb, TRUE);
2633
2634         fd->dest_path = g_strdup(path);
2635
2636         fd->entry = gtk_entry_new();
2637         gtk_box_pack_start(GTK_BOX(GENERIC_DIALOG(fd)->vbox), fd->entry, FALSE, FALSE, 0);
2638         generic_dialog_attach_default(GENERIC_DIALOG(fd), fd->entry);
2639         gtk_widget_grab_focus(fd->entry);
2640         gtk_widget_show(fd->entry);
2641
2642         gtk_widget_show(GENERIC_DIALOG(fd)->dialog);
2643 }
2644
2645 gint file_util_rename_dir(const gchar *old_path, const gchar *new_path, GtkWidget *parent)
2646 {
2647         const gchar *old_name;
2648         const gchar *new_name;
2649
2650         if (!old_path || !new_path || !isdir(old_path)) return FALSE;
2651
2652         old_name = filename_from_path(old_path);
2653         new_name = filename_from_path(new_path);
2654
2655         if (isdir(new_path))
2656                 {
2657                 gchar *text = g_strdup_printf(_("The folder:\n%s\nalready exists."), new_name);
2658                 file_util_warning_dialog(_("Folder exists"), text, GTK_STOCK_DIALOG_INFO, parent);
2659                 g_free(text);
2660
2661                 return FALSE;
2662                 }
2663
2664         if (isname(new_path))
2665                 {
2666                 gchar *text = g_strdup_printf(_("The path:\n%s\nalready exists as a file."), new_name);
2667                 file_util_warning_dialog(_("Rename failed"), text, GTK_STOCK_DIALOG_INFO,parent);
2668                 g_free(text);
2669
2670                 return FALSE;
2671                 }
2672
2673         if (!rename_file(old_path, new_path))
2674                 {
2675                 gchar *text = g_strdup_printf(_("Failed to rename %s to %s."), old_name, new_name);
2676                 file_util_warning_dialog(_("Rename failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
2677                 g_free(text);
2678
2679                 return FALSE;
2680                 }
2681
2682         return TRUE;
2683 }
2684
2685 /*
2686  *--------------------------------------------------------------------------
2687  * Delete directory routines
2688  *--------------------------------------------------------------------------
2689  */
2690
2691 /* The plan is to eventually make all of utilops.c
2692  * use UtilityData and UtilityType.
2693  * And clean up the above mess someday.
2694  */
2695
2696 typedef enum {
2697         UTILITY_TYPE_NONE = 0,
2698         UTILITY_TYPE_COPY,
2699         UTILITY_TYPE_MOVE,
2700         UTILITY_TYPE_RENAME,
2701         UTILITY_TYPE_DELETE,
2702         UTILITY_TYPE_DELETE_LINK,
2703         UTILITY_TYPE_DELETE_FOLDER
2704 } UtilityType;
2705
2706 typedef struct _UtilityData UtilityData;
2707 struct _UtilityData {
2708         UtilityType type;
2709         gchar *source_path;
2710         GList *dlist;
2711         GList *flist;
2712
2713         GenericDialog *gd;
2714 };
2715
2716 #define UTILITY_LIST_MIN_WIDTH  250
2717 #define UTILITY_LIST_MIN_HEIGHT 150
2718
2719 /* thumbnail spec has a max depth of 4 (.thumb??/fail/appname/??.png) */
2720 #define UTILITY_DELETE_MAX_DEPTH 5
2721
2722
2723 static void file_util_data_free(UtilityData *ud)
2724 {
2725         if (!ud) return;
2726
2727         g_free(ud->source_path);
2728         path_list_free(ud->dlist);
2729         path_list_free(ud->flist);
2730
2731         if (ud->gd) generic_dialog_close(ud->gd);
2732
2733         g_free(ud);
2734 }
2735
2736 static GtkTreeViewColumn *file_util_dialog_add_list_column(GtkWidget *view, const gchar *text, gint n)
2737 {
2738         GtkTreeViewColumn *column;
2739         GtkCellRenderer *renderer;
2740
2741         column = gtk_tree_view_column_new();
2742         gtk_tree_view_column_set_title(column, text);
2743         gtk_tree_view_column_set_min_width(column, 4);
2744         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2745         renderer = gtk_cell_renderer_text_new();
2746         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2747         gtk_tree_view_column_add_attribute(column, renderer, "text", n);
2748         gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
2749
2750         return column;
2751 }
2752
2753 static GtkWidget *file_util_dialog_add_list(GtkWidget *box, GList *list, gint full_paths)
2754 {
2755         GtkWidget *scrolled;
2756         GtkWidget *view;
2757         GtkListStore *store;
2758
2759         scrolled = gtk_scrolled_window_new(NULL, NULL);
2760         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2761         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
2762                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2763         gtk_box_pack_start(GTK_BOX(box), scrolled, TRUE, TRUE, 0);
2764         gtk_widget_show(scrolled);
2765
2766         store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
2767         view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2768         g_object_unref(store);
2769
2770         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
2771         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), FALSE);
2772
2773         if (full_paths)
2774                 {
2775                 file_util_dialog_add_list_column(view, _("Location"), 0);
2776                 }
2777         else
2778                 {
2779                 file_util_dialog_add_list_column(view, _("Name"), 1);
2780                 }
2781
2782         gtk_widget_set_size_request(view, UTILITY_LIST_MIN_WIDTH, UTILITY_LIST_MIN_HEIGHT);
2783         gtk_container_add(GTK_CONTAINER(scrolled), view);
2784         gtk_widget_show(view);
2785
2786         while (list)
2787                 {
2788                 gchar *path = list->data;
2789                 GtkTreeIter iter;
2790
2791                 gtk_list_store_append(store, &iter);
2792                 gtk_list_store_set(store, &iter, 0, path,
2793                                                  1, filename_from_path(path), -1);
2794
2795                 list = list->next;
2796                 }
2797
2798         return view;
2799 }
2800
2801 static gboolean file_util_delete_dir_preview_cb(GtkTreeSelection *selection, GtkTreeModel *store,
2802                                                 GtkTreePath *tpath, gboolean path_currently_selected,
2803                                                 gpointer data)
2804 {
2805         UtilityData *ud = data;
2806         GtkTreeIter iter;
2807         gchar *path = NULL;
2808
2809         if (path_currently_selected ||
2810             !gtk_tree_model_get_iter(store, &iter, tpath)) return TRUE;
2811
2812         gtk_tree_model_get(store, &iter, 0, &path, -1);
2813         generic_dialog_image_set(ud->gd, path);
2814         g_free(path);
2815
2816         return TRUE;
2817 }
2818
2819 static void file_util_delete_dir_cancel_cb(GenericDialog *gd, gpointer data)
2820 {
2821         UtilityData *ud = data;
2822
2823         ud->gd = NULL;
2824         file_util_data_free(ud);
2825 }
2826
2827 static gchar *file_util_delete_dir_empty_path(const gchar *path, gint real_content, gint level)
2828 {
2829         GList *dlist = NULL;
2830         GList *flist = NULL;
2831         GList *work;
2832         gchar *fail = NULL;
2833
2834         if (debug) printf("deltree into: %s\n", path);
2835
2836         level++;
2837         if (level > UTILITY_DELETE_MAX_DEPTH)
2838                 {
2839                 printf("folder recursion depth past %d, giving up\n", UTILITY_DELETE_MAX_DEPTH);
2840                 return g_strdup(path);
2841                 }
2842
2843         if (!path_list_lstat(path, &flist, &dlist)) return g_strdup(path);
2844
2845         work = dlist;
2846         while (work && !fail)
2847                 {
2848                 gchar *name;
2849
2850                 name = work->data;
2851                 work = work->next;
2852
2853                 fail = file_util_delete_dir_empty_path(name, real_content, level);
2854                 }
2855
2856         work = flist;
2857         while (work && !fail)
2858                 {
2859                 gchar *name;
2860
2861                 name = work->data;
2862                 work = work->next;
2863
2864                 if (debug) printf("deltree child: %s\n", name);
2865
2866                 if (real_content && !islink(name))
2867                         {
2868                         if (!file_util_unlink(name)) fail = g_strdup(name);
2869                         }
2870                 else
2871                         {
2872                         if (!unlink_file(name)) fail = g_strdup(name);
2873                         }
2874                 }
2875
2876         path_list_free(dlist);
2877         path_list_free(flist);
2878
2879         if (!fail && !rmdir_utf8(path))
2880                 {
2881                 fail = g_strdup(path);
2882                 }
2883
2884         if (debug) printf("deltree done: %s\n", path);
2885
2886         return fail;
2887 }
2888
2889 static void file_util_delete_dir_ok_cb(GenericDialog *gd, gpointer data)
2890 {
2891         UtilityData *ud = data;
2892
2893         ud->gd = NULL;
2894
2895         if (ud->type == UTILITY_TYPE_DELETE_LINK)
2896                 {
2897                 if (!unlink_file(ud->source_path))
2898                         {
2899                         gchar *text;
2900
2901                         text = g_strdup_printf("Unable to remove symbolic link:\n %s", ud->source_path);
2902                         file_util_warning_dialog(_("Delete failed"), text,
2903                                                  GTK_STOCK_DIALOG_ERROR, NULL);
2904                         g_free(text);
2905                         }
2906                 }
2907         else
2908                 {
2909                 gchar *fail = NULL;
2910                 GList *work;
2911
2912                 work = ud->dlist;
2913                 while (work && !fail)
2914                         {
2915                         gchar *path;
2916
2917                         path = work->data;
2918                         work = work->next;
2919
2920                         fail = file_util_delete_dir_empty_path(path, FALSE, 0);
2921                         }
2922
2923                 work = ud->flist;
2924                 while (work && !fail)
2925                         {
2926                         gchar *path;
2927
2928                         path = work->data;
2929                         work = work->next;
2930
2931                         if (debug) printf("deltree unlink: %s\n", path);
2932
2933                         if (islink(path))
2934                                 {
2935                                 if (!unlink_file(path)) fail = g_strdup(path);
2936                                 }
2937                         else
2938                                 {
2939                                 if (!file_util_unlink(path)) fail = g_strdup(path);
2940                                 }
2941                         }
2942
2943                 if (!fail)
2944                         {
2945                         if (!rmdir_utf8(ud->source_path)) fail = g_strdup(ud->source_path);
2946                         }
2947
2948                 if (fail)
2949                         {
2950                         gchar *text;
2951
2952                         text = g_strdup_printf(_("Unable to delete folder:\n\n%s"), ud->source_path);
2953                         gd = file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2954                         g_free(text);
2955
2956                         if (strcmp(fail, ud->source_path) != 0)
2957                                 {
2958                                 pref_spacer(gd->vbox, PREF_PAD_GROUP);
2959                                 text = g_strdup_printf(_("Removal of folder contents failed at this file:\n\n%s"),
2960                                                         fail);
2961                                 pref_label_new(gd->vbox, text);
2962                                 g_free(text);
2963                                 }
2964
2965                         g_free(fail);
2966                         }
2967                 }
2968
2969         file_util_data_free(ud);
2970 }
2971
2972 static GList *file_util_delete_dir_remaining_folders(GList *dlist)
2973 {
2974         GList *rlist = NULL;
2975
2976         while (dlist)
2977                 {
2978                 gchar *path;
2979                 const gchar *name;
2980
2981                 path = dlist->data;
2982                 dlist = dlist->next;
2983
2984                 name = filename_from_path(path);
2985                 if (!name ||
2986                     (strcmp(name, THUMB_FOLDER_GLOBAL) != 0 &&
2987                      strcmp(name, THUMB_FOLDER_LOCAL) != 0 &&
2988                      strcmp(name, GQVIEW_CACHE_LOCAL_METADATA) != 0) )
2989                         {
2990                         rlist = g_list_prepend(rlist, path);
2991                         }
2992                 }
2993
2994         return g_list_reverse(rlist);
2995 }
2996
2997 void file_util_delete_dir(const gchar *path, GtkWidget *parent)
2998 {
2999         GList *dlist = NULL;
3000         GList *flist = NULL;
3001         GList *rlist;
3002
3003         if (!isdir(path)) return;
3004
3005         if (islink(path))
3006                 {
3007                 UtilityData *ud;
3008                 gchar *text;
3009
3010                 ud = g_new0(UtilityData, 1);
3011                 ud->type = UTILITY_TYPE_DELETE_LINK;
3012                 ud->source_path = g_strdup(path);
3013                 ud->dlist = NULL;
3014                 ud->flist = NULL;
3015
3016                 ud->gd = file_util_gen_dlg(_("Delete folder"), "GQview", "dlg_confirm",
3017                                            parent, TRUE,
3018                                            file_util_delete_dir_cancel_cb, ud);
3019
3020                 text = g_strdup_printf(_("This will delete the symbolic link:\n\n%s\n\n"
3021                                          "The folder this link points to will not be deleted."),
3022                                         path);
3023                 generic_dialog_add_message(ud->gd, GTK_STOCK_DIALOG_QUESTION,
3024                                            _("Delete symbolic link to folder?"),
3025                                            text);
3026                 g_free(text);
3027
3028                 generic_dialog_add_button(ud->gd, GTK_STOCK_DELETE, NULL, file_util_delete_dir_ok_cb, TRUE);
3029
3030                 gtk_widget_show(ud->gd->dialog);
3031
3032                 return;
3033                 }
3034
3035         if (!access_file(path, W_OK | X_OK))
3036                 {
3037                 gchar *text;
3038
3039                 text = g_strdup_printf(_("Unable to remove folder %s\n"
3040                                          "Permissions do not allow writing to the folder."), path);
3041                 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
3042                 g_free(text);
3043
3044                 return;
3045                 }
3046
3047         if (!path_list_lstat(path, &flist, &dlist))
3048                 {
3049                 gchar *text;
3050
3051                 text = g_strdup_printf(_("Unable to list contents of folder %s"), path);
3052                 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
3053                 g_free(text);
3054
3055                 return;
3056                 }
3057
3058         rlist = file_util_delete_dir_remaining_folders(dlist);
3059         if (rlist)
3060                 {
3061                 GenericDialog *gd;
3062                 GtkWidget *box;
3063                 gchar *text;
3064
3065                 gd = file_util_gen_dlg(_("Folder contains subfolders"), "GQview", "dlg_warning",
3066                                         parent, TRUE, NULL, NULL);
3067                 generic_dialog_add_button(gd, GTK_STOCK_CLOSE, NULL, NULL, TRUE);
3068
3069                 text = g_strdup_printf(_("Unable to delete the folder:\n\n%s\n\n"
3070                                          "This folder contains subfolders which must be moved before it can be deleted."),
3071                                         path);
3072                 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING,
3073                                                  _("Folder contains subfolders"),
3074                                                  text);
3075                 g_free(text);
3076
3077                 box = pref_group_new(box, TRUE, _("Subfolders:"), GTK_ORIENTATION_VERTICAL);
3078
3079                 rlist = path_list_sort(rlist);
3080                 file_util_dialog_add_list(box, rlist, FALSE);
3081
3082                 gtk_widget_show(gd->dialog);
3083                 }
3084         else
3085                 {
3086                 UtilityData *ud;
3087                 GtkWidget *box;
3088                 GtkWidget *view;
3089                 GtkTreeSelection *selection;
3090                 gchar *text;
3091
3092                 ud = g_new0(UtilityData, 1);
3093                 ud->type = UTILITY_TYPE_DELETE_FOLDER;
3094                 ud->source_path = g_strdup(path);
3095                 ud->dlist = dlist;
3096                 dlist = NULL;
3097                 ud->flist = path_list_sort(flist);
3098                 flist = NULL;
3099
3100                 ud->gd = file_util_gen_dlg(_("Delete folder"), "GQview", "dlg_confirm",
3101                                            parent, TRUE, file_util_delete_dir_cancel_cb, ud);
3102                 generic_dialog_add_button(ud->gd, GTK_STOCK_DELETE, NULL, file_util_delete_dir_ok_cb, TRUE);
3103
3104                 text = g_strdup_printf(_("This will delete the folder:\n\n%s\n\n"
3105                                          "The contents of this folder will also be deleted."),
3106                                         path);
3107                 box = generic_dialog_add_message(ud->gd, GTK_STOCK_DIALOG_QUESTION,
3108                                                  _("Delete folder?"),
3109                                                  text);
3110                 g_free(text);
3111
3112                 box = pref_group_new(box, TRUE, _("Contents:"), GTK_ORIENTATION_HORIZONTAL);
3113
3114                 view = file_util_dialog_add_list(box, ud->flist, FALSE);
3115                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
3116                 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3117                 gtk_tree_selection_set_select_function(selection, file_util_delete_dir_preview_cb, ud, NULL);
3118
3119                 generic_dialog_add_image(ud->gd, box, NULL, NULL, NULL, NULL, FALSE);
3120
3121                 box_append_safe_delete_status(ud->gd);
3122
3123                 gtk_widget_show(ud->gd->dialog);
3124                 }
3125
3126         g_list_free(rlist);
3127         path_list_free(dlist);
3128         path_list_free(flist);
3129 }
3130