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