Sat May 14 13:04:23 2005 John Ellis <johne@verizon.net>
[geeqie.git] / src / utilops.c
1 /*
2  * GQview
3  * (C) 2004 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12
13 #include "gqview.h"
14 #include "utilops.h"
15
16
17 #include "cache_maint.h"
18 #include "collect.h"
19 #include "dupe.h"
20 #include "filelist.h"
21 #include "image.h"
22 #include "img-view.h"
23 #include "layout.h"
24 #include "search.h"
25 #include "ui_bookmark.h"
26 #include "ui_fileops.h"
27 #include "ui_misc.h"
28 #include "ui_tabcomp.h"
29
30
31 /*
32  *--------------------------------------------------------------------------
33  * call these when names change, files move, deleted, etc.
34  * so that any open windows are also updated
35  *--------------------------------------------------------------------------
36  */
37
38 void file_maint_renamed(const gchar *source, const gchar *dest)
39 {
40         cache_maint_moved(source, dest);
41         collection_maint_renamed(source, dest);
42
43         layout_maint_renamed(source, dest);
44         view_window_maint_moved(source, dest);
45         dupe_maint_renamed(source, dest);
46         search_maint_renamed(source, dest);
47 }
48
49 /* under most cases ignore_list should be NULL */
50 void file_maint_removed(const gchar *path, GList *ignore_list)
51 {
52         layout_maint_removed(path, ignore_list);
53         view_window_maint_removed(path, ignore_list);
54         dupe_maint_removed(path);
55         search_maint_removed(path);
56
57         collection_maint_removed(path);
58         cache_maint_removed(path);
59 }
60
61 /* special case for correct main window behavior */
62 void file_maint_moved(const gchar *source, const gchar *dest, GList *ignore_list)
63 {
64         cache_maint_moved(source, dest);
65         collection_maint_renamed(source, dest);
66
67         layout_maint_moved(source, dest, ignore_list);
68         view_window_maint_moved(source, dest);
69         dupe_maint_renamed(source, dest);
70         search_maint_renamed(source, dest);
71 }
72
73 void file_maint_copied(const gchar *source, const gchar *dest)
74 {
75         cache_maint_copied(source, dest);
76 }
77
78 /*
79  *--------------------------------------------------------------------------
80  * The file manipulation dialogs
81  *--------------------------------------------------------------------------
82  */
83
84
85 enum {
86         DIALOG_NEW_DIR,
87         DIALOG_COPY,
88         DIALOG_MOVE,
89         DIALOG_DELETE,
90         DIALOG_RENAME
91 };
92
93 typedef struct _FileDataMult FileDataMult;
94 struct _FileDataMult
95 {
96         gint confirm_all;
97         gint confirmed;
98         gint skip;
99         GList *source_list;
100         GList *source_next;
101         gchar *dest_base;
102         gchar *source;
103         gchar *dest;
104         gint copy;
105
106         gint rename;
107         gint rename_auto;
108         gint rename_all;
109
110         GtkWidget *rename_box;
111         GtkWidget *rename_entry;
112         GtkWidget *rename_auto_box;
113
114         GtkWidget *yes_all_button;
115 };
116
117 typedef struct _FileDataSingle FileDataSingle;
118 struct _FileDataSingle
119 {
120         gint confirmed;
121         gchar *source;
122         gchar *dest;
123         gint copy;
124
125         gint rename;
126         gint rename_auto;
127
128         GtkWidget *rename_box;
129         GtkWidget *rename_entry;
130         GtkWidget *rename_auto_box;
131 };
132
133 /*
134  *--------------------------------------------------------------------------
135  * Adds 1 or 2 images (if 2, side by side) to a GenericDialog
136  *--------------------------------------------------------------------------
137  */
138
139 #define DIALOG_DEF_IMAGE_DIM_X 200
140 #define DIALOG_DEF_IMAGE_DIM_Y 150
141
142 static void generic_dialog_add_image(GenericDialog *gd, const gchar *path1, const gchar *header1,
143                                      const gchar *path2, const gchar *header2,
144                                      gint show_filename)
145 {
146         ImageWindow *imd;
147         GtkWidget *hbox = NULL;
148         GtkWidget *vbox;
149         GtkWidget *label = NULL;
150
151         if (path2)
152                 {
153                 hbox = pref_box_new(gd->vbox, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
154                 }
155
156         /* image 1 */
157
158         vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP);
159         if (hbox)
160                 {
161                 GtkWidget *sep;
162
163                 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
164
165                 sep = gtk_vseparator_new();
166                 gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
167                 gtk_widget_show(sep);
168                 }
169         else
170                 {
171                 gtk_box_pack_start(GTK_BOX(gd->vbox), vbox, TRUE, TRUE, PREF_PAD_GAP);
172                 }
173         gtk_widget_show(vbox);
174
175         if (header1)
176                 {
177                 GtkWidget *head;
178
179                 head = pref_label_new(vbox, header1);
180                 pref_label_bold(head, TRUE, FALSE);
181                 gtk_misc_set_alignment(GTK_MISC(head), 0.0, 0.5);
182                 }
183
184         imd = image_new(FALSE);
185         gtk_widget_set_size_request(imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
186         gtk_box_pack_start(GTK_BOX(vbox), imd->widget, TRUE, TRUE, 0);
187         image_change_path(imd, path1, 0.0);
188         gtk_widget_show(imd->widget);
189
190         if (show_filename)
191                 {
192                 label = pref_label_new(vbox, (path1 == NULL) ? "" : filename_from_path(path1));
193                 }
194
195         /* only the first image is stored (for use in gd_image_set) */
196         g_object_set_data(G_OBJECT(gd->dialog), "img_image", imd);
197         g_object_set_data(G_OBJECT(gd->dialog), "img_label", label);
198                 
199
200         /* image 2 */
201
202         if (hbox && path2)
203                 {
204                 vbox = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
205
206                 if (header2)
207                         {
208                         GtkWidget *head;
209
210                         head = pref_label_new(vbox, header2);
211                         pref_label_bold(head, TRUE, FALSE);
212                         gtk_misc_set_alignment(GTK_MISC(head), 0.0, 0.5);
213                         }
214
215                 imd = image_new(FALSE);
216                 gtk_widget_set_size_request(imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
217                 gtk_box_pack_start(GTK_BOX(vbox), imd->widget, TRUE, TRUE, 0);
218                 image_change_path(imd, path2, 0.0);
219                 gtk_widget_show(imd->widget);
220
221                 pref_label_new(vbox, filename_from_path(path2));
222                 }
223 }
224
225 static void generic_dialog_image_set(GenericDialog *gd, const gchar *path)
226 {
227         ImageWindow *imd;
228         GtkWidget *label;
229         
230         imd = g_object_get_data(G_OBJECT(gd->dialog), "img_image");
231         label = g_object_get_data(G_OBJECT(gd->dialog), "img_label");
232
233         if (!imd) return;
234
235         image_change_path(imd, path, 0.0);
236         if (label) gtk_label_set_text(GTK_LABEL(label), filename_from_path(path));
237 }
238
239 /*
240  *--------------------------------------------------------------------------
241  * Wrappers to aid in setting additional dialog properties (unde mouse, etc.)
242  *--------------------------------------------------------------------------
243  */
244
245 GenericDialog *file_util_gen_dlg(const gchar *title,
246                                  const gchar *wmclass, const gchar *wmsubclass,
247                                  GtkWidget *parent, gint auto_close,
248                                  void (*cancel_cb)(GenericDialog *, gpointer), gpointer data)
249 {
250         GenericDialog *gd;
251
252         gd = generic_dialog_new(title, wmclass, wmsubclass, parent, auto_close, cancel_cb, data);
253         if (place_dialogs_under_mouse)
254                 {
255                 gtk_window_set_position(GTK_WINDOW(gd->dialog), GTK_WIN_POS_MOUSE);
256                 }
257
258         return gd;
259 }
260
261 FileDialog *file_util_file_dlg(const gchar *title,
262                                const gchar *wmclass, const gchar *wmsubclass,
263                                GtkWidget *parent,
264                                void (*cancel_cb)(FileDialog *, gpointer), gpointer data)
265 {
266         FileDialog *fd;
267
268         fd = file_dialog_new(title, wmclass, wmsubclass, parent, cancel_cb, data);
269         if (place_dialogs_under_mouse)
270                 {
271                 gtk_window_set_position(GTK_WINDOW(GENERIC_DIALOG(fd)->dialog), GTK_WIN_POS_MOUSE);
272                 }
273
274         return fd;
275 }
276
277 /* this warning dialog is copied from SLIK's ui_utildg.c,
278  * because it does not have a mouse center option,
279  * and we must center it before show, implement it here.
280  */
281 static void file_util_warning_dialog_ok_cb(GenericDialog *gd, gpointer data)
282 {
283         /* no op */
284 }
285
286 GenericDialog *file_util_warning_dialog(const gchar *heading, const gchar *message,
287                                         const gchar *icon_stock_id, GtkWidget *parent)
288 {
289         GenericDialog *gd;
290
291         gd = file_util_gen_dlg(heading, "GQview", "warning", parent, TRUE, NULL, NULL);
292         generic_dialog_add_message(gd, icon_stock_id, heading, message);
293         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, file_util_warning_dialog_ok_cb, TRUE);
294         if (place_dialogs_under_mouse)
295                 {
296                 gtk_window_set_position(GTK_WINDOW(gd->dialog), GTK_WIN_POS_MOUSE);
297                 }
298         gtk_widget_show(gd->dialog);
299
300         return gd;
301 }
302
303 static gint filename_base_length(const gchar *name)
304 {
305         gint n;
306
307         if (!name) return 0;
308
309         n = strlen(name);
310
311         if (filter_name_exists(name))
312                 {
313                 const gchar *ext;
314
315                 ext = extension_from_path(name);
316                 if (ext) n -= strlen(ext);
317                 }
318
319         return n;
320 }
321
322
323 /*
324  *--------------------------------------------------------------------------
325  * Move and Copy routines
326  *--------------------------------------------------------------------------
327  */
328
329 /*
330  * Multi file move
331  */
332
333 static FileDataMult *file_data_multiple_new(GList *source_list, const gchar *dest, gint copy)
334 {
335         FileDataMult *fdm = g_new0(FileDataMult, 1);
336         fdm->confirm_all = FALSE;
337         fdm->confirmed = FALSE;
338         fdm->skip = FALSE;
339         fdm->source_list = source_list;
340         fdm->source_next = fdm->source_list;
341         fdm->dest_base = g_strdup(dest);
342         fdm->source = NULL;
343         fdm->dest = NULL;
344         fdm->copy = copy;
345         return fdm;
346 }
347
348 static void file_data_multiple_free(FileDataMult *fdm)
349 {
350         path_list_free(fdm->source_list);
351         g_free(fdm->dest_base);
352         g_free(fdm->dest);
353         g_free(fdm);
354 }
355
356 static void file_util_move_multiple(FileDataMult *fdm);
357
358 static void file_util_move_multiple_ok_cb(GenericDialog *gd, gpointer data)
359 {
360         FileDataMult *fdm = data;
361
362         fdm->confirmed = TRUE;
363
364         if (fdm->rename_auto)
365                 {
366                 gchar *buf;
367
368                 buf = unique_filename_simple(fdm->dest);
369                 if (buf)
370                         {
371                         g_free(fdm->dest);
372                         fdm->dest = buf;
373                         }
374                 else
375                         {
376                         /* unique failed? well, return to the overwrite prompt :( */
377                         fdm->confirmed = FALSE;
378                         }
379                 }
380         else if (fdm->rename)
381                 {
382                 const gchar *name;
383
384                 name = gtk_entry_get_text(GTK_ENTRY(fdm->rename_entry));
385                 if (strlen(name) == 0 ||
386                     strcmp(name, filename_from_path(fdm->source)) == 0)
387                         {
388                         fdm->confirmed = FALSE;
389                         }
390                 else
391                         {
392                         g_free(fdm->dest);
393                         fdm->dest = concat_dir_and_file(fdm->dest_base, name);
394                         fdm->confirmed = !isname(fdm->dest);
395                         }
396                 }
397
398         file_util_move_multiple(fdm);
399 }
400
401 static void file_util_move_multiple_all_cb(GenericDialog *gd, gpointer data)
402 {
403         FileDataMult *fdm = data;
404
405         fdm->confirm_all = TRUE;
406
407         if (fdm->rename_auto) fdm->rename_all = TRUE;
408
409         file_util_move_multiple(fdm);
410 }
411
412 static void file_util_move_multiple_skip_cb(GenericDialog *gd, gpointer data)
413 {
414         FileDataMult *fdm = data;
415
416         fdm->skip = TRUE;
417         fdm->confirmed = TRUE;
418
419         file_util_move_multiple(fdm);
420 }
421
422 static void file_util_move_multiple_skip_all_cb(GenericDialog *gd, gpointer data)
423 {
424         FileDataMult *fdm = data;
425
426         fdm->skip = TRUE;
427         fdm->confirm_all = TRUE;
428         file_util_move_multiple(fdm);
429 }
430
431 static void file_util_move_multiple_continue_cb(GenericDialog *gd, gpointer data)
432 {
433         FileDataMult *fdm = data;
434
435         fdm->confirmed = TRUE;
436         file_util_move_multiple(fdm);
437 }
438
439 static void file_util_move_multiple_cancel_cb(GenericDialog *gd, gpointer data)
440 {
441         FileDataMult *fdm = data;
442
443         file_data_multiple_free(fdm);
444 }
445
446 /* rename option */
447
448 static void file_util_move_multiple_rename_auto_cb(GtkWidget *widget, gpointer data)
449 {
450         GenericDialog *gd = data;
451         FileDataMult *fdm;
452
453         fdm = gd->data;
454
455         fdm->rename_auto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
456         gtk_widget_set_sensitive(fdm->rename_box, !fdm->rename_auto);
457         gtk_widget_set_sensitive(fdm->rename_entry, (!fdm->rename_auto && fdm->rename));
458
459         if (fdm->rename_auto)
460                 {
461                 gchar *preview;
462
463                 preview = unique_filename_simple(fdm->dest);
464                 if (preview) gtk_entry_set_text(GTK_ENTRY(fdm->rename_entry), filename_from_path(preview));
465                 g_free(preview);
466                 }
467
468         gtk_widget_set_sensitive(fdm->yes_all_button, (fdm->rename_auto || !fdm->rename));
469 }
470
471 static void file_util_move_multiple_rename_cb(GtkWidget *widget, gpointer data)
472 {
473         GenericDialog *gd = data;
474         FileDataMult *fdm;
475
476         fdm = gd->data;
477
478         fdm->rename = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
479         gtk_widget_set_sensitive(fdm->rename_entry, fdm->rename);
480         gtk_widget_set_sensitive(fdm->yes_all_button, !fdm->rename);
481
482         if (fdm->rename)
483                 {
484                 const gchar *name;
485
486                 gtk_widget_grab_focus(fdm->rename_entry);
487
488                 name = gtk_entry_get_text(GTK_ENTRY(fdm->rename_entry));
489                 gtk_editable_select_region(GTK_EDITABLE(fdm->rename_entry), 0, filename_base_length(name));
490                 }
491 }
492
493 static GenericDialog *file_util_move_multiple_confirm_dialog(FileDataMult *fdm)
494 {
495         GenericDialog *gd;
496         GtkWidget *hbox;
497
498         gd = file_util_gen_dlg(_("Overwrite file"), "GQview", "dlg_confirm",
499                                 NULL, TRUE,
500                                 file_util_move_multiple_cancel_cb, fdm);
501
502         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
503                                    _("Overwrite file?"),
504                                    _("Replace existing file with new file."));
505         pref_spacer(gd->vbox, 0);
506
507         generic_dialog_add_button(gd, GTK_STOCK_YES, _("_Overwrite"), file_util_move_multiple_ok_cb, TRUE);
508         fdm->yes_all_button = generic_dialog_add_button(gd, NULL, _("Overwrite _all"),
509                                                         file_util_move_multiple_all_cb, FALSE);
510         generic_dialog_add_button(gd, GTK_STOCK_GOTO_LAST, _("S_kip all"), file_util_move_multiple_skip_all_cb, FALSE);
511         generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("_Skip"), file_util_move_multiple_skip_cb, FALSE);
512         generic_dialog_add_image(gd, fdm->dest, _("Existing file"), fdm->source, _("New file"), TRUE);
513
514         /* rename option */
515
516         fdm->rename = FALSE;
517         fdm->rename_all = FALSE;
518         fdm->rename_auto = FALSE;
519
520         hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
521
522         fdm->rename_auto_box = gtk_check_button_new_with_label(_("Auto rename"));
523         g_signal_connect(G_OBJECT(fdm->rename_auto_box), "clicked",
524                          G_CALLBACK(file_util_move_multiple_rename_auto_cb), gd);
525         gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_auto_box, FALSE, FALSE, 0);
526         gtk_widget_show(fdm->rename_auto_box);
527
528         hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
529
530         fdm->rename_box = gtk_check_button_new_with_label(_("Rename"));
531         g_signal_connect(G_OBJECT(fdm->rename_box), "clicked",
532                          G_CALLBACK(file_util_move_multiple_rename_cb), gd);
533         gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_box, FALSE, FALSE, 0);
534         gtk_widget_show(fdm->rename_box);
535
536         fdm->rename_entry = gtk_entry_new();
537         gtk_entry_set_text(GTK_ENTRY(fdm->rename_entry), filename_from_path(fdm->dest));
538         gtk_widget_set_sensitive(fdm->rename_entry, FALSE);
539         gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_entry, TRUE, TRUE, 0);
540         gtk_widget_show(fdm->rename_entry);
541
542         return gd;
543 }
544
545 static void file_util_move_multiple(FileDataMult *fdm)
546 {
547         while (fdm->dest || fdm->source_next)
548                 {
549                 gint success = FALSE;
550                 gint skip_file = FALSE;
551
552                 if (!fdm->dest)
553                         {
554                         GList *work = fdm->source_next;
555                         fdm->source = work->data;
556                         fdm->dest = concat_dir_and_file(fdm->dest_base, filename_from_path(fdm->source));
557                         fdm->source_next = work->next;
558                         fdm->confirmed = FALSE;
559                         }
560
561                 if (fdm->dest && fdm->source && strcmp(fdm->dest, fdm->source) == 0)
562                         {
563                         if (!fdm->confirmed)
564                                 {
565                                 GenericDialog *gd;
566                                 const gchar *title;
567                                 gchar *text;
568
569                                 if (fdm->copy)
570                                         {
571                                         title = _("Source to copy matches destination");
572                                         text = g_strdup_printf(_("Unable to copy file:\n%s\nto itself."), fdm->dest);
573                                         }
574                                 else
575                                         {
576                                         title = _("Source to move matches destination");
577                                         text = g_strdup_printf(_("Unable to move file:\n%s\nto itself."), fdm->dest);
578                                         }
579
580                                 gd = file_util_gen_dlg(title, "GQview", "dlg_confirm",
581                                                         NULL, TRUE,
582                                                         file_util_move_multiple_cancel_cb, fdm);
583                                 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING, title, text);
584                                 g_free(text);
585                                 generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
586                                                          file_util_move_multiple_continue_cb, TRUE);
587
588                                 gtk_widget_show(gd->dialog);
589                                 return;
590                                 }
591                         skip_file = TRUE;
592                         }
593                 else if (isfile(fdm->dest))
594                         {
595                         if (!fdm->confirmed && !fdm->confirm_all)
596                                 {
597                                 GenericDialog *gd;
598
599                                 gd = file_util_move_multiple_confirm_dialog(fdm);
600                                 gtk_widget_show(gd->dialog);
601                                 return;
602                                 }
603                         if (fdm->skip) skip_file = TRUE;
604                         }
605
606                 if (skip_file)
607                         {
608                         success = TRUE;
609                         if (!fdm->confirm_all) fdm->skip = FALSE;
610                         }
611                 else
612                         {
613                         gint try = TRUE;
614
615                         if (fdm->confirm_all && fdm->rename_all && isfile(fdm->dest))
616                                 {
617                                 gchar *buf;
618                                 buf = unique_filename_simple(fdm->dest);
619                                 if (buf)
620                                         {
621                                         g_free(fdm->dest);
622                                         fdm->dest = buf;
623                                         }
624                                 else
625                                         {
626                                         try = FALSE;
627                                         }
628                                 }
629                         if (try)
630                                 {
631                                 if (fdm->copy)
632                                         {
633                                         if (copy_file(fdm->source, fdm->dest))
634                                                 {
635                                                 success = TRUE;
636                                                 file_maint_copied(fdm->source, fdm->dest);
637                                                 }
638                                         }
639                                 else
640                                         {
641                                         if (move_file(fdm->source, fdm->dest))
642                                                 {
643                                                 success = TRUE;
644                                                 file_maint_moved(fdm->source, fdm->dest, fdm->source_list);
645                                                 }
646                                         }
647                                 }
648                         }
649
650                 if (!success)
651                         {
652                         GenericDialog *gd;
653                         const gchar *title;
654                         gchar *text;
655
656                         if (fdm->copy)
657                                 {
658                                 title = _("Error copying file");
659                                 text = g_strdup_printf(_("Unable to copy file:\n%s\nto:\n%s\nduring multiple file copy."), fdm->source, fdm->dest);
660                                 }
661                         else
662                                 {
663                                 title = _("Error moving file");
664                                 text = g_strdup_printf(_("Unable to move file:\n%s\nto:\n%s\nduring multiple file move."), fdm->source, fdm->dest);
665                                 }
666                         gd = file_util_gen_dlg(title, "GQview", "dlg_confirm",
667                                                 NULL, TRUE,
668                                                 file_util_move_multiple_cancel_cb, fdm);
669                         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING, title, text);
670                         g_free(text);
671
672                         generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
673                                                   file_util_move_multiple_continue_cb, TRUE);
674                         gtk_widget_show(gd->dialog);
675                         }
676
677                 g_free(fdm->dest);
678                 fdm->dest = NULL;
679
680                 if (!success) return;
681                 }
682
683         file_data_multiple_free(fdm);
684 }
685
686 /*
687  * Single file move
688  */
689
690 static FileDataSingle *file_data_single_new(const gchar *source, const gchar *dest, gint copy)
691 {
692         FileDataSingle *fds = g_new0(FileDataSingle, 1);
693         fds->confirmed = FALSE;
694         fds->source = g_strdup(source);
695         fds->dest = g_strdup(dest);
696         fds->copy = copy;
697         return fds;
698 }
699
700 static void file_data_single_free(FileDataSingle *fds)
701 {
702         g_free(fds->source);
703         g_free(fds->dest);
704         g_free(fds);
705 }
706
707 static void file_util_move_single(FileDataSingle *fds);
708
709 static void file_util_move_single_ok_cb(GenericDialog *gd, gpointer data)
710 {
711         FileDataSingle *fds = data;
712
713         fds->confirmed = TRUE;
714
715         if (fds->rename_auto)
716                 {
717                 gchar *buf;
718
719                 buf = unique_filename_simple(fds->dest);
720                 if (buf)
721                         {
722                         g_free(fds->dest);
723                         fds->dest = buf;
724                         }
725                 else
726                         {
727                         /* unique failed? well, return to the overwrite prompt :( */
728                         fds->confirmed = FALSE;
729                         }
730                 }
731         else if (fds->rename)
732                 {
733                 const gchar *name;
734
735                 name = gtk_entry_get_text(GTK_ENTRY(fds->rename_entry));
736                 if (strlen(name) == 0 ||
737                     strcmp(name, filename_from_path(fds->source)) == 0)
738                         {
739                         fds->confirmed = FALSE;
740                         }
741                 else
742                         {
743                         gchar *base;
744
745                         base = remove_level_from_path(fds->dest);
746                         g_free(fds->dest);
747                         fds->dest = concat_dir_and_file(base, name);
748                         fds->confirmed = !isname(fds->dest);
749
750                         g_free(base);
751                         }
752                 }
753
754         file_util_move_single(fds);
755 }
756
757 static void file_util_move_single_cancel_cb(GenericDialog *gd, gpointer data)
758 {
759         FileDataSingle *fds = data;
760
761         file_data_single_free(fds);
762 }
763
764 static void file_util_move_single_rename_auto_cb(GtkWidget *widget, gpointer data)
765 {
766         GenericDialog *gd = data;
767         FileDataSingle *fds;
768
769         fds = gd->data;
770
771         fds->rename_auto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
772         gtk_widget_set_sensitive(fds->rename_box, !fds->rename_auto);
773         gtk_widget_set_sensitive(fds->rename_entry, (!fds->rename_auto && fds->rename));
774
775         if (fds->rename_auto)
776                 {
777                 gchar *preview;
778
779                 preview = unique_filename_simple(fds->dest);
780                 if (preview) gtk_entry_set_text(GTK_ENTRY(fds->rename_entry), filename_from_path(preview));
781                 g_free(preview);
782                 }
783 }
784
785 static void file_util_move_single_rename_cb(GtkWidget *widget, gpointer data)
786 {
787         GenericDialog *gd = data;
788         FileDataSingle *fds;
789
790         fds = gd->data;
791
792         fds->rename = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
793         gtk_widget_set_sensitive(fds->rename_entry, fds->rename);
794
795         if (fds->rename)
796                 {
797                 const gchar *name;
798
799                 gtk_widget_grab_focus(fds->rename_entry);
800
801                 name = gtk_entry_get_text(GTK_ENTRY(fds->rename_entry));
802                 gtk_editable_select_region(GTK_EDITABLE(fds->rename_entry), 0, filename_base_length(name));
803                 }
804 }
805
806 static void file_util_move_single(FileDataSingle *fds)
807 {
808         if (fds->dest && fds->source && strcmp(fds->dest, fds->source) == 0)
809                 {
810                 file_util_warning_dialog(_("Source matches destination"),
811                                          _("Source and destination are the same, operation cancelled."),
812                                          GTK_STOCK_DIALOG_INFO, NULL);
813                 }
814         else if (isfile(fds->dest) && !fds->confirmed)
815                 {
816                 GenericDialog *gd;
817                 GtkWidget *hbox;
818
819                 gd = file_util_gen_dlg(_("Overwrite file"), "GQview", "dlg_confirm",
820                                         NULL, TRUE,
821                                         file_util_move_single_cancel_cb, fds);
822
823                 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
824                                            _("Overwrite file?"),
825                                            _("Replace existing file with new file."));
826                 pref_spacer(gd->vbox, 0);
827
828                 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_move_single_ok_cb, TRUE);
829                 generic_dialog_add_image(gd, fds->dest, _("Existing file"), fds->source, _("New file"), TRUE);
830
831                 /* rename option */
832
833                 fds->rename = FALSE;
834                 fds->rename_auto = FALSE;
835
836                 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
837
838                 fds->rename_auto_box = gtk_check_button_new_with_label(_("Auto rename"));
839                 g_signal_connect(G_OBJECT(fds->rename_auto_box), "clicked",
840                                  G_CALLBACK(file_util_move_single_rename_auto_cb), gd);
841                 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_auto_box, FALSE, FALSE, 0);
842                 gtk_widget_show(fds->rename_auto_box);
843
844                 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
845
846                 fds->rename_box = gtk_check_button_new_with_label(_("Rename"));
847                 g_signal_connect(G_OBJECT(fds->rename_box), "clicked",
848                                  G_CALLBACK(file_util_move_single_rename_cb), gd);
849                 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_box, FALSE, FALSE, 0);
850                 gtk_widget_show(fds->rename_box);
851
852                 fds->rename_entry = gtk_entry_new();
853                 gtk_entry_set_text(GTK_ENTRY(fds->rename_entry), filename_from_path(fds->dest));
854                 gtk_widget_set_sensitive(fds->rename_entry, FALSE);
855                 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_entry, TRUE, TRUE, 0);
856                 gtk_widget_show(fds->rename_entry);
857
858                 gtk_widget_show(gd->dialog);
859                 return;
860                 }
861         else
862                 {
863                 gint success = FALSE;
864                 if (fds->copy)
865                         {
866                         if (copy_file(fds->source, fds->dest))
867                                 {
868                                 success = TRUE;
869                                 file_maint_copied(fds->source, fds->dest);
870                                 }
871                         }
872                 else
873                         {
874                         if (move_file(fds->source, fds->dest))
875                                 {
876                                 success = TRUE;
877                                 file_maint_moved(fds->source, fds->dest, NULL);
878                                 }
879                         }
880                 if (!success)
881                         {
882                         gchar *title;
883                         gchar *text;
884                         if (fds->copy)
885                                 {
886                                 title = _("Error copying file");
887                                 text = g_strdup_printf(_("Unable to copy file:\n%s\nto:\n%s"), fds->source, fds->dest);
888                                 }
889                         else
890                                 {
891                                 title = _("Error moving file");
892                                 text = g_strdup_printf(_("Unable to move file:\n%s\nto:\n%s"), fds->source, fds->dest);
893                                 }
894                         file_util_warning_dialog(title, text, GTK_STOCK_DIALOG_ERROR, NULL);
895                         g_free(text);
896                         }
897                 }
898
899         file_data_single_free(fds);
900 }
901
902 /*
903  * file move dialog
904  */
905
906 static void file_util_move_do(FileDialog *fd)
907 {
908         file_dialog_sync_history(fd, TRUE);
909
910         if (fd->multiple_files)
911                 {
912                 file_util_move_multiple(file_data_multiple_new(fd->source_list, fd->dest_path, fd->type));
913                 fd->source_list = NULL;
914                 }
915         else
916                 {
917                 if (isdir(fd->dest_path))
918                         {
919                         gchar *buf = concat_dir_and_file(fd->dest_path, filename_from_path(fd->source_path));
920                         gtk_entry_set_text(GTK_ENTRY(fd->entry), buf);
921                         g_free(buf);
922                         }
923                 file_util_move_single(file_data_single_new(fd->source_path, fd->dest_path, fd->type));
924                 }
925
926         file_dialog_close(fd);
927 }
928
929 static void file_util_move_check(FileDialog *fd)
930 {
931         if (fd->dest_path && strcmp(fd->dest_path, "~") == 0)
932                 {
933                 gtk_entry_set_text(GTK_ENTRY(fd->entry), homedir());
934                 }
935
936         if (fd->multiple_files && !isdir(fd->dest_path))
937                 {
938                 if (isfile(fd->dest_path))
939                         {
940                         file_util_warning_dialog(_("Invalid destination"),
941                                                  _("When operating with multiple files, please select\na folder, not a file."),
942                                                  GTK_STOCK_DIALOG_INFO, NULL);
943                         }
944                 else
945                         file_util_warning_dialog(_("Invalid folder"),
946                                                  _("Please select an existing folder."),
947                                                  GTK_STOCK_DIALOG_INFO, NULL);
948                 return;
949                 }
950
951         if (!fd->dest_path || fd->dest_path[0] != '/')
952                 {
953                 if (fd->source_path)
954                         {
955                         gchar *base;
956                         gchar *path;
957
958                         base = remove_level_from_path(fd->source_path);
959                         path = concat_dir_and_file(base, fd->dest_path);
960
961                         gtk_entry_set_text(GTK_ENTRY(fd->entry), path);
962
963                         g_free(path);
964                         g_free(base);
965                         }
966                 return;
967                 }
968
969         file_util_move_do(fd);
970 }
971
972 static void file_util_move_cb(FileDialog *fd, gpointer data)
973 {
974         file_util_move_check(fd);
975 }
976
977 static void file_util_move_cancel_cb(FileDialog *fd, gpointer data)
978 {
979         file_dialog_close(fd);
980 }
981
982 static void real_file_util_move(const gchar *source_path, GList *source_list,
983                                 const gchar *dest_path, gint copy, GtkWidget *parent)
984 {
985         FileDialog *fd;
986         GtkWidget *label;
987         gchar *path = NULL;
988         gint multiple;
989         const gchar *text;
990         const gchar *title;
991         const gchar *op_text;
992         const gchar *stock_id;
993
994         if (!source_path && !source_list) return;
995
996         if (source_path)
997                 {
998                 path = g_strdup(source_path);
999                 multiple = FALSE;
1000                 }
1001         else if (source_list->next)
1002                 {
1003                 multiple = TRUE;
1004                 }
1005         else
1006                 {
1007                 path = g_strdup(source_list->data);
1008                 path_list_free(source_list);
1009                 source_list = NULL;
1010                 multiple = FALSE;
1011                 }
1012
1013         if (copy)
1014                 {
1015                 title = _("Copy - GQview");
1016                 op_text = _("_Copy");
1017                 if (path)
1018                         {
1019                         text = _("Copy file");
1020                         }
1021                 else
1022                         {
1023                         text = _("Copy multiple files");
1024                         }
1025                 stock_id = GTK_STOCK_COPY;
1026                 }
1027         else
1028                 {
1029                 title = _("Move - GQview");
1030                 op_text = _("_Move");
1031                 if (path)
1032                         {
1033                         text = _("Move file");
1034                         }
1035                 else
1036                         {
1037                         text = _("Move multiple files");
1038                         }
1039                 stock_id = GTK_STOCK_OK;
1040                 }
1041
1042         fd = file_util_file_dlg(title, "GQview", "dlg_copymove", parent,
1043                                 file_util_move_cancel_cb, NULL);
1044         generic_dialog_add_message(GENERIC_DIALOG(fd), NULL, text, NULL);
1045
1046         if (path)
1047                 {
1048                 GtkWidget *box;
1049
1050                 box = pref_box_new(GENERIC_DIALOG(fd)->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1051                 pref_label_new(box, _("File name:"));
1052                 pref_label_new(box, filename_from_path(path));
1053                 }
1054
1055         label = pref_label_new(GENERIC_DIALOG(fd)->vbox, _("Choose the destination folder."));
1056         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1057         pref_spacer(GENERIC_DIALOG(fd)->vbox, 0);
1058
1059         file_dialog_add_button(fd, stock_id, op_text, file_util_move_cb, TRUE);
1060
1061         file_dialog_add_path_widgets(fd, NULL, dest_path, "move_copy", NULL, NULL);
1062
1063         fd->type = copy;
1064         fd->source_path = path;
1065         fd->source_list = source_list;
1066         fd->multiple_files = multiple;
1067
1068         gtk_widget_show(GENERIC_DIALOG(fd)->dialog);
1069 }
1070
1071 void file_util_move(const gchar *source_path, GList *source_list, const gchar *dest_path, GtkWidget *parent)
1072 {
1073         real_file_util_move(source_path, source_list, dest_path, FALSE, parent);
1074 }
1075
1076 void file_util_copy(const gchar *source_path, GList *source_list, const gchar *dest_path, GtkWidget *parent)
1077 {
1078         real_file_util_move(source_path, source_list, dest_path, TRUE, parent);
1079 }
1080
1081 void file_util_move_simple(GList *list, const gchar *dest_path)
1082 {
1083         if (!list) return;
1084         if (!dest_path)
1085                 {
1086                 path_list_free(list);
1087                 return;
1088                 }
1089
1090         if (!list->next)
1091                 {
1092                 const gchar *source;
1093                 gchar *dest;
1094
1095                 source = list->data;
1096                 dest = concat_dir_and_file(dest_path, filename_from_path(source));
1097
1098                 file_util_move_single(file_data_single_new(source, dest, FALSE));
1099                 g_free(dest);
1100                 path_list_free(list);
1101                 return;
1102                 }
1103
1104         file_util_move_multiple(file_data_multiple_new(list, dest_path, FALSE));
1105 }
1106
1107 void file_util_copy_simple(GList *list, const gchar *dest_path)
1108 {
1109         if (!list) return;
1110         if (!dest_path)
1111                 {
1112                 path_list_free(list);
1113                 return;
1114                 }
1115
1116         if (!list->next)
1117                 {
1118                 const gchar *source;
1119                 gchar *dest;
1120
1121                 source = list->data;
1122                 dest = concat_dir_and_file(dest_path, filename_from_path(source));
1123
1124                 file_util_move_single(file_data_single_new(source, dest, TRUE));
1125                 g_free(dest);
1126                 path_list_free(list);
1127                 return;
1128                 }
1129
1130         file_util_move_multiple(file_data_multiple_new(list, dest_path, TRUE));
1131 }
1132
1133 /*
1134  *--------------------------------------------------------------------------
1135  * Safe Delete
1136  *--------------------------------------------------------------------------
1137  */
1138
1139 static gint file_util_safe_number(gint64 free_space)
1140 {
1141         gint n = 0;
1142         gint64 total = 0;
1143         GList *list;
1144         GList *work;
1145         gint sorted = FALSE;
1146         gint warned = FALSE;
1147
1148         if (!filelist_read(safe_delete_path, &list, NULL)) return 0;
1149
1150         work = list;
1151         while (work)
1152                 {
1153                 FileData *fd;
1154                 gint v;
1155                
1156                 fd = work->data;
1157                 work = work->next;
1158
1159                 v = (gint)strtol(fd->name, NULL, 10);
1160                 if (v >= n) n = v + 1;
1161
1162                 total += fd->size;
1163                 }
1164
1165         while (list &&
1166                (free_space < 0 || total + free_space > (gint64)safe_delete_size * 1048576) )
1167                 {
1168                 FileData *fd;
1169
1170                 if (!sorted)
1171                         {
1172                         list = filelist_sort(list, SORT_NAME, TRUE);
1173                         sorted = TRUE;
1174                         }
1175
1176                 fd = list->data;
1177                 list = g_list_remove(list, fd);
1178
1179                 if (debug) printf("expunging from trash for space: %s\n", fd->name);
1180                 if (!unlink_file(fd->path) && !warned)
1181                         {
1182                         file_util_warning_dialog(_("Delete failed"),
1183                                                  _("Unable to remove old file from trash folder"),
1184                                                  GTK_STOCK_DIALOG_WARNING, NULL);
1185                         warned = TRUE;
1186                         }
1187                 total -= fd->size;
1188                 file_data_free(fd);
1189                 }
1190
1191         filelist_free(list);
1192
1193         return n;
1194 }
1195
1196 void file_util_trash_clear(void)
1197 {
1198         file_util_safe_number(-1);
1199 }
1200
1201 static gchar *file_util_safe_dest(const gchar *path)
1202 {
1203         gint n;
1204
1205         n = file_util_safe_number(filesize(path));
1206         return g_strdup_printf("%s/%06d_%s", safe_delete_path, n, filename_from_path(path));
1207 }
1208
1209 static void file_util_safe_del_toggle_cb(GtkWidget *button, gpointer data)
1210 {
1211         safe_delete_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1212 }
1213
1214 static void file_util_safe_del_close_cb(GtkWidget *dialog, gpointer data)
1215 {
1216         GenericDialog **gd = data;
1217
1218         *gd = NULL;
1219 }
1220
1221 static gint file_util_unlink(const gchar *path)
1222 {
1223         static GenericDialog *gd = NULL;
1224         gchar *result = NULL;
1225         gint success = TRUE;
1226
1227         if (!isfile(path)) return FALSE;
1228
1229         if (!safe_delete_enable)
1230                 {
1231                 return unlink_file(path);
1232                 }
1233
1234         if (!isdir(safe_delete_path))
1235                 {
1236                 if (debug) printf("creating trash: %s\n", safe_delete_path);
1237                 if (!safe_delete_path || !mkdir_utf8(safe_delete_path, 0755))
1238                         {
1239                         result = _("Could not create folder");
1240                         success = FALSE;
1241                         }
1242                 }
1243
1244         if (success)
1245                 {
1246                 gchar *dest;
1247
1248                 dest = file_util_safe_dest(path);
1249                 if (dest)
1250                         {
1251                         if (debug) printf("safe deleting %s to %s\n", path, dest);
1252                         success = move_file(path, dest);
1253                         }
1254                 else
1255                         {
1256                         success = FALSE;
1257                         }
1258
1259                 if (!success && !access_file(path, W_OK))
1260                         {
1261                         result = _("Permission denied");
1262                         }
1263                 g_free(dest);
1264                 }
1265
1266         if (result && !gd)
1267                 {
1268                 GtkWidget *button;
1269                 gchar *buf;
1270
1271                 buf = g_strdup_printf(_("Unable to access or create the trash folder.\n\"%s\""), safe_delete_path);
1272                 gd = file_util_warning_dialog(result, buf, GTK_STOCK_DIALOG_WARNING, NULL);
1273                 g_free(buf);
1274
1275                 button = gtk_check_button_new_with_label(_("Turn off safe delete"));
1276                 g_signal_connect(G_OBJECT(button), "toggled",
1277                                  G_CALLBACK(file_util_safe_del_toggle_cb), NULL);
1278                 gtk_box_pack_start(GTK_BOX(gd->vbox), button, FALSE, FALSE, 0);
1279                 gtk_widget_show(button);
1280
1281                 g_signal_connect(G_OBJECT(gd->dialog), "destroy",
1282                                  G_CALLBACK(file_util_safe_del_close_cb), &gd);
1283                 }
1284
1285         return success;
1286 }
1287
1288 static void box_append_safe_delete_status(GenericDialog *gd)
1289 {
1290         GtkWidget *label;
1291         gchar *buf;
1292
1293         buf = g_strdup_printf(_("Safe delete: %s"), (safe_delete_enable) ? _("on") : _("off"));
1294         label = pref_label_new(gd->vbox, buf);
1295         g_free(buf);
1296
1297         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1298         gtk_widget_set_sensitive(label, FALSE);
1299 }
1300
1301 /*
1302  *--------------------------------------------------------------------------
1303  * Delete routines
1304  *--------------------------------------------------------------------------
1305  */
1306
1307 /*
1308  * delete multiple files
1309  */
1310
1311 static void file_util_delete_multiple_ok_cb(GenericDialog *gd, gpointer data);
1312 static void file_util_delete_multiple_cancel_cb(GenericDialog *gd, gpointer data);
1313
1314 static void file_util_delete_multiple_ok_cb(GenericDialog *gd, gpointer data)
1315 {
1316         GList *source_list = data;
1317
1318         while (source_list)
1319                 {
1320                 gchar *path = source_list->data;
1321
1322                 source_list = g_list_remove(source_list, path);
1323
1324                 if (!file_util_unlink(path))
1325                         {
1326                         if (source_list)
1327                                 {
1328                                 GenericDialog *d;
1329                                 gchar *text;
1330
1331                                 d = file_util_gen_dlg(_("Delete failed"), "GQview", "dlg_confirm",
1332                                                       NULL, TRUE,
1333                                                       file_util_delete_multiple_cancel_cb, source_list);
1334
1335                                 text = g_strdup_printf(_("Unable to delete file:\n %s\n Continue multiple delete operation?"), path);
1336                                 generic_dialog_add_message(d, GTK_STOCK_DIALOG_WARNING, NULL, text);
1337                                 g_free(text);
1338
1339                                 generic_dialog_add_button(d, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
1340                                                           file_util_delete_multiple_ok_cb, TRUE);
1341                                 gtk_widget_show(d->dialog);
1342                                 }
1343                         else
1344                                 {
1345                                 gchar *text;
1346                                 
1347                                 text = g_strdup_printf(_("Unable to delete file:\n%s"), path);
1348                                 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1349                                 g_free(text);
1350                                 }
1351                         g_free(path);
1352                         return;
1353                         }
1354                 else
1355                         {
1356                         file_maint_removed(path, source_list);
1357                         }
1358                 g_free(path);
1359                 }
1360 }
1361
1362 static void file_util_delete_multiple_cancel_cb(GenericDialog *gd, gpointer data)
1363 {
1364         GList *source_list = data;
1365
1366         path_list_free(source_list);
1367 }
1368
1369 static void file_util_delete_multiple_review_skip(GenericDialog *gd, gint next)
1370 {
1371         GtkWidget *button_back;
1372         GtkWidget *button_next;
1373         GtkWidget *button_label;
1374         GList *list;
1375         GList *list_point;
1376         const gchar *path;
1377         gchar *buf;
1378
1379         list = gd->data;
1380         button_back = g_object_get_data(G_OBJECT(gd->dialog), "button_back");
1381         button_next = g_object_get_data(G_OBJECT(gd->dialog), "button_next");
1382         button_label = g_object_get_data(G_OBJECT(gd->dialog), "button_label");
1383         list_point = g_object_get_data(G_OBJECT(gd->dialog), "list_point");
1384
1385         if (!list || !button_label) return;
1386
1387         if (list_point)
1388                 {
1389                 if (next)
1390                         {
1391                         if (list_point->next) list_point = list_point->next;
1392                         }
1393                 else
1394                         {
1395                         if (list_point->prev) list_point = list_point->prev;
1396                         }
1397                 }
1398         else
1399                 {
1400                 list_point = list;
1401                 }
1402
1403         if (!list_point) return;
1404
1405         path = list_point->data;
1406         buf = g_strdup_printf(_("File %d of %d"),
1407                               g_list_index(list, (gpointer)path) + 1,
1408                               g_list_length(list));
1409         gtk_label_set_text(GTK_LABEL(button_label), buf);
1410         g_free(buf);
1411
1412         gtk_widget_set_sensitive(button_back, (list_point->prev != NULL) );
1413         gtk_widget_set_sensitive(button_next, (list_point->next != NULL) );
1414
1415         generic_dialog_image_set(gd, path);
1416
1417         g_object_set_data(G_OBJECT(gd->dialog), "list_point", list_point);
1418 }
1419
1420 static void file_util_delete_multiple_review_back(GtkWidget *button, gpointer data)
1421 {
1422         GenericDialog *gd = data;
1423
1424         file_util_delete_multiple_review_skip(gd, FALSE);
1425 }
1426
1427 static void file_util_delete_multiple_review_next(GtkWidget *button, gpointer data)
1428 {
1429         GenericDialog *gd = data;
1430
1431         file_util_delete_multiple_review_skip(gd, TRUE);
1432 }
1433
1434 static void file_util_delete_multiple_review_button_cb(ImageWindow *imd, gint button, guint32 time,
1435                                                        gdouble x, gdouble y, guint state, gpointer data)
1436 {
1437         if (button == 1)
1438                 {
1439                 file_util_delete_multiple_review_next(NULL, data);
1440                 }
1441         else if (button == 2 || button == 3)
1442                 {
1443                 file_util_delete_multiple_review_back(NULL, data);
1444                 }
1445 }
1446
1447 static void file_util_delete_multiple_review_scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
1448                                                        gdouble x, gdouble y, guint state, gpointer data)
1449 {
1450         if (direction == GDK_SCROLL_UP)
1451                 {
1452                 file_util_delete_multiple_review_back(NULL, data);
1453                 }
1454         else if (direction == GDK_SCROLL_DOWN)
1455                 {
1456                 file_util_delete_multiple_review_next(NULL, data);
1457                 }
1458 }
1459
1460 static void file_util_delete_multiple(GList *source_list, GtkWidget *parent)
1461 {
1462         if (!confirm_delete)
1463                 {
1464                 file_util_delete_multiple_ok_cb(NULL, source_list);
1465                 }
1466         else
1467                 {
1468                 GenericDialog *gd;
1469                 GtkWidget *hbox;
1470                 GtkWidget *button;
1471                 GtkWidget *label;
1472                 ImageWindow *imd;
1473                 gchar *buf;
1474
1475                 gd = file_util_gen_dlg(_("Delete files - GQview"),
1476                                         "GQview", "dlg_confirm", parent, TRUE,
1477                                         file_util_delete_multiple_cancel_cb, source_list);
1478
1479                 generic_dialog_add_message(gd, NULL, _("Delete multiple files"), NULL);
1480
1481                 generic_dialog_add_image(gd, NULL, NULL, NULL, NULL, TRUE);
1482                 imd = g_object_get_data(G_OBJECT(gd->dialog), "img_image");
1483                 image_set_button_func(imd, file_util_delete_multiple_review_button_cb, gd);
1484                 image_set_scroll_func(imd, file_util_delete_multiple_review_scroll_cb, gd);
1485
1486                 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
1487
1488                 button = pref_button_new(hbox, GTK_STOCK_GO_BACK, NULL, TRUE,
1489                                          G_CALLBACK(file_util_delete_multiple_review_back), gd);
1490                 gtk_widget_set_sensitive(button, FALSE);
1491                 g_object_set_data(G_OBJECT(gd->dialog), "button_back", button);
1492
1493                 button = pref_button_new(hbox, GTK_STOCK_GO_FORWARD, NULL, TRUE,
1494                                          G_CALLBACK(file_util_delete_multiple_review_next), gd);
1495                 g_object_set_data(G_OBJECT(gd->dialog), "button_next", button);
1496
1497                 buf = g_strdup_printf(_("Review %d files"), g_list_length(source_list) );
1498                 label = pref_label_new(hbox, buf);
1499                 g_free(buf);
1500                 g_object_set_data(G_OBJECT(gd->dialog), "button_label", label);
1501
1502                 box_append_safe_delete_status(gd);
1503
1504                 generic_dialog_add_button(gd, GTK_STOCK_DELETE, NULL, file_util_delete_multiple_ok_cb, TRUE);
1505
1506                 gtk_widget_show(gd->dialog);
1507                 }
1508 }
1509
1510 /*
1511  * delete single file
1512  */
1513
1514 static void file_util_delete_ok_cb(GenericDialog *gd, gpointer data)
1515 {
1516         gchar *path = data;
1517
1518         if (!file_util_unlink(path))
1519                 {
1520                 gchar *text = g_strdup_printf(_("Unable to delete file:\n%s"), path);
1521                 file_util_warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1522                 g_free(text);
1523                 }
1524         else
1525                 {
1526                 file_maint_removed(path, NULL);
1527                 }
1528
1529         g_free(path);
1530 }
1531
1532 static void file_util_delete_cancel_cb(GenericDialog *gd, gpointer data)
1533 {
1534         gchar *path = data;
1535
1536         g_free(path);
1537 }
1538
1539 static void file_util_delete_single(const gchar *path, GtkWidget *parent)
1540 {
1541         gchar *buf = g_strdup(path);
1542
1543         if (!confirm_delete)
1544                 {
1545                 file_util_delete_ok_cb(NULL, buf);
1546                 }
1547         else
1548                 {
1549                 GenericDialog *gd;
1550                 GtkWidget *table;
1551                 gchar *base;
1552
1553                 gd = file_util_gen_dlg(_("Delete file - GQview"), "GQview", "dlg_confirm",
1554                                         parent, TRUE,
1555                                         file_util_delete_cancel_cb, buf);
1556
1557                 generic_dialog_add_message(gd, NULL, _("Delete file?"), NULL);
1558
1559                 table = pref_table_new(gd->vbox, 2, 2, FALSE, FALSE);
1560
1561                 pref_table_label(table, 0, 0, _("File name:"), 1.0);
1562                 pref_table_label(table, 1, 0, filename_from_path(path), 0.0);
1563
1564                 pref_table_label(table, 0, 1, _("Location:"), 1.0);
1565
1566                 base = remove_level_from_path(path);
1567                 pref_table_label(table, 1, 1, base, 0.0);
1568                 g_free(base);
1569
1570                 generic_dialog_add_image(gd, path, NULL, NULL, NULL, FALSE);
1571
1572                 box_append_safe_delete_status(gd);
1573
1574                 generic_dialog_add_button(gd, GTK_STOCK_DELETE, NULL, file_util_delete_ok_cb, TRUE);
1575
1576                 gtk_widget_show(gd->dialog);
1577                 }
1578 }
1579
1580 void file_util_delete(const gchar *source_path, GList *source_list, GtkWidget *parent)
1581 {
1582         if (!source_path && !source_list) return;
1583
1584         if (source_path)
1585                 {
1586                 file_util_delete_single(source_path, parent);
1587                 }
1588         else if (!source_list->next)
1589                 {
1590                 file_util_delete_single(source_list->data, parent);
1591                 path_list_free(source_list);
1592                 }
1593         else
1594                 {
1595                 file_util_delete_multiple(source_list, parent);
1596                 }
1597 }
1598
1599 /*
1600  *--------------------------------------------------------------------------
1601  * Rename routines
1602  *--------------------------------------------------------------------------
1603  */
1604
1605 /*
1606  * rename multiple files
1607  */
1608
1609 enum {
1610         RENAME_COLUMN_PATH = 0,
1611         RENAME_COLUMN_NAME,
1612         RENAME_COLUMN_PREVIEW,
1613         RENAME_COLUMN_COUNT
1614 };
1615
1616 typedef struct _RenameDataMult RenameDataMult;
1617 struct _RenameDataMult
1618 {
1619         FileDialog *fd;
1620
1621         gint rename_auto;
1622
1623         GtkWidget *listview;
1624         GtkWidget *button_auto;
1625
1626         GtkWidget *rename_box;
1627         GtkWidget *rename_label;
1628         GtkWidget *rename_entry;
1629
1630         GtkWidget *auto_box;
1631         GtkWidget *auto_entry_front;
1632         GtkWidget *auto_spin_start;
1633         GtkWidget *auto_spin_pad;
1634         GtkWidget *auto_entry_end;
1635
1636         ImageWindow *imd;
1637
1638         gint update_idle_id;
1639 };
1640
1641 static void file_util_rename_multiple(RenameDataMult *rd);
1642
1643 static void file_util_rename_multiple_ok_cb(GenericDialog *gd, gpointer data)
1644 {
1645         RenameDataMult *rd = data;
1646         GtkWidget *dialog;
1647
1648         dialog = GENERIC_DIALOG(rd->fd)->dialog;
1649         if (!GTK_WIDGET_VISIBLE(dialog)) gtk_widget_show(dialog);
1650
1651         rd->fd->type = TRUE;
1652         file_util_rename_multiple(rd);
1653 }
1654
1655 static void file_util_rename_multiple_cancel_cb(GenericDialog *gd, gpointer data)
1656 {
1657         RenameDataMult *rd = data;
1658         GtkWidget *dialog;
1659
1660         dialog = GENERIC_DIALOG(rd->fd)->dialog;
1661         if (!GTK_WIDGET_VISIBLE(dialog)) gtk_widget_show(dialog);
1662 }
1663
1664 static gint file_util_rename_multiple_find_row(RenameDataMult *rd, const gchar *path, GtkTreeIter *iter)
1665 {
1666         GtkTreeModel *store;
1667         gint valid;
1668         gint row = 0;
1669
1670         store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1671         valid = gtk_tree_model_get_iter_first(store, iter);
1672         while (valid)
1673                 {
1674                 gchar *path_n;
1675                 gint ret;
1676
1677                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, RENAME_COLUMN_PATH, &path_n, -1);
1678                 ret = (strcmp(path_n, path) == 0);
1679                 g_free(path_n);
1680                 if (ret) return row;
1681
1682                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
1683                 row++;
1684                 }
1685
1686         return -1;
1687 }
1688
1689 static void file_util_rename_multiple(RenameDataMult *rd)
1690 {
1691         FileDialog *fd;
1692
1693         fd = rd->fd;
1694
1695         if (isfile(fd->dest_path) && !fd->type)
1696                 {
1697                 GenericDialog *gd;
1698
1699                 gd = file_util_gen_dlg(_("Overwrite file"), "GQview", "dlg_confirm",
1700                                         NULL, TRUE,
1701                                         file_util_rename_multiple_cancel_cb, rd);
1702
1703                 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
1704                                            _("Overwrite file?"),
1705                                            _("Replace existing file by renaming new file."));
1706                 pref_spacer(gd->vbox, 0);
1707
1708                 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_rename_multiple_ok_cb, TRUE);
1709                 generic_dialog_add_image(gd, fd->dest_path, _("Existing file"), fd->source_path, _("New file"), TRUE);
1710
1711                 gtk_widget_hide(GENERIC_DIALOG(fd)->dialog);
1712
1713                 gtk_widget_show(gd->dialog);
1714                 return;
1715                 }
1716         else
1717                 {
1718                 if (!rename_file(fd->source_path, fd->dest_path))
1719                         {
1720                         gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\n to:\n%s"),
1721                                                       filename_from_path(fd->source_path),
1722                                                       filename_from_path(fd->dest_path));
1723                         file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1724                         g_free(text);
1725                         }
1726                 else
1727                         {
1728                         GtkTreeModel *store;
1729                         GtkTreeIter iter;
1730                         GtkTreeIter next;
1731                         gint row;
1732
1733                         file_maint_renamed(fd->source_path, fd->dest_path);
1734
1735                         store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1736                         row = file_util_rename_multiple_find_row(rd, rd->fd->source_path, &iter);
1737
1738                         if (row >= 0 &&
1739                             (gtk_tree_model_iter_nth_child(store, &next, NULL, row + 1) ||
1740                             (row > 0 && gtk_tree_model_iter_nth_child(store, &next, NULL, row - 1)) ) )
1741                                 {
1742                                 GtkTreeSelection *selection;
1743
1744                                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
1745                                 gtk_tree_selection_select_iter(selection, &next);
1746                                 gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1747                                 }
1748                         else
1749                                 {
1750                                 if (debug) printf("closed by #%d\n", row);
1751
1752                                 file_dialog_close(rd->fd);
1753                                 }
1754                         }
1755                 }
1756 }
1757
1758 static void file_util_rename_multiple_auto(RenameDataMult *rd)
1759 {
1760         const gchar *front;
1761         const gchar *end;
1762         gint start_n;
1763         gint padding;
1764         gint n;
1765         GtkTreeModel *store;
1766         GtkTreeIter iter;
1767         gint valid;
1768         gint success;
1769
1770         history_combo_append_history(rd->auto_entry_front, NULL);
1771         history_combo_append_history(rd->auto_entry_end, NULL);
1772
1773         front = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_front));
1774         end = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_end));
1775         start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_start));
1776         padding = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_pad));
1777
1778         store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1779
1780         /* first check for name conflicts */
1781         success = TRUE;
1782         n = start_n;
1783         valid = gtk_tree_model_get_iter_first(store, &iter);
1784         while (valid && success)
1785                 {
1786                 gchar *dest;
1787                 gchar *base;
1788                 gchar *path;
1789
1790                 gtk_tree_model_get(store, &iter, RENAME_COLUMN_PATH, &path, -1);
1791
1792                 base = remove_level_from_path(path);
1793                 dest = g_strdup_printf("%s/%s%0*d%s", base, front, padding, n, end);
1794                 if (isname(dest)) success = FALSE;
1795                 g_free(dest);
1796                 g_free(base);
1797                 g_free(path);
1798
1799                 n++;
1800                 valid = gtk_tree_model_iter_next(store, &iter);
1801                 }
1802
1803         if (!success)
1804                 {
1805                 file_util_warning_dialog(_("Auto rename"),
1806                                _("Can not auto rename with the selected\nnumber set, one or more files exist that\nmatch the resulting name list.\n"),
1807                                GTK_STOCK_DIALOG_WARNING, NULL);
1808                 return;
1809                 }
1810
1811         /* select the first iter, so that on fail the correct info is given to user */
1812         if (gtk_tree_model_get_iter_first(store, &iter))
1813                 {
1814                 GtkTreeSelection *selection;
1815
1816                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
1817                 gtk_tree_selection_select_iter(selection, &iter);
1818                 }
1819
1820         /* now do it for real */
1821         success = TRUE;
1822         n = start_n;
1823         while (success && gtk_tree_model_get_iter_first(store, &iter))
1824                 {
1825                 gchar *dest;
1826                 gchar *base;
1827                 gchar *path;
1828
1829                 gtk_tree_model_get(store, &iter, RENAME_COLUMN_PATH, &path, -1);
1830
1831                 base = remove_level_from_path(path);
1832                 dest = g_strdup_printf("%s/%s%0*d%s", base, front, padding, n, end);
1833                 if (!rename_file(path, dest))
1834                         {
1835                         success = FALSE;
1836                         }
1837                 else
1838                         {
1839                         file_maint_renamed(path, dest);
1840                         }
1841
1842                 g_free(dest);
1843                 g_free(base);
1844                 g_free(path);
1845
1846                 if (success)
1847                         {
1848                         gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1849                         if (gtk_tree_model_get_iter_first(store, &iter))
1850                                 {
1851                                 GtkTreeSelection *selection;
1852
1853                                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
1854                                 gtk_tree_selection_select_iter(selection, &iter);
1855                                 }
1856                         }
1857
1858                 n++;
1859                 }
1860
1861         if (!success)
1862                 {
1863                 gchar *buf;
1864
1865                 n--;
1866                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(rd->auto_spin_start), (gdouble)n);
1867
1868                 buf = g_strdup_printf(_("Failed to rename\n%s\nThe number was %d."), filename_from_path(rd->fd->source_path), n);
1869                 file_util_warning_dialog(_("Auto rename"), buf, GTK_STOCK_DIALOG_ERROR, NULL);
1870                 g_free(buf);
1871
1872                 return;
1873                 }
1874
1875         file_dialog_close(rd->fd);
1876 }
1877
1878 static void file_util_rename_multiple_cb(FileDialog *fd, gpointer data)
1879 {
1880         RenameDataMult *rd = data;
1881         gchar *base;
1882         const gchar *name;
1883
1884         if (rd->rename_auto)
1885                 {
1886                 file_util_rename_multiple_auto(rd);
1887                 return;
1888                 }
1889
1890         name = gtk_entry_get_text(GTK_ENTRY(rd->rename_entry));
1891         base = remove_level_from_path(fd->source_path);
1892
1893         g_free(fd->dest_path);
1894         fd->dest_path = concat_dir_and_file(base, name);
1895         g_free(base);
1896
1897         if (strlen(name) == 0 || strcmp(fd->source_path, fd->dest_path) == 0)
1898                 {
1899                 return;
1900                 }
1901
1902         fd->type = FALSE;
1903         file_util_rename_multiple(rd);
1904 }
1905
1906 static void file_util_rename_multiple_close_cb(FileDialog *fd, gpointer data)
1907 {
1908         RenameDataMult *rd = data;
1909
1910         file_dialog_close(rd->fd);
1911 }
1912
1913 static gboolean file_util_rename_multiple_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
1914                                                     gboolean path_currently_selected, gpointer data)
1915 {
1916         RenameDataMult *rd = data;
1917         GtkTreeIter iter;
1918         const gchar *name;
1919         gchar *path = NULL;
1920
1921         if (path_currently_selected ||
1922             !gtk_tree_model_get_iter(store, &iter, tpath)) return TRUE;
1923         gtk_tree_model_get(store, &iter, RENAME_COLUMN_PATH, &path, -1);
1924
1925         g_free(rd->fd->source_path);
1926         rd->fd->source_path = path;
1927
1928         name = filename_from_path(rd->fd->source_path);
1929         gtk_label_set_text(GTK_LABEL(rd->rename_label), name);
1930         gtk_entry_set_text(GTK_ENTRY(rd->rename_entry), name);
1931
1932         image_change_path(rd->imd, rd->fd->source_path, 0.0);
1933
1934         if (GTK_WIDGET_VISIBLE(rd->rename_box))
1935                 {
1936                 gtk_widget_grab_focus(rd->rename_entry);
1937                 gtk_editable_select_region(GTK_EDITABLE(rd->rename_entry), 0, filename_base_length(name));
1938                 }
1939
1940         return TRUE;
1941 }
1942
1943 static void file_util_rename_multiple_preview_update(RenameDataMult *rd)
1944 {
1945         GtkTreeModel *store;
1946         GtkTreeIter iter;
1947         const gchar *front;
1948         const gchar *end;
1949         gint valid;
1950         gint start_n;
1951         gint padding;
1952         gint n;
1953
1954         front = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_front));
1955         end = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_end));
1956         start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_start));
1957         padding = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_pad));
1958
1959         store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1960         n = start_n;
1961         valid = gtk_tree_model_get_iter_first(store, &iter);
1962         while (valid)
1963                 {
1964                 gchar *dest;
1965
1966                 dest = g_strdup_printf("%s%0*d%s", front, padding, n, end);
1967                 gtk_list_store_set(GTK_LIST_STORE(store), &iter, RENAME_COLUMN_PREVIEW, dest, -1);
1968                 g_free(dest);
1969
1970                 n++;
1971                 valid = gtk_tree_model_iter_next(store, &iter);
1972                 }
1973
1974 }
1975
1976 static gboolean file_util_rename_multiple_idle_cb(gpointer data)
1977 {
1978         RenameDataMult *rd = data;
1979
1980         file_util_rename_multiple_preview_update(rd);
1981
1982         rd->update_idle_id = -1;
1983         return FALSE;
1984 }
1985
1986 static void file_util_rename_multiple_preview_order_cb(GtkTreeModel *treemodel, GtkTreePath *tpath,
1987                                                        GtkTreeIter *iter, gpointer data)
1988 {
1989         RenameDataMult *rd = data;
1990
1991         if (rd->rename_auto && rd->update_idle_id == -1)
1992                 {
1993                 rd->update_idle_id = g_idle_add(file_util_rename_multiple_idle_cb, rd);
1994                 }
1995 }
1996
1997 static void file_util_rename_multiple_preview_entry_cb(GtkWidget *entry, gpointer data)
1998 {
1999         RenameDataMult *rd = data;
2000         file_util_rename_multiple_preview_update(rd);
2001 }
2002
2003 static void file_util_rename_multiple_preview_adj_cb(GtkWidget *spin, gpointer data)
2004 {
2005         RenameDataMult *rd = data;
2006         file_util_rename_multiple_preview_update(rd);
2007 }
2008
2009 static void file_util_rename_mulitple_auto_toggle(GtkWidget *widget, gpointer data)
2010 {
2011         RenameDataMult *rd = data;
2012         GtkTreeViewColumn *column;
2013
2014         rd->rename_auto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rd->button_auto));
2015
2016         if (rd->rename_auto)
2017                 {
2018                 if (GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_hide(rd->rename_box);
2019                 if (!GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_show(rd->auto_box);
2020                 file_util_rename_multiple_preview_update(rd);
2021                 }
2022         else
2023                 {
2024                 if (GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_hide(rd->auto_box);
2025                 if (!GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_show(rd->rename_box);
2026                 }
2027
2028         column = gtk_tree_view_get_column(GTK_TREE_VIEW(rd->listview), RENAME_COLUMN_PREVIEW - 1);
2029         gtk_tree_view_column_set_visible(column, rd->rename_auto);
2030 }
2031
2032 static GtkWidget *furm_simple_vlabel(GtkWidget *box, const gchar *text, gint expand)
2033 {
2034         GtkWidget *vbox;
2035         GtkWidget *label;
2036
2037         vbox = gtk_vbox_new(FALSE, 0);
2038         gtk_box_pack_start(GTK_BOX(box), vbox, expand, expand, 0);
2039         gtk_widget_show(vbox);
2040
2041         label = gtk_label_new(text);
2042         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
2043         gtk_widget_show(label);
2044
2045         return vbox;
2046 }
2047
2048 static GtkTreeViewColumn *file_util_rename_multiple_add_column(RenameDataMult *rd, const gchar *text, gint n)
2049 {
2050         GtkTreeViewColumn *column;
2051         GtkCellRenderer *renderer;
2052
2053         column = gtk_tree_view_column_new();
2054         gtk_tree_view_column_set_title(column, text);
2055         gtk_tree_view_column_set_min_width(column, 4);
2056         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2057         renderer = gtk_cell_renderer_text_new();
2058         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2059         gtk_tree_view_column_add_attribute(column, renderer, "text", n);
2060         gtk_tree_view_append_column(GTK_TREE_VIEW(rd->listview), column);
2061
2062         return column;
2063 }
2064
2065 static void file_util_rename_multiple_destroy_cb(GtkWidget *widget, gpointer data)
2066 {
2067         RenameDataMult *rd = data;
2068
2069         if (rd->update_idle_id != -1) g_source_remove(rd->update_idle_id);
2070
2071         g_free(rd);
2072 }
2073
2074 static void file_util_rename_multiple_do(GList *source_list, GtkWidget *parent)
2075 {
2076         RenameDataMult *rd;
2077         GtkWidget *pane;
2078         GtkWidget *scrolled;
2079         GtkListStore *store;
2080         GtkTreeSelection *selection;
2081         GtkTreeViewColumn *column;
2082         GtkWidget *hbox;
2083         GtkWidget *vbox;
2084         GtkWidget *box2;
2085         GtkWidget *table;
2086         GtkWidget *combo;
2087         GList *work;
2088         const gchar *name;
2089
2090         rd = g_new0(RenameDataMult, 1);
2091
2092         rd->fd = file_util_file_dlg(_("Rename - GQview"),
2093                                     "GQview", "dlg_rename", parent,
2094                                     file_util_rename_multiple_close_cb, rd);
2095         generic_dialog_add_message(GENERIC_DIALOG(rd->fd), NULL, _("Rename multiple files"), NULL);
2096         file_dialog_add_button(rd->fd, GTK_STOCK_OK, _("_Rename"), file_util_rename_multiple_cb, TRUE);
2097
2098         rd->fd->source_path = g_strdup(source_list->data);
2099         rd->fd->dest_path = NULL;
2100
2101         rd->update_idle_id = -1;
2102
2103         vbox = GENERIC_DIALOG(rd->fd)->vbox;
2104
2105         pane = gtk_hpaned_new();
2106         gtk_box_pack_start(GTK_BOX(vbox), pane, TRUE, TRUE, 0);
2107         gtk_widget_show(pane);
2108         
2109         scrolled = gtk_scrolled_window_new(NULL, NULL);
2110         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2111         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
2112                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2113         gtk_paned_pack1(GTK_PANED(pane), scrolled, TRUE, TRUE);
2114         gtk_widget_show(scrolled);
2115
2116         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
2117         rd->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2118         g_object_unref(store);
2119
2120         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(rd->listview), TRUE);
2121         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(rd->listview), FALSE);
2122
2123         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2124         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2125         gtk_tree_selection_set_select_function(selection, file_util_rename_multiple_select_cb, rd, NULL);
2126
2127         file_util_rename_multiple_add_column(rd, _("Original Name"), RENAME_COLUMN_NAME);
2128         column = file_util_rename_multiple_add_column(rd, _("Preview"), RENAME_COLUMN_PREVIEW);
2129         gtk_tree_view_column_set_visible(column, FALSE);
2130         
2131         gtk_tree_view_set_reorderable(GTK_TREE_VIEW(rd->listview), TRUE);
2132         g_signal_connect(G_OBJECT(store), "row_changed",
2133                          G_CALLBACK(file_util_rename_multiple_preview_order_cb), rd);
2134         gtk_widget_set_size_request(rd->listview, 250, 150);
2135
2136         gtk_container_add(GTK_CONTAINER(scrolled), rd->listview);
2137         gtk_widget_show(rd->listview);
2138
2139         work = source_list;
2140         while (work)
2141                 {
2142                 gchar *path = work->data;
2143                 GtkTreeIter iter;
2144
2145                 gtk_list_store_append(store, &iter);
2146                 gtk_list_store_set(store, &iter, RENAME_COLUMN_PATH, path, RENAME_COLUMN_NAME, filename_from_path(path), -1);
2147
2148                 work = work->next;
2149                 }
2150
2151         path_list_free(source_list);
2152
2153         rd->imd = image_new(TRUE);
2154         gtk_widget_set_size_request(rd->imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
2155         gtk_paned_pack2(GTK_PANED(pane), rd->imd->widget, FALSE, TRUE);
2156         gtk_widget_show(rd->imd->widget);
2157
2158         hbox = gtk_hbox_new(FALSE, 0);
2159         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2160         gtk_widget_show(hbox);
2161
2162         rd->button_auto = gtk_check_button_new_with_label(_("Auto rename"));
2163         g_signal_connect(G_OBJECT(rd->button_auto), "clicked",
2164                          G_CALLBACK(file_util_rename_mulitple_auto_toggle), rd);
2165         gtk_box_pack_end(GTK_BOX(hbox), rd->button_auto, FALSE, FALSE, 0);
2166         gtk_widget_show(rd->button_auto);
2167
2168         rd->rename_box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, 0);
2169         table = pref_table_new(rd->rename_box, 2, 2, FALSE, FALSE);
2170
2171         pref_table_label(table, 0, 0, _("Original name:"), 1.0);
2172         rd->rename_label = pref_table_label(table, 1, 0, filename_from_path(rd->fd->source_path), 0.0);
2173
2174         pref_table_label(table, 0, 1, _("New name:"), 1.0);
2175
2176         rd->rename_entry = gtk_entry_new();
2177         gtk_table_attach(GTK_TABLE(table), rd->rename_entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
2178         generic_dialog_attach_default(GENERIC_DIALOG(rd->fd), rd->rename_entry);
2179         gtk_widget_grab_focus(rd->rename_entry);
2180
2181         name = filename_from_path(rd->fd->source_path);
2182         gtk_entry_set_text(GTK_ENTRY(rd->rename_entry), name);
2183         gtk_editable_select_region(GTK_EDITABLE(rd->rename_entry), 0, filename_base_length(name));
2184         gtk_widget_show(rd->rename_entry);
2185
2186         rd->auto_box = gtk_vbox_new(FALSE, PREF_PAD_GAP);
2187         gtk_box_pack_start(GTK_BOX(vbox), rd->auto_box, FALSE, FALSE, 0);
2188         /* do not show it here */
2189
2190         hbox = pref_box_new(rd->auto_box, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
2191
2192         box2 = furm_simple_vlabel(hbox, _("Begin text"), TRUE);
2193
2194         combo = history_combo_new(&rd->auto_entry_front, "", "numerical_rename_prefix", -1);
2195         g_signal_connect(G_OBJECT(rd->auto_entry_front), "changed",
2196                          G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2197         gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2198         gtk_widget_show(combo);
2199         
2200         box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
2201
2202         rd->auto_spin_start = pref_spin_new(box2, NULL, NULL,
2203                                             0.0, 1000000.0, 1.0, 0, 0.0,
2204                                             G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2205
2206         box2 = furm_simple_vlabel(hbox, _("End text"), TRUE);
2207
2208         combo = history_combo_new(&rd->auto_entry_end, "", "numerical_rename_suffix", -1);
2209         g_signal_connect(G_OBJECT(rd->auto_entry_end), "changed",
2210                          G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2211         gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2212         gtk_widget_show(combo);
2213
2214         rd->auto_spin_pad = pref_spin_new(rd->auto_box, _("Padding:"), NULL,
2215                                           1.0, 8.0, 1.0, 0, 1.0,
2216                                           G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2217
2218         image_change_path(rd->imd, rd->fd->source_path, 0.0);
2219
2220         g_signal_connect(G_OBJECT(GENERIC_DIALOG(rd->fd)->dialog), "destroy",
2221                          G_CALLBACK(file_util_rename_multiple_destroy_cb), rd);
2222
2223         gtk_widget_show(GENERIC_DIALOG(rd->fd)->dialog);
2224 }
2225
2226 /*
2227  * rename single file
2228  */
2229
2230 static void file_util_rename_single(FileDataSingle *fds);
2231
2232 static void file_util_rename_single_ok_cb(GenericDialog *gd, gpointer data)
2233 {
2234         FileDataSingle *fds = data;
2235         fds->confirmed = TRUE;
2236         file_util_rename_single(fds);
2237 }
2238
2239 static void file_util_rename_single_cancel_cb(GenericDialog *gd, gpointer data)
2240 {
2241         FileDataSingle *fds = data;
2242         file_data_single_free(fds);
2243 }
2244
2245 static void file_util_rename_single(FileDataSingle *fds)
2246 {
2247         if (isfile(fds->dest) && !fds->confirmed)
2248                 {
2249                 GenericDialog *gd;
2250
2251                 gd = file_util_gen_dlg(_("Overwrite file"), "GQview", "dlg_confirm",
2252                                         NULL, TRUE,
2253                                         file_util_rename_single_cancel_cb, fds);
2254
2255                 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
2256                                            _("Overwrite file?"),
2257                                            _("Replace existing file by renaming new file."));
2258                 pref_spacer(gd->vbox, 0);
2259
2260                 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_rename_single_ok_cb, TRUE);
2261                 generic_dialog_add_image(gd, fds->dest, _("Existing file"), fds->source, _("New file"), TRUE);
2262
2263                 gtk_widget_show(gd->dialog);
2264
2265                 return;
2266                 }
2267         else
2268                 {
2269                 if (!rename_file(fds->source, fds->dest))
2270                         {
2271                         gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), filename_from_path(fds->source), filename_from_path(fds->dest));
2272                         file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2273                         g_free(text);
2274                         }
2275                 else
2276                         {
2277                         file_maint_renamed(fds->source, fds->dest);
2278                         }
2279                 }
2280         file_data_single_free(fds);
2281 }
2282
2283 static void file_util_rename_single_cb(FileDialog *fd, gpointer data)
2284 {
2285         const gchar *name;
2286         gchar *path;
2287
2288         name = gtk_entry_get_text(GTK_ENTRY(fd->entry));
2289         path = concat_dir_and_file(fd->dest_path, name);
2290
2291         if (strlen(name) == 0 || strcmp(fd->source_path, path) == 0)
2292                 {
2293                 g_free(path);
2294                 return;
2295                 }
2296
2297         file_util_rename_single(file_data_single_new(fd->source_path, path, fd->type));
2298
2299         g_free(path);
2300         file_dialog_close(fd);
2301 }
2302
2303 static void file_util_rename_single_close_cb(FileDialog *fd, gpointer data)
2304 {
2305         file_dialog_close(fd);
2306 }
2307
2308 static void file_util_rename_single_do(const gchar *source_path, GtkWidget *parent)
2309 {
2310         FileDialog *fd;
2311         GtkWidget *table;
2312         const gchar *name;
2313
2314         fd = file_util_file_dlg(_("Rename - GQview"), "GQview", "dlg_rename", parent,
2315                              file_util_rename_single_close_cb, NULL);
2316
2317         generic_dialog_add_message(GENERIC_DIALOG(fd), NULL, _("Rename file"), NULL);
2318         generic_dialog_add_image(GENERIC_DIALOG(fd), source_path, NULL, NULL, NULL, FALSE);
2319
2320         file_dialog_add_button(fd, GTK_STOCK_OK, _("_Rename"), file_util_rename_single_cb, TRUE);
2321
2322         fd->source_path = g_strdup(source_path);
2323         fd->dest_path = remove_level_from_path(source_path);
2324
2325         table = pref_table_new(GENERIC_DIALOG(fd)->vbox, 2, 2, FALSE, FALSE);
2326
2327         pref_table_label(table, 0, 0, _("Original name:"), 1.0);
2328         pref_table_label(table, 1, 0, filename_from_path(fd->source_path), 0.0);
2329
2330         pref_table_label(table, 0, 1, _("New name:"), 1.0);
2331
2332         fd->entry = gtk_entry_new();
2333         gtk_table_attach(GTK_TABLE(table), fd->entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
2334         generic_dialog_attach_default(GENERIC_DIALOG(fd), fd->entry);
2335         gtk_widget_grab_focus(fd->entry);
2336
2337         name = filename_from_path(fd->source_path);
2338         gtk_entry_set_text(GTK_ENTRY(fd->entry), name);
2339         gtk_editable_select_region(GTK_EDITABLE(fd->entry), 0, filename_base_length(name));
2340         gtk_widget_show(fd->entry);
2341
2342         gtk_widget_show(GENERIC_DIALOG(fd)->dialog);
2343 }
2344
2345 void file_util_rename(const gchar *source_path, GList *source_list, GtkWidget *parent)
2346 {
2347         if (!source_path && !source_list) return;
2348
2349         if (source_path)
2350                 {
2351                 file_util_rename_single_do(source_path, parent);
2352                 }
2353         else if (!source_list->next)
2354                 {
2355                 file_util_rename_single_do(source_list->data, parent);
2356                 path_list_free(source_list);
2357                 }
2358         else
2359                 {
2360                 file_util_rename_multiple_do(source_list, parent);
2361                 }
2362 }
2363
2364 /*
2365  *--------------------------------------------------------------------------
2366  * Create directory routines
2367  *--------------------------------------------------------------------------
2368  */
2369
2370 static void file_util_create_dir_do(const gchar *base, const gchar *name)
2371 {
2372         gchar *path;
2373
2374         path = concat_dir_and_file(base, name);
2375
2376         if (isdir(path))
2377                 {
2378                 gchar *text = g_strdup_printf(_("The folder:\n%s\nalready exists."), name);
2379                 file_util_warning_dialog(_("Folder exists"), text, GTK_STOCK_DIALOG_INFO, NULL);
2380                 g_free(text);
2381                 }
2382         else if (isname(path))
2383                 {
2384                 gchar *text = g_strdup_printf(_("The path:\n%s\nalready exists as a file."), name);
2385                 file_util_warning_dialog(_("Could not create folder"), text, GTK_STOCK_DIALOG_INFO, NULL);
2386                 g_free(text);
2387                 }
2388         else
2389                 {
2390                 if (!mkdir_utf8(path, 0755))
2391                         {
2392                         gchar *text = g_strdup_printf(_("Unable to create folder:\n%s"), name);
2393                         file_util_warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2394                         g_free(text);
2395                         }
2396                 }
2397
2398         g_free(path);
2399 }
2400
2401 static void file_util_create_dir_cb(FileDialog *fd, gpointer data)
2402 {
2403         const gchar *name;
2404
2405         name = gtk_entry_get_text(GTK_ENTRY(fd->entry));
2406
2407         if (strlen(name) == 0) return;
2408
2409         if (name[0] == '/')
2410                 {
2411                 gchar *buf;
2412                 buf  = remove_level_from_path(name);
2413                 file_util_create_dir_do(buf, filename_from_path(name));
2414                 g_free(buf);
2415                 }
2416         else
2417                 {
2418                 file_util_create_dir_do(fd->dest_path, name);
2419                 }
2420
2421         file_dialog_close(fd);
2422 }
2423
2424 static void file_util_create_dir_close_cb(FileDialog *fd, gpointer data)
2425 {
2426         file_dialog_close(fd);
2427 }
2428
2429 void file_util_create_dir(const gchar *path, GtkWidget *parent)
2430 {
2431         FileDialog *fd;
2432         gchar *text;
2433
2434         if (!isdir(path)) return;
2435
2436         fd = file_util_file_dlg(_("New folder - GQview"), "GQview", "dlg_newdir", parent,
2437                              file_util_create_dir_close_cb, NULL);
2438
2439         text = g_strdup_printf(_("Create folder in:\n%s\nnamed:"), path);
2440         generic_dialog_add_message(GENERIC_DIALOG(fd), NULL, NULL, text);
2441         g_free(text);
2442
2443         file_dialog_add_button(fd, GTK_STOCK_OK, NULL, file_util_create_dir_cb, TRUE);
2444
2445         fd->dest_path = g_strdup(path);
2446
2447         fd->entry = gtk_entry_new();
2448         gtk_box_pack_start(GTK_BOX(GENERIC_DIALOG(fd)->vbox), fd->entry, FALSE, FALSE, 0);
2449         generic_dialog_attach_default(GENERIC_DIALOG(fd), fd->entry);
2450         gtk_widget_grab_focus(fd->entry);
2451         gtk_widget_show(fd->entry);
2452
2453         gtk_widget_show(GENERIC_DIALOG(fd)->dialog);
2454 }
2455