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