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!
18 #include "cache_maint.h"
26 #include "thumb_standard.h"
27 #include "ui_bookmark.h"
28 #include "ui_fileops.h"
30 #include "ui_tabcomp.h"
34 *--------------------------------------------------------------------------
35 * call these when names change, files move, deleted, etc.
36 * so that any open windows are also updated
37 *--------------------------------------------------------------------------
41 void file_maint_renamed(FileData *fd)
43 cache_maint_moved(fd);
44 collection_maint_renamed(fd);
46 layout_maint_renamed(fd);
47 view_window_maint_moved(fd);
48 dupe_maint_renamed(fd);
49 search_maint_renamed(fd);
52 /* under most cases ignore_list should be NULL */
53 void file_maint_removed(FileData *fd, GList *ignore_list)
55 layout_maint_removed(fd, ignore_list);
56 view_window_maint_removed(fd, ignore_list);
57 dupe_maint_removed(fd);
58 search_maint_removed(fd);
60 collection_maint_removed(fd);
61 cache_maint_removed(fd);
64 /* special case for correct main window behavior */
65 void file_maint_moved(FileData *fd, GList *ignore_list)
67 cache_maint_moved(fd);
68 collection_maint_renamed(fd);
70 layout_maint_moved(fd, ignore_list);
71 view_window_maint_moved(fd);
72 dupe_maint_renamed(fd);
73 search_maint_renamed(fd);
76 void file_maint_copied(FileData *fd)
78 cache_maint_copied(fd);
82 *--------------------------------------------------------------------------
83 * The file manipulation dialogs
84 *--------------------------------------------------------------------------
96 typedef struct _FileDataMult FileDataMult;
113 GtkWidget *rename_box;
114 GtkWidget *rename_entry;
115 GtkWidget *rename_auto_box;
117 GtkWidget *yes_all_button;
120 typedef struct _FileDataSingle FileDataSingle;
121 struct _FileDataSingle
131 GtkWidget *rename_box;
132 GtkWidget *rename_entry;
133 GtkWidget *rename_auto_box;
137 *--------------------------------------------------------------------------
138 * Adds 1 or 2 images (if 2, side by side) to a GenericDialog
139 *--------------------------------------------------------------------------
142 #define DIALOG_DEF_IMAGE_DIM_X 200
143 #define DIALOG_DEF_IMAGE_DIM_Y 150
145 static void generic_dialog_add_image(GenericDialog *gd, GtkWidget *box,
146 FileData *fd1, const gchar *header1,
147 FileData *fd2, const gchar *header2,
151 GtkWidget *hbox = NULL;
153 GtkWidget *label = NULL;
155 if (!box) box = gd->vbox;
159 hbox = pref_box_new(box, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
164 vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP);
169 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
171 sep = gtk_vseparator_new();
172 gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
173 gtk_widget_show(sep);
177 gtk_box_pack_start(GTK_BOX(box), vbox, TRUE, TRUE, PREF_PAD_GAP);
179 gtk_widget_show(vbox);
185 head = pref_label_new(vbox, header1);
186 pref_label_bold(head, TRUE, FALSE);
187 gtk_misc_set_alignment(GTK_MISC(head), 0.0, 0.5);
190 imd = image_new(FALSE);
191 g_object_set(G_OBJECT(imd->pr), "zoom_expand", FALSE, NULL);
192 gtk_widget_set_size_request(imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
193 gtk_box_pack_start(GTK_BOX(vbox), imd->widget, TRUE, TRUE, 0);
194 image_change_fd(imd, fd1, 0.0);
195 gtk_widget_show(imd->widget);
199 label = pref_label_new(vbox, (fd1 == NULL) ? "" : fd1->name);
202 /* only the first image is stored (for use in gd_image_set) */
203 g_object_set_data(G_OBJECT(gd->dialog), "img_image", imd);
204 g_object_set_data(G_OBJECT(gd->dialog), "img_label", label);
211 vbox = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
217 head = pref_label_new(vbox, header2);
218 pref_label_bold(head, TRUE, FALSE);
219 gtk_misc_set_alignment(GTK_MISC(head), 0.0, 0.5);
222 imd = image_new(FALSE);
223 g_object_set(G_OBJECT(imd->pr), "zoom_expand", FALSE, NULL);
224 gtk_widget_set_size_request(imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
225 gtk_box_pack_start(GTK_BOX(vbox), imd->widget, TRUE, TRUE, 0);
226 image_change_fd(imd, fd2, 0.0);
227 gtk_widget_show(imd->widget);
229 pref_label_new(vbox, fd2->name);
233 static void generic_dialog_image_set(GenericDialog *gd, FileData *fd)
238 imd = g_object_get_data(G_OBJECT(gd->dialog), "img_image");
239 label = g_object_get_data(G_OBJECT(gd->dialog), "img_label");
243 image_change_fd(imd, fd, 0.0);
244 if (label) gtk_label_set_text(GTK_LABEL(label), fd->name);
248 *--------------------------------------------------------------------------
249 * Wrappers to aid in setting additional dialog properties (unde mouse, etc.)
250 *--------------------------------------------------------------------------
253 GenericDialog *file_util_gen_dlg(const gchar *title,
254 const gchar *wmclass, const gchar *wmsubclass,
255 GtkWidget *parent, gint auto_close,
256 void (*cancel_cb)(GenericDialog *, gpointer), gpointer data)
260 gd = generic_dialog_new(title, wmclass, wmsubclass, parent, auto_close, cancel_cb, data);
261 if (place_dialogs_under_mouse)
263 gtk_window_set_position(GTK_WINDOW(gd->dialog), GTK_WIN_POS_MOUSE);
269 FileDialog *file_util_file_dlg(const gchar *title,
270 const gchar *wmclass, const gchar *wmsubclass,
272 void (*cancel_cb)(FileDialog *, gpointer), gpointer data)
276 fdlg = file_dialog_new(title, wmclass, wmsubclass, parent, cancel_cb, data);
277 if (place_dialogs_under_mouse)
279 gtk_window_set_position(GTK_WINDOW(GENERIC_DIALOG(fdlg)->dialog), GTK_WIN_POS_MOUSE);
285 /* this warning dialog is copied from SLIK's ui_utildg.c,
286 * because it does not have a mouse center option,
287 * and we must center it before show, implement it here.
289 static void file_util_warning_dialog_ok_cb(GenericDialog *gd, gpointer data)
294 GenericDialog *file_util_warning_dialog(const gchar *heading, const gchar *message,
295 const gchar *icon_stock_id, GtkWidget *parent)
299 gd = file_util_gen_dlg(heading, "Geeqie", "warning", parent, TRUE, NULL, NULL);
300 generic_dialog_add_message(gd, icon_stock_id, heading, message);
301 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, file_util_warning_dialog_ok_cb, TRUE);
302 if (place_dialogs_under_mouse)
304 gtk_window_set_position(GTK_WINDOW(gd->dialog), GTK_WIN_POS_MOUSE);
306 gtk_widget_show(gd->dialog);
311 static gint filename_base_length(const gchar *name)
319 if (filter_name_exists(name))
323 ext = extension_from_path(name);
324 if (ext) n -= strlen(ext);
332 *--------------------------------------------------------------------------
333 * Move and Copy routines
334 *--------------------------------------------------------------------------
337 static gint copy_file_ext_cb(gpointer ed, gint flags, GList *list, gpointer data)
339 if ((flags & EDITOR_ERROR_MASK) && !(flags & EDITOR_ERROR_SKIPPED))
341 FileData *fd = list->data;
342 gchar *title = _("Error copying file");
343 gchar *text = g_strdup_printf(_("%s\nUnable to copy file:\n%s\nto:\n%s"), editor_get_error_str(flags), fd->name, fd->change->dest);
344 file_util_warning_dialog(title, text, GTK_STOCK_DIALOG_ERROR, NULL);
349 FileData *fd = list->data;
350 if (!(flags & EDITOR_ERROR_MASK))
351 file_maint_copied(fd);
352 file_data_change_info_free(NULL, fd);
355 return EDITOR_CB_CONTINUE;
359 gint copy_file_ext(FileData *fd)
362 g_assert(fd->change);
363 if (editor_command[CMD_COPY])
365 ok = !start_editor_from_file_full(CMD_COPY, fd, copy_file_ext_cb, NULL);
366 if (ok) return ok; /* that's all for now, let's continue in callback */
369 ok = copy_file(fd->change->source, fd->change->dest);
373 file_maint_copied(fd);
376 file_data_change_info_free(NULL, fd);
381 static gint move_file_ext_cb(gpointer ed, gint flags, GList *list, gpointer data)
383 if ((flags & EDITOR_ERROR_MASK) && !(flags & EDITOR_ERROR_SKIPPED))
385 FileData *fd = list->data;
386 gchar *title = _("Error moving file");
387 gchar *text = g_strdup_printf(_("%s\nUnable to move file:\n%s\nto:\n%s"), editor_get_error_str(flags), fd->name, fd->change->dest);
388 file_util_warning_dialog(title, text, GTK_STOCK_DIALOG_ERROR, NULL);
393 FileData *fd = list->data;
394 if (!(flags & EDITOR_ERROR_MASK))
396 file_data_do_change(fd);
397 file_maint_moved(fd, NULL);
399 file_data_change_info_free(NULL, fd);
402 return EDITOR_CB_CONTINUE;
407 gint move_file_ext(FileData *fd)
410 g_assert(fd->change);
411 if (editor_command[CMD_MOVE])
413 ok = !start_editor_from_file_full(CMD_MOVE, fd, move_file_ext_cb, NULL);
414 if (ok) return ok; /* that's all for now, let's continue in callback */
417 ok = move_file(fd->change->source, fd->change->dest);
421 file_data_do_change(fd);
422 file_maint_moved(fd, NULL);
425 file_data_change_info_free(NULL, fd);
430 static gint rename_file_ext_cb(gpointer ed, gint flags, GList *list, gpointer data)
432 if ((flags & EDITOR_ERROR_MASK) && !(flags & EDITOR_ERROR_SKIPPED))
434 FileData *fd = list->data;
435 gchar *title = _("Error renaming file");
436 gchar *text = g_strdup_printf(_("%s\nUnable to rename file:\n%s\nto:\n%s"), editor_get_error_str(flags), fd->name, fd->change->dest);
437 file_util_warning_dialog(title, text, GTK_STOCK_DIALOG_ERROR, NULL);
442 FileData *fd = list->data;
443 if (!(flags & EDITOR_ERROR_MASK))
445 file_data_do_change(fd);
446 file_maint_renamed(fd);
448 file_data_change_info_free(NULL, fd);
451 return EDITOR_CB_CONTINUE;
454 gint rename_file_ext(FileData *fd)
457 g_assert(fd->change);
458 if (editor_command[CMD_RENAME])
460 ok = !start_editor_from_file_full(CMD_RENAME, fd, rename_file_ext_cb, NULL);
461 if (ok) return ok; /* that's all for now, let's continue in callback */
464 ok = rename_file(fd->change->source, fd->change->dest);
468 file_data_do_change(fd);
469 file_maint_renamed(fd);
472 file_data_change_info_free(NULL, fd);
482 static FileDataMult *file_data_multiple_new(GList *source_list, const gchar *dest, gint copy)
484 FileDataMult *fdm = g_new0(FileDataMult, 1);
485 fdm->confirm_all = FALSE;
486 fdm->confirmed = FALSE;
488 fdm->source_list = source_list;
489 fdm->source_next = fdm->source_list;
490 fdm->dest_base = g_strdup(dest);
491 fdm->source_fd = NULL;
497 static void file_data_multiple_free(FileDataMult *fdm)
499 filelist_free(fdm->source_list);
500 g_free(fdm->dest_base);
505 static void file_util_move_multiple(FileDataMult *fdm);
507 static void file_util_move_multiple_ok_cb(GenericDialog *gd, gpointer data)
509 FileDataMult *fdm = data;
511 fdm->confirmed = TRUE;
513 if (fdm->rename_auto)
517 buf = unique_filename_simple(fdm->dest);
525 /* unique failed? well, return to the overwrite prompt :( */
526 fdm->confirmed = FALSE;
529 else if (fdm->rename)
533 name = gtk_entry_get_text(GTK_ENTRY(fdm->rename_entry));
534 if (strlen(name) == 0 ||
535 strcmp(name, fdm->source_fd->name) == 0)
537 fdm->confirmed = FALSE;
542 fdm->dest = concat_dir_and_file(fdm->dest_base, name);
543 fdm->confirmed = !isname(fdm->dest);
547 file_util_move_multiple(fdm);
550 static void file_util_move_multiple_all_cb(GenericDialog *gd, gpointer data)
552 FileDataMult *fdm = data;
554 fdm->confirm_all = TRUE;
556 if (fdm->rename_auto) fdm->rename_all = TRUE;
558 file_util_move_multiple(fdm);
561 static void file_util_move_multiple_skip_cb(GenericDialog *gd, gpointer data)
563 FileDataMult *fdm = data;
566 fdm->confirmed = TRUE;
568 file_util_move_multiple(fdm);
571 static void file_util_move_multiple_skip_all_cb(GenericDialog *gd, gpointer data)
573 FileDataMult *fdm = data;
576 fdm->confirm_all = TRUE;
577 file_util_move_multiple(fdm);
580 static void file_util_move_multiple_continue_cb(GenericDialog *gd, gpointer data)
582 FileDataMult *fdm = data;
584 fdm->confirmed = TRUE;
585 file_util_move_multiple(fdm);
588 static void file_util_move_multiple_cancel_cb(GenericDialog *gd, gpointer data)
590 FileDataMult *fdm = data;
592 file_data_multiple_free(fdm);
597 static void file_util_move_multiple_rename_auto_cb(GtkWidget *widget, gpointer data)
599 GenericDialog *gd = data;
604 fdm->rename_auto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
605 gtk_widget_set_sensitive(fdm->rename_box, !fdm->rename_auto);
606 gtk_widget_set_sensitive(fdm->rename_entry, (!fdm->rename_auto && fdm->rename));
608 if (fdm->rename_auto)
612 preview = unique_filename_simple(fdm->dest);
613 if (preview) gtk_entry_set_text(GTK_ENTRY(fdm->rename_entry), filename_from_path(preview));
617 gtk_widget_set_sensitive(fdm->yes_all_button, (fdm->rename_auto || !fdm->rename));
620 static void file_util_move_multiple_rename_cb(GtkWidget *widget, gpointer data)
622 GenericDialog *gd = data;
627 fdm->rename = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
628 gtk_widget_set_sensitive(fdm->rename_entry, fdm->rename);
629 gtk_widget_set_sensitive(fdm->yes_all_button, !fdm->rename);
635 gtk_widget_grab_focus(fdm->rename_entry);
637 name = gtk_entry_get_text(GTK_ENTRY(fdm->rename_entry));
638 gtk_editable_select_region(GTK_EDITABLE(fdm->rename_entry), 0, filename_base_length(name));
642 static GenericDialog *file_util_move_multiple_confirm_dialog(FileDataMult *fdm)
647 gd = file_util_gen_dlg(_("Overwrite file"), "Geeqie", "dlg_confirm",
649 file_util_move_multiple_cancel_cb, fdm);
651 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
652 _("Overwrite file?"),
653 _("Replace existing file with new file."));
654 pref_spacer(gd->vbox, 0);
656 generic_dialog_add_button(gd, GTK_STOCK_YES, _("_Overwrite"), file_util_move_multiple_ok_cb, TRUE);
657 fdm->yes_all_button = generic_dialog_add_button(gd, NULL, _("Overwrite _all"),
658 file_util_move_multiple_all_cb, FALSE);
659 generic_dialog_add_button(gd, GTK_STOCK_GOTO_LAST, _("S_kip all"), file_util_move_multiple_skip_all_cb, FALSE);
660 generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("_Skip"), file_util_move_multiple_skip_cb, FALSE);
661 generic_dialog_add_image(gd, NULL, file_data_new_simple(fdm->dest), _("Existing file"), fdm->source_fd, _("New file"), TRUE);
666 fdm->rename_all = FALSE;
667 fdm->rename_auto = FALSE;
669 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
671 fdm->rename_auto_box = gtk_check_button_new_with_label(_("Auto rename"));
672 g_signal_connect(G_OBJECT(fdm->rename_auto_box), "clicked",
673 G_CALLBACK(file_util_move_multiple_rename_auto_cb), gd);
674 gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_auto_box, FALSE, FALSE, 0);
675 gtk_widget_show(fdm->rename_auto_box);
677 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
679 fdm->rename_box = gtk_check_button_new_with_label(_("Rename"));
680 g_signal_connect(G_OBJECT(fdm->rename_box), "clicked",
681 G_CALLBACK(file_util_move_multiple_rename_cb), gd);
682 gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_box, FALSE, FALSE, 0);
683 gtk_widget_show(fdm->rename_box);
685 fdm->rename_entry = gtk_entry_new();
686 gtk_entry_set_text(GTK_ENTRY(fdm->rename_entry), filename_from_path(fdm->dest));
687 gtk_widget_set_sensitive(fdm->rename_entry, FALSE);
688 gtk_box_pack_start(GTK_BOX(hbox), fdm->rename_entry, TRUE, TRUE, 0);
689 gtk_widget_show(fdm->rename_entry);
694 static void file_util_move_multiple(FileDataMult *fdm)
696 while (fdm->dest || fdm->source_next)
698 gint success = FALSE;
699 gint skip_file = FALSE;
703 GList *work = fdm->source_next;
704 fdm->source_fd = work->data;
705 fdm->dest = concat_dir_and_file(fdm->dest_base, fdm->source_fd->name);
706 fdm->source_next = work->next;
707 fdm->confirmed = FALSE;
710 if (fdm->dest && fdm->source_fd && strcmp(fdm->dest, fdm->source_fd->name) == 0)
720 title = _("Source to copy matches destination");
721 text = g_strdup_printf(_("Unable to copy file:\n%s\nto itself."), fdm->dest);
725 title = _("Source to move matches destination");
726 text = g_strdup_printf(_("Unable to move file:\n%s\nto itself."), fdm->dest);
729 gd = file_util_gen_dlg(title, "Geeqie", "dlg_confirm",
731 file_util_move_multiple_cancel_cb, fdm);
732 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING, title, text);
734 generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
735 file_util_move_multiple_continue_cb, TRUE);
737 gtk_widget_show(gd->dialog);
742 else if (isfile(fdm->dest))
744 if (!fdm->confirmed && !fdm->confirm_all)
748 gd = file_util_move_multiple_confirm_dialog(fdm);
749 gtk_widget_show(gd->dialog);
752 if (fdm->skip) skip_file = TRUE;
758 if (!fdm->confirm_all) fdm->skip = FALSE;
764 if (fdm->confirm_all && fdm->rename_all && isfile(fdm->dest))
767 buf = unique_filename_simple(fdm->dest);
782 if (file_data_add_change_info(fdm->source_fd, FILEDATA_CHANGE_COPY, fdm->source_fd->path, fdm->dest) &&
783 copy_file_ext(fdm->source_fd))
790 if (file_data_add_change_info(fdm->source_fd, FILEDATA_CHANGE_MOVE, fdm->source_fd->path, fdm->dest) &&
791 move_file_ext(fdm->source_fd))
807 title = _("Error copying file");
808 text = g_strdup_printf(_("Unable to copy file:\n%s\nto:\n%s\nduring multiple file copy."), fdm->source_fd->path, fdm->dest);
812 title = _("Error moving file");
813 text = g_strdup_printf(_("Unable to move file:\n%s\nto:\n%s\nduring multiple file move."), fdm->source_fd->path, fdm->dest);
815 gd = file_util_gen_dlg(title, "Geeqie", "dlg_confirm",
817 file_util_move_multiple_cancel_cb, fdm);
818 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING, title, text);
821 generic_dialog_add_button(gd, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
822 file_util_move_multiple_continue_cb, TRUE);
823 gtk_widget_show(gd->dialog);
829 if (!success) return;
832 if (fdm->source_list)
833 file_util_do_move_list(fdm->source_list, fdm->copy);
836 GList *list = g_list_append(NULL, file_data_ref(fdm->source_fd));
837 file_util_do_move_list(list, fdm->copy);
841 file_data_multiple_free(fdm);
848 static FileDataSingle *file_data_single_new(FileData *source_fd, const gchar *dest, gint copy)
850 FileDataSingle *fds = g_new0(FileDataSingle, 1);
851 fds->confirmed = FALSE;
852 fds->source_fd = file_data_ref(source_fd);
853 fds->dest = g_strdup(dest);
858 static void file_data_single_free(FileDataSingle *fds)
860 file_data_unref(fds->source_fd);
865 static void file_util_move_single(FileDataSingle *fds);
867 static void file_util_move_single_ok_cb(GenericDialog *gd, gpointer data)
869 FileDataSingle *fds = data;
871 fds->confirmed = TRUE;
873 if (fds->rename_auto)
877 buf = unique_filename_simple(fds->dest);
885 /* unique failed? well, return to the overwrite prompt :( */
886 fds->confirmed = FALSE;
889 else if (fds->rename)
893 name = gtk_entry_get_text(GTK_ENTRY(fds->rename_entry));
894 if (strlen(name) == 0 ||
895 strcmp(name, fds->source_fd->name) == 0)
897 fds->confirmed = FALSE;
903 base = remove_level_from_path(fds->dest);
905 fds->dest = concat_dir_and_file(base, name);
906 fds->confirmed = !isname(fds->dest);
912 file_util_move_single(fds);
915 static void file_util_move_single_cancel_cb(GenericDialog *gd, gpointer data)
917 FileDataSingle *fds = data;
919 file_data_single_free(fds);
922 static void file_util_move_single_rename_auto_cb(GtkWidget *widget, gpointer data)
924 GenericDialog *gd = data;
929 fds->rename_auto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
930 gtk_widget_set_sensitive(fds->rename_box, !fds->rename_auto);
931 gtk_widget_set_sensitive(fds->rename_entry, (!fds->rename_auto && fds->rename));
933 if (fds->rename_auto)
937 preview = unique_filename_simple(fds->dest);
938 if (preview) gtk_entry_set_text(GTK_ENTRY(fds->rename_entry), filename_from_path(preview));
943 static void file_util_move_single_rename_cb(GtkWidget *widget, gpointer data)
945 GenericDialog *gd = data;
950 fds->rename = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
951 gtk_widget_set_sensitive(fds->rename_entry, fds->rename);
957 gtk_widget_grab_focus(fds->rename_entry);
959 name = gtk_entry_get_text(GTK_ENTRY(fds->rename_entry));
960 gtk_editable_select_region(GTK_EDITABLE(fds->rename_entry), 0, filename_base_length(name));
964 static void file_util_move_single(FileDataSingle *fds)
966 if (fds->dest && fds->source_fd && strcmp(fds->dest, fds->source_fd->name) == 0)
968 file_util_warning_dialog(_("Source matches destination"),
969 _("Source and destination are the same, operation cancelled."),
970 GTK_STOCK_DIALOG_INFO, NULL);
972 else if (isfile(fds->dest) && !fds->confirmed)
977 gd = file_util_gen_dlg(_("Overwrite file"), "Geeqie", "dlg_confirm",
979 file_util_move_single_cancel_cb, fds);
981 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
982 _("Overwrite file?"),
983 _("Replace existing file with new file."));
984 pref_spacer(gd->vbox, 0);
986 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_move_single_ok_cb, TRUE);
987 generic_dialog_add_image(gd, NULL, file_data_new_simple(fds->dest), _("Existing file"), fds->source_fd, _("New file"), TRUE);
992 fds->rename_auto = FALSE;
994 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
996 fds->rename_auto_box = gtk_check_button_new_with_label(_("Auto rename"));
997 g_signal_connect(G_OBJECT(fds->rename_auto_box), "clicked",
998 G_CALLBACK(file_util_move_single_rename_auto_cb), gd);
999 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_auto_box, FALSE, FALSE, 0);
1000 gtk_widget_show(fds->rename_auto_box);
1002 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
1004 fds->rename_box = gtk_check_button_new_with_label(_("Rename"));
1005 g_signal_connect(G_OBJECT(fds->rename_box), "clicked",
1006 G_CALLBACK(file_util_move_single_rename_cb), gd);
1007 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_box, FALSE, FALSE, 0);
1008 gtk_widget_show(fds->rename_box);
1010 fds->rename_entry = gtk_entry_new();
1011 gtk_entry_set_text(GTK_ENTRY(fds->rename_entry), filename_from_path(fds->dest));
1012 gtk_widget_set_sensitive(fds->rename_entry, FALSE);
1013 gtk_box_pack_start(GTK_BOX(hbox), fds->rename_entry, TRUE, TRUE, 0);
1014 gtk_widget_show(fds->rename_entry);
1016 gtk_widget_show(gd->dialog);
1021 gint success = FALSE;
1024 if (file_data_add_change_info(fds->source_fd, FILEDATA_CHANGE_COPY, fds->source_fd->path, fds->dest) &&
1025 copy_file_ext(fds->source_fd))
1032 if (file_data_add_change_info(fds->source_fd, FILEDATA_CHANGE_MOVE, fds->source_fd->path, fds->dest) &&
1033 move_file_ext(fds->source_fd))
1044 title = _("Error copying file");
1045 text = g_strdup_printf(_("Unable to copy file:\n%s\nto:\n%s"), fds->source_fd->name, fds->dest);
1049 title = _("Error moving file");
1050 text = g_strdup_printf(_("Unable to move file:\n%s\nto:\n%s"), fds->source_fd->name, fds->dest);
1052 file_util_warning_dialog(title, text, GTK_STOCK_DIALOG_ERROR, NULL);
1057 file_data_single_free(fds);
1064 static void file_util_move_do(FileDialog *fdlg)
1066 file_dialog_sync_history(fdlg, TRUE);
1068 if (fdlg->multiple_files)
1070 file_util_move_multiple(file_data_multiple_new(fdlg->source_list, fdlg->dest_path, fdlg->type));
1071 fdlg->source_list = NULL;
1075 if (isdir(fdlg->dest_path))
1077 gchar *buf = concat_dir_and_file(fdlg->dest_path, fdlg->source_fd->name);
1078 gtk_entry_set_text(GTK_ENTRY(fdlg->entry), buf);
1081 file_util_move_single(file_data_single_new(fdlg->source_fd, fdlg->dest_path, fdlg->type));
1084 file_dialog_close(fdlg);
1087 static void file_util_move_check(FileDialog *fdlg)
1089 if (fdlg->dest_path && strcmp(fdlg->dest_path, "~") == 0)
1091 gtk_entry_set_text(GTK_ENTRY(fdlg->entry), homedir());
1094 if (fdlg->multiple_files && !isdir(fdlg->dest_path))
1096 if (isfile(fdlg->dest_path))
1098 file_util_warning_dialog(_("Invalid destination"),
1099 _("When operating with multiple files, please select\na folder, not a file."),
1100 GTK_STOCK_DIALOG_INFO, NULL);
1103 file_util_warning_dialog(_("Invalid folder"),
1104 _("Please select an existing folder."),
1105 GTK_STOCK_DIALOG_INFO, NULL);
1109 if (!fdlg->dest_path || fdlg->dest_path[0] != '/')
1111 if (fdlg->source_fd)
1116 base = remove_level_from_path(fdlg->source_fd->path);
1117 path = concat_dir_and_file(base, fdlg->dest_path);
1119 gtk_entry_set_text(GTK_ENTRY(fdlg->entry), path);
1127 file_util_move_do(fdlg);
1130 static void file_util_move_cb(FileDialog *fdlg, gpointer data)
1132 file_util_move_check(fdlg);
1135 static void file_util_move_cancel_cb(FileDialog *fdlg, gpointer data)
1137 file_dialog_close(fdlg);
1140 static void real_file_util_move(FileData *source_fd, GList *source_list,
1141 const gchar *dest_path, gint copy, GtkWidget *parent)
1145 FileData *fd = NULL;
1149 const gchar *op_text;
1150 const gchar *stock_id;
1152 if (!source_fd && !source_list) return;
1156 fd = file_data_ref(source_fd);
1159 else if (source_list->next)
1165 fd = file_data_ref(source_list->data);
1166 filelist_free(source_list);
1173 title = _("Copy - Geeqie");
1174 op_text = _("_Copy");
1177 text = _("Copy file");
1181 text = _("Copy multiple files");
1183 stock_id = GTK_STOCK_COPY;
1187 title = _("Move - Geeqie");
1188 op_text = _("_Move");
1191 text = _("Move file");
1195 text = _("Move multiple files");
1197 stock_id = GTK_STOCK_OK;
1200 fdlg = file_util_file_dlg(title, "Geeqie", "dlg_copymove", parent,
1201 file_util_move_cancel_cb, NULL);
1202 generic_dialog_add_message(GENERIC_DIALOG(fdlg), NULL, text, NULL);
1208 box = pref_box_new(GENERIC_DIALOG(fdlg)->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1209 pref_label_new(box, _("File name:"));
1210 pref_label_new(box, fd->name);
1213 label = pref_label_new(GENERIC_DIALOG(fdlg)->vbox, _("Choose the destination folder."));
1214 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1215 pref_spacer(GENERIC_DIALOG(fdlg)->vbox, 0);
1217 file_dialog_add_button(fdlg, stock_id, op_text, file_util_move_cb, TRUE);
1219 file_dialog_add_path_widgets(fdlg, NULL, dest_path, "move_copy", NULL, NULL);
1222 fdlg->source_fd = fd;
1223 fdlg->source_list = source_list;
1224 fdlg->multiple_files = multiple;
1226 gtk_widget_show(GENERIC_DIALOG(fdlg)->dialog);
1229 void file_util_move(FileData *source_fd, GList *source_list, const gchar *dest_path, GtkWidget *parent)
1231 real_file_util_move(source_fd, source_list, dest_path, FALSE, parent);
1234 void file_util_copy(FileData *source_fd, GList *source_list, const gchar *dest_path, GtkWidget *parent)
1236 real_file_util_move(source_fd, source_list, dest_path, TRUE, parent);
1239 void file_util_move_simple(GList *list, const gchar *dest_path)
1244 filelist_free(list);
1250 FileData *source_fd;
1253 source_fd = list->data;
1254 dest = concat_dir_and_file(dest_path, source_fd->name);
1256 file_util_move_single(file_data_single_new(source_fd, dest, FALSE));
1258 filelist_free(list);
1262 file_util_move_multiple(file_data_multiple_new(list, dest_path, FALSE));
1265 void file_util_copy_simple(GList *list, const gchar *dest_path)
1270 filelist_free(list);
1276 FileData *source_fd;
1279 source_fd = list->data;
1280 dest = concat_dir_and_file(dest_path, source_fd->name);
1282 file_util_move_single(file_data_single_new(source_fd, dest, TRUE));
1284 filelist_free(list);
1288 file_util_move_multiple(file_data_multiple_new(list, dest_path, TRUE));
1292 *--------------------------------------------------------------------------
1294 *--------------------------------------------------------------------------
1297 static gint file_util_safe_number(gint64 free_space)
1303 gint sorted = FALSE;
1304 gint warned = FALSE;
1306 if (!filelist_read(safe_delete_path, &list, NULL)) return 0;
1317 v = (gint)strtol(fd->name, NULL, 10);
1318 if (v >= n) n = v + 1;
1324 (free_space < 0 || total + free_space > (gint64)safe_delete_size * 1048576) )
1330 list = filelist_sort(list, SORT_NAME, TRUE);
1335 list = g_list_remove(list, fd);
1337 if (debug) printf("expunging from trash for space: %s\n", fd->name);
1338 if (!unlink_file(fd->path) && !warned)
1340 file_util_warning_dialog(_("Delete failed"),
1341 _("Unable to remove old file from trash folder"),
1342 GTK_STOCK_DIALOG_WARNING, NULL);
1346 file_data_unref(fd);
1349 filelist_free(list);
1354 void file_util_trash_clear(void)
1356 file_util_safe_number(-1);
1359 static gchar *file_util_safe_dest(const gchar *path)
1363 n = file_util_safe_number(filesize(path));
1364 return g_strdup_printf("%s/%06d_%s", safe_delete_path, n, filename_from_path(path));
1367 static void file_util_safe_del_toggle_cb(GtkWidget *button, gpointer data)
1369 safe_delete_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1372 static void file_util_safe_del_close_cb(GtkWidget *dialog, gpointer data)
1374 GenericDialog **gd = data;
1379 static gint file_util_unlink(FileData *fd)
1381 static GenericDialog *gd = NULL;
1382 gchar *result = NULL;
1383 gint success = TRUE;
1385 if (!isfile(fd->path)) return FALSE;
1388 if (!safe_delete_enable)
1390 return unlink_file(fd->path);
1393 if (!isdir(safe_delete_path))
1395 if (debug) printf("creating trash: %s\n", safe_delete_path);
1396 if (!safe_delete_path || !mkdir_utf8(safe_delete_path, 0755))
1398 result = _("Could not create folder");
1407 dest = file_util_safe_dest(fd->path);
1410 if (debug) printf("safe deleting %s to %s\n", fd->path, dest);
1411 success = move_file(fd->path, dest);
1418 if (!success && !access_file(fd->path, W_OK))
1420 result = _("Permission denied");
1430 buf = g_strdup_printf(_("Unable to access or create the trash folder.\n\"%s\""), safe_delete_path);
1431 gd = file_util_warning_dialog(result, buf, GTK_STOCK_DIALOG_WARNING, NULL);
1434 button = gtk_check_button_new_with_label(_("Turn off safe delete"));
1435 g_signal_connect(G_OBJECT(button), "toggled",
1436 G_CALLBACK(file_util_safe_del_toggle_cb), NULL);
1437 gtk_box_pack_start(GTK_BOX(gd->vbox), button, FALSE, FALSE, 0);
1438 gtk_widget_show(button);
1440 g_signal_connect(G_OBJECT(gd->dialog), "destroy",
1441 G_CALLBACK(file_util_safe_del_close_cb), &gd);
1447 static void box_append_safe_delete_status(GenericDialog *gd)
1452 buf = g_strdup_printf(_("Safe delete: %s"), (safe_delete_enable) ? _("on") : _("off"));
1453 label = pref_label_new(gd->vbox, buf);
1456 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1457 gtk_widget_set_sensitive(label, FALSE);
1461 *--------------------------------------------------------------------------
1463 *--------------------------------------------------------------------------
1467 * delete multiple files
1470 static void file_util_delete_multiple_ok_cb(GenericDialog *gd, gpointer data);
1471 static void file_util_delete_multiple_cancel_cb(GenericDialog *gd, gpointer data);
1473 static void file_util_delete_ext_ok_cb(GenericDialog *gd, gpointer data)
1475 editor_resume(data);
1478 static void file_util_delete_ext_cancel_cb(GenericDialog *gd, gpointer data)
1484 static gint file_util_delete_ext_cb(gpointer resume_data, gint flags, GList *list, gpointer data)
1486 gint ret = EDITOR_CB_CONTINUE;
1487 if ((flags & EDITOR_ERROR_MASK) && !(flags & EDITOR_ERROR_SKIPPED))
1489 GString *msg = g_string_new(editor_get_error_str(flags));
1490 g_string_append(msg,_("\nUnable to delete file by external command:\n"));
1494 FileData *fd = list->data;
1496 g_string_append(msg, fd->path);
1497 g_string_append(msg, "\n");
1502 g_string_append(msg, _("\n Continue multiple delete operation?"));
1503 d = file_util_gen_dlg(_("Delete failed"), "Geeqie", "dlg_confirm",
1505 file_util_delete_ext_cancel_cb, resume_data);
1507 generic_dialog_add_message(d, GTK_STOCK_DIALOG_WARNING, NULL, msg->str);
1509 generic_dialog_add_button(d, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
1510 file_util_delete_ext_ok_cb, TRUE);
1511 gtk_widget_show(d->dialog);
1512 ret = EDITOR_CB_SUSPEND;
1516 file_util_warning_dialog(_("Delete failed"), msg->str, GTK_STOCK_DIALOG_ERROR, NULL);
1518 g_string_free(msg, TRUE);
1525 FileData *fd = list->data;
1526 if (flags & EDITOR_ERROR_MASK)
1527 /* an error occured -> no change -> delete change info */
1528 file_data_change_info_free(NULL, fd);
1530 /* files were successfully deleted, call the maint functions and keep the change info forever */
1531 file_maint_removed(fd, list);
1538 static void file_util_delete_multiple_ok_cb(GenericDialog *gd, gpointer data)
1540 GList *source_list = data;
1541 GList *work = source_list;
1546 FileData *fd = work->data;
1547 if (fd->change) ok = FALSE; /* another operation in progress */
1553 file_util_warning_dialog(_("File deletion failed"), _("Another operation in progress.\n"), GTK_STOCK_DIALOG_ERROR, NULL);
1554 filelist_free(source_list);
1559 if (editor_command[CMD_DELETE])
1565 FileData *fd = work->data;
1566 file_data_add_change_info(fd, FILEDATA_CHANGE_DELETE, NULL, NULL);
1570 if ((flags = start_editor_from_filelist_full(CMD_DELETE, source_list, file_util_delete_ext_cb, NULL)))
1572 gchar *text = g_strdup_printf(_("%s\nUnable to delete files by external command.\n"), editor_get_error_str(flags));
1573 file_util_warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1577 filelist_free(source_list);
1584 FileData *fd = source_list->data;
1586 source_list = g_list_remove(source_list, fd);
1588 if (!file_util_unlink(fd))
1595 d = file_util_gen_dlg(_("Delete failed"), "Geeqie", "dlg_confirm",
1597 file_util_delete_multiple_cancel_cb, source_list);
1599 text = g_strdup_printf(_("Unable to delete file:\n %s\n Continue multiple delete operation?"), fd->path);
1600 generic_dialog_add_message(d, GTK_STOCK_DIALOG_WARNING, NULL, text);
1603 generic_dialog_add_button(d, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
1604 file_util_delete_multiple_ok_cb, TRUE);
1605 gtk_widget_show(d->dialog);
1611 text = g_strdup_printf(_("Unable to delete file:\n%s"), fd->path);
1612 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1615 file_data_unref(fd);
1620 file_maint_removed(fd, source_list);
1622 file_data_unref(fd);
1626 static void file_util_delete_multiple_cancel_cb(GenericDialog *gd, gpointer data)
1628 GList *source_list = data;
1630 filelist_free(source_list);
1633 static void file_util_delete_multiple_review_skip(GenericDialog *gd, gint next)
1635 GtkWidget *button_back;
1636 GtkWidget *button_next;
1637 GtkWidget *button_label;
1644 button_back = g_object_get_data(G_OBJECT(gd->dialog), "button_back");
1645 button_next = g_object_get_data(G_OBJECT(gd->dialog), "button_next");
1646 button_label = g_object_get_data(G_OBJECT(gd->dialog), "button_label");
1647 list_point = g_object_get_data(G_OBJECT(gd->dialog), "list_point");
1649 if (!list || !button_label) return;
1655 if (list_point->next) list_point = list_point->next;
1659 if (list_point->prev) list_point = list_point->prev;
1667 if (!list_point) return;
1669 fd = list_point->data;
1670 buf = g_strdup_printf(_("File %d of %d"),
1671 g_list_index(list, (gpointer)fd) + 1,
1672 g_list_length(list));
1673 gtk_label_set_text(GTK_LABEL(button_label), buf);
1676 gtk_widget_set_sensitive(button_back, (list_point->prev != NULL) );
1677 gtk_widget_set_sensitive(button_next, (list_point->next != NULL) );
1679 generic_dialog_image_set(gd, fd);
1681 g_object_set_data(G_OBJECT(gd->dialog), "list_point", list_point);
1684 static void file_util_delete_multiple_review_back(GtkWidget *button, gpointer data)
1686 GenericDialog *gd = data;
1688 file_util_delete_multiple_review_skip(gd, FALSE);
1691 static void file_util_delete_multiple_review_next(GtkWidget *button, gpointer data)
1693 GenericDialog *gd = data;
1695 file_util_delete_multiple_review_skip(gd, TRUE);
1698 static void file_util_delete_multiple_review_button_cb(ImageWindow *imd, gint button, guint32 time,
1699 gdouble x, gdouble y, guint state, gpointer data)
1703 file_util_delete_multiple_review_next(NULL, data);
1705 else if (button == 2 || button == 3)
1707 file_util_delete_multiple_review_back(NULL, data);
1711 static void file_util_delete_multiple_review_scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
1712 gdouble x, gdouble y, guint state, gpointer data)
1714 if (direction == GDK_SCROLL_UP)
1716 file_util_delete_multiple_review_back(NULL, data);
1718 else if (direction == GDK_SCROLL_DOWN)
1720 file_util_delete_multiple_review_next(NULL, data);
1724 static void file_util_delete_multiple(GList *source_list, GtkWidget *parent)
1726 if (!confirm_delete)
1728 file_util_delete_multiple_ok_cb(NULL, source_list);
1739 gd = file_util_gen_dlg(_("Delete files - Geeqie"),
1740 "Geeqie", "dlg_confirm", parent, TRUE,
1741 file_util_delete_multiple_cancel_cb, source_list);
1743 generic_dialog_add_message(gd, NULL, _("Delete multiple files"), NULL);
1745 generic_dialog_add_image(gd, NULL, NULL, NULL, NULL, NULL, TRUE);
1746 imd = g_object_get_data(G_OBJECT(gd->dialog), "img_image");
1747 image_set_button_func(imd, file_util_delete_multiple_review_button_cb, gd);
1748 image_set_scroll_func(imd, file_util_delete_multiple_review_scroll_cb, gd);
1750 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
1752 button = pref_button_new(hbox, GTK_STOCK_GO_BACK, NULL, TRUE,
1753 G_CALLBACK(file_util_delete_multiple_review_back), gd);
1754 gtk_widget_set_sensitive(button, FALSE);
1755 g_object_set_data(G_OBJECT(gd->dialog), "button_back", button);
1757 button = pref_button_new(hbox, GTK_STOCK_GO_FORWARD, NULL, TRUE,
1758 G_CALLBACK(file_util_delete_multiple_review_next), gd);
1759 g_object_set_data(G_OBJECT(gd->dialog), "button_next", button);
1761 buf = g_strdup_printf(_("Review %d files"), g_list_length(source_list) );
1762 label = pref_label_new(hbox, buf);
1764 g_object_set_data(G_OBJECT(gd->dialog), "button_label", label);
1766 box_append_safe_delete_status(gd);
1768 generic_dialog_add_button(gd, GTK_STOCK_DELETE, NULL, file_util_delete_multiple_ok_cb, TRUE);
1770 gtk_widget_show(gd->dialog);
1775 * delete single file
1778 static void file_util_delete_ok_cb(GenericDialog *gd, gpointer data)
1780 FileData *fd = data;
1782 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_DELETE, NULL, NULL))
1784 file_util_warning_dialog(_("File deletion failed"), _("Another operation in progress.\n"), GTK_STOCK_DIALOG_ERROR, NULL);
1785 file_data_unref(fd);
1790 if (editor_command[CMD_DELETE])
1793 if ((flags = start_editor_from_file_full(CMD_DELETE, fd, file_util_delete_ext_cb, NULL)))
1795 gchar *text = g_strdup_printf(_("%s\nUnable to delete file by external command:\n%s"), editor_get_error_str(flags), fd->path);
1796 file_util_warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1798 file_data_change_info_free(NULL, fd);
1801 else if (!file_util_unlink(fd))
1803 gchar *text = g_strdup_printf(_("Unable to delete file:\n%s"), fd->path);
1804 file_util_warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1806 file_data_change_info_free(NULL, fd);
1810 file_maint_removed(fd, NULL);
1813 file_data_unref(fd);
1816 static void file_util_delete_cancel_cb(GenericDialog *gd, gpointer data)
1818 FileData *fd = data;
1820 file_data_unref(fd);
1823 static void file_util_delete_single(FileData *fd, GtkWidget *parent)
1825 if (!confirm_delete)
1827 file_util_delete_ok_cb(NULL, file_data_ref(fd));
1835 gd = file_util_gen_dlg(_("Delete file - Geeqie"), "Geeqie", "dlg_confirm",
1837 file_util_delete_cancel_cb, file_data_ref(fd));
1839 generic_dialog_add_message(gd, NULL, _("Delete file?"), NULL);
1841 table = pref_table_new(gd->vbox, 2, 2, FALSE, FALSE);
1843 pref_table_label(table, 0, 0, _("File name:"), 1.0);
1844 pref_table_label(table, 1, 0, fd->name, 0.0);
1846 pref_table_label(table, 0, 1, _("Location:"), 1.0);
1848 base = remove_level_from_path(fd->path);
1849 pref_table_label(table, 1, 1, base, 0.0);
1852 generic_dialog_add_image(gd, NULL, fd, NULL, NULL, NULL, FALSE);
1854 box_append_safe_delete_status(gd);
1856 generic_dialog_add_button(gd, GTK_STOCK_DELETE, NULL, file_util_delete_ok_cb, TRUE);
1858 gtk_widget_show(gd->dialog);
1862 void file_util_delete(FileData *source_fd, GList *source_list, GtkWidget *parent)
1864 if (!source_fd && !source_list) return;
1868 file_util_delete_single(source_fd, parent);
1870 else if (!source_list->next)
1872 file_util_delete_single(source_list->data, parent);
1873 filelist_free(source_list);
1877 file_util_delete_multiple(source_list, parent);
1882 *--------------------------------------------------------------------------
1884 *--------------------------------------------------------------------------
1888 * rename multiple files
1892 RENAME_COLUMN_FD = 0,
1895 RENAME_COLUMN_PREVIEW,
1900 RENAME_TYPE_MANUAL = 0,
1901 RENAME_TYPE_FORMATTED,
1905 typedef struct _RenameDataMult RenameDataMult;
1906 struct _RenameDataMult
1910 RenameType rename_type;
1912 GtkWidget *listview;
1913 GtkWidget *combo_type;
1915 GtkWidget *rename_box;
1916 GtkWidget *rename_label;
1917 GtkWidget *rename_entry;
1919 GtkWidget *auto_box;
1920 GtkWidget *auto_entry_front;
1921 GtkWidget *auto_spin_start;
1922 GtkWidget *auto_spin_pad;
1923 GtkWidget *auto_entry_end;
1925 GtkWidget *format_box;
1926 GtkWidget *format_entry;
1927 GtkWidget *format_spin;
1931 gint update_idle_id;
1934 static void file_util_rename_multiple(RenameDataMult *rd);
1936 static void file_util_rename_multiple_ok_cb(GenericDialog *gd, gpointer data)
1938 RenameDataMult *rd = data;
1941 dialog = GENERIC_DIALOG(rd->fdlg)->dialog;
1942 if (!GTK_WIDGET_VISIBLE(dialog)) gtk_widget_show(dialog);
1944 rd->fdlg->type = TRUE;
1945 file_util_rename_multiple(rd);
1948 static void file_util_rename_multiple_cancel_cb(GenericDialog *gd, gpointer data)
1950 RenameDataMult *rd = data;
1953 dialog = GENERIC_DIALOG(rd->fdlg)->dialog;
1954 if (!GTK_WIDGET_VISIBLE(dialog)) gtk_widget_show(dialog);
1957 static gint file_util_rename_multiple_find_row(RenameDataMult *rd, FileData *fd, GtkTreeIter *iter)
1959 GtkTreeModel *store;
1963 store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1964 valid = gtk_tree_model_get_iter_first(store, iter);
1970 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, RENAME_COLUMN_FD, &fd_n, -1);
1972 // file_data_unref(fd_n);
1973 if (ret) return row;
1975 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
1982 static void file_util_rename_multiple(RenameDataMult *rd)
1988 if (isfile(fdlg->dest_path) && !fdlg->type)
1992 gd = file_util_gen_dlg(_("Overwrite file"), "Geeqie", "dlg_confirm",
1994 file_util_rename_multiple_cancel_cb, rd);
1996 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
1997 _("Overwrite file?"),
1998 _("Replace existing file by renaming new file."));
1999 pref_spacer(gd->vbox, 0);
2001 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_rename_multiple_ok_cb, TRUE);
2002 generic_dialog_add_image(gd, NULL,
2003 file_data_new_simple(fdlg->dest_path), _("Existing file"),
2004 fdlg->source_fd, _("New file"), TRUE);
2006 gtk_widget_hide(GENERIC_DIALOG(fdlg)->dialog);
2008 gtk_widget_show(gd->dialog);
2013 if (!file_data_add_change_info(fdlg->source_fd, FILEDATA_CHANGE_RENAME, fdlg->source_fd->path, fdlg->dest_path) ||
2014 !rename_file_ext(fdlg->source_fd))
2016 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\n to:\n%s"),
2017 fdlg->source_fd->name,
2018 filename_from_path(fdlg->dest_path));
2019 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2024 GtkTreeModel *store;
2029 store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
2030 row = file_util_rename_multiple_find_row(rd, rd->fdlg->source_fd, &iter);
2033 (gtk_tree_model_iter_nth_child(store, &next, NULL, row + 1) ||
2034 (row > 0 && gtk_tree_model_iter_nth_child(store, &next, NULL, row - 1)) ) )
2036 GtkTreeSelection *selection;
2038 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2039 gtk_tree_selection_select_iter(selection, &next);
2040 gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
2044 if (debug) printf("closed by #%d\n", row);
2046 file_dialog_close(rd->fdlg);
2052 /* format: * = filename without extension, ## = number position, extension is kept */
2053 static gchar *file_util_rename_multiple_auto_format_name(const gchar *format, const gchar *name, gint n)
2064 if (!format || !name) return NULL;
2066 tmp = g_strdup(format);
2067 pad_start = strchr(tmp, '#');
2070 pad_end = pad_start;
2072 while (*pad_end == '#')
2079 parsed = g_strdup_printf("%s%0*d%s", tmp, padding, n, pad_end);
2087 ext = extension_from_path(name);
2089 middle = strchr(parsed, '*');
2097 base = remove_extension_from_path(name);
2098 new_name = g_strconcat(parsed, base, middle, ext, NULL);
2103 new_name = g_strconcat(parsed, ext, NULL);
2111 static void file_util_rename_multiple_auto(RenameDataMult *rd)
2115 const gchar *format;
2119 GtkTreeModel *store;
2124 front = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_front));
2125 end = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_end));
2126 padding = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_pad));
2128 format = gtk_entry_get_text(GTK_ENTRY(rd->format_entry));
2130 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2132 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->format_spin));
2135 (strchr(format, '*') == NULL && strchr(format, '#') == NULL))
2137 file_util_warning_dialog(_("Auto rename"),
2138 _("Format must include at least one of the symbol characters '*' or '#'.\n"),
2139 GTK_STOCK_DIALOG_WARNING, NULL);
2143 history_combo_append_history(rd->format_entry, NULL);
2147 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_start));
2149 history_combo_append_history(rd->auto_entry_front, NULL);
2150 history_combo_append_history(rd->auto_entry_end, NULL);
2153 store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
2155 /* first check for name conflicts */
2158 valid = gtk_tree_model_get_iter_first(store, &iter);
2159 while (valid && success)
2165 gtk_tree_model_get(store, &iter, RENAME_COLUMN_FD, &fd, -1);
2166 base = remove_level_from_path(fd->path);
2168 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2172 new_name = file_util_rename_multiple_auto_format_name(format, fd->name, n);
2173 dest = g_strconcat(base, "/", new_name, NULL);
2178 dest = g_strdup_printf("%s/%s%0*d%s", base, front, padding, n, end);
2181 if (isname(dest)) success = FALSE;
2185 // file_data_unref(fd);
2188 valid = gtk_tree_model_iter_next(store, &iter);
2193 file_util_warning_dialog(_("Auto rename"),
2194 _("Can not auto rename with the selected\nnumber set, one or more files exist that\nmatch the resulting name list.\n"),
2195 GTK_STOCK_DIALOG_WARNING, NULL);
2199 /* select the first iter, so that on fail the correct info is given to user */
2200 if (gtk_tree_model_get_iter_first(store, &iter))
2202 GtkTreeSelection *selection;
2204 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2205 gtk_tree_selection_select_iter(selection, &iter);
2208 /* now do it for real */
2211 while (success && gtk_tree_model_get_iter_first(store, &iter))
2217 gtk_tree_model_get(store, &iter, RENAME_COLUMN_FD, &fd, -1);
2218 base = remove_level_from_path(fd->path);
2220 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2224 new_name = file_util_rename_multiple_auto_format_name(format, fd->name, n);
2225 dest = g_strconcat(base, "/", new_name, NULL);
2230 dest = g_strdup_printf("%s/%s%0*d%s", base, front, padding, n, end);
2233 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_RENAME, fd->path, dest) ||
2234 !rename_file_ext(fd))
2241 // file_data_unref(fd);
2245 gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
2246 if (gtk_tree_model_get_iter_first(store, &iter))
2248 GtkTreeSelection *selection;
2250 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2251 gtk_tree_selection_select_iter(selection, &iter);
2263 gtk_spin_button_set_value(GTK_SPIN_BUTTON(rd->auto_spin_start), (gdouble)n);
2265 buf = g_strdup_printf(_("Failed to rename\n%s\nThe number was %d."), rd->fdlg->source_fd->name, n);
2266 file_util_warning_dialog(_("Auto rename"), buf, GTK_STOCK_DIALOG_ERROR, NULL);
2272 file_dialog_close(rd->fdlg);
2275 static void file_util_rename_multiple_cb(FileDialog *fdlg, gpointer data)
2277 RenameDataMult *rd = data;
2281 if (rd->rename_type != RENAME_TYPE_MANUAL)
2283 file_util_rename_multiple_auto(rd);
2287 name = gtk_entry_get_text(GTK_ENTRY(rd->rename_entry));
2288 base = remove_level_from_path(fdlg->source_fd->path);
2290 g_free(fdlg->dest_path);
2291 fdlg->dest_path = concat_dir_and_file(base, name);
2294 if (strlen(name) == 0 || strcmp(fdlg->source_fd->path, fdlg->dest_path) == 0)
2300 file_util_rename_multiple(rd);
2303 static void file_util_rename_multiple_close_cb(FileDialog *fdlg, gpointer data)
2305 RenameDataMult *rd = data;
2307 file_dialog_close(rd->fdlg);
2310 static gboolean file_util_rename_multiple_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
2311 gboolean path_currently_selected, gpointer data)
2313 RenameDataMult *rd = data;
2316 FileData *fd = NULL;
2318 if (path_currently_selected ||
2319 !gtk_tree_model_get_iter(store, &iter, tpath)) return TRUE;
2320 gtk_tree_model_get(store, &iter, RENAME_COLUMN_FD, &fd, -1);
2322 file_data_unref(rd->fdlg->source_fd);
2323 rd->fdlg->source_fd = file_data_ref(fd);
2325 name = rd->fdlg->source_fd->name;
2326 gtk_label_set_text(GTK_LABEL(rd->rename_label), name);
2327 gtk_entry_set_text(GTK_ENTRY(rd->rename_entry), name);
2329 image_change_fd(rd->imd, rd->fdlg->source_fd, 0.0);
2331 if (GTK_WIDGET_VISIBLE(rd->rename_box))
2333 gtk_widget_grab_focus(rd->rename_entry);
2334 gtk_editable_select_region(GTK_EDITABLE(rd->rename_entry), 0, filename_base_length(name));
2340 static void file_util_rename_multiple_preview_update(RenameDataMult *rd)
2342 GtkTreeModel *store;
2346 const gchar *format;
2352 front = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_front));
2353 end = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_end));
2354 padding = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_pad));
2356 format = gtk_entry_get_text(GTK_ENTRY(rd->format_entry));
2358 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2360 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->format_spin));
2364 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_start));
2367 store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
2369 valid = gtk_tree_model_get_iter_first(store, &iter);
2374 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2378 gtk_tree_model_get(store, &iter, RENAME_COLUMN_FD, &fd, -1);
2379 dest = file_util_rename_multiple_auto_format_name(format, fd->name, n);
2380 // file_data_unref(fd);
2384 dest = g_strdup_printf("%s%0*d%s", front, padding, n, end);
2386 gtk_list_store_set(GTK_LIST_STORE(store), &iter, RENAME_COLUMN_PREVIEW, dest, -1);
2390 valid = gtk_tree_model_iter_next(store, &iter);
2395 static gboolean file_util_rename_multiple_idle_cb(gpointer data)
2397 RenameDataMult *rd = data;
2399 file_util_rename_multiple_preview_update(rd);
2401 rd->update_idle_id = -1;
2405 static void file_util_rename_multiple_preview_order_cb(GtkTreeModel *treemodel, GtkTreePath *tpath,
2406 GtkTreeIter *iter, gpointer data)
2408 RenameDataMult *rd = data;
2410 if (rd->rename_type != RENAME_TYPE_MANUAL && rd->update_idle_id == -1)
2412 rd->update_idle_id = g_idle_add(file_util_rename_multiple_idle_cb, rd);
2416 static void file_util_rename_multiple_preview_entry_cb(GtkWidget *entry, gpointer data)
2418 RenameDataMult *rd = data;
2419 file_util_rename_multiple_preview_update(rd);
2422 static void file_util_rename_multiple_preview_adj_cb(GtkWidget *spin, gpointer data)
2424 RenameDataMult *rd = data;
2425 file_util_rename_multiple_preview_update(rd);
2428 static void file_util_rename_multiple_auto_change(GtkWidget *widget, gpointer data)
2430 RenameDataMult *rd = data;
2431 GtkTreeViewColumn *column;
2433 rd->rename_type = gtk_combo_box_get_active(GTK_COMBO_BOX(rd->combo_type));
2435 switch (rd->rename_type)
2437 case RENAME_TYPE_FORMATTED:
2438 if (GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_hide(rd->rename_box);
2439 if (GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_hide(rd->auto_box);
2440 if (!GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_show(rd->format_box);
2441 file_util_rename_multiple_preview_update(rd);
2443 case RENAME_TYPE_AUTO:
2444 if (GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_hide(rd->format_box);
2445 if (GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_hide(rd->rename_box);
2446 if (!GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_show(rd->auto_box);
2447 file_util_rename_multiple_preview_update(rd);
2449 case RENAME_TYPE_MANUAL:
2451 if (GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_hide(rd->format_box);
2452 if (GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_hide(rd->auto_box);
2453 if (!GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_show(rd->rename_box);
2457 column = gtk_tree_view_get_column(GTK_TREE_VIEW(rd->listview), RENAME_COLUMN_PREVIEW - 1);
2458 gtk_tree_view_column_set_visible(column, (rd->rename_type != RENAME_TYPE_MANUAL));
2461 static GtkWidget *furm_simple_vlabel(GtkWidget *box, const gchar *text, gint expand)
2466 vbox = gtk_vbox_new(FALSE, 0);
2467 gtk_box_pack_start(GTK_BOX(box), vbox, expand, expand, 0);
2468 gtk_widget_show(vbox);
2470 label = gtk_label_new(text);
2471 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
2472 gtk_widget_show(label);
2477 static GtkTreeViewColumn *file_util_rename_multiple_add_column(RenameDataMult *rd, const gchar *text, gint n)
2479 GtkTreeViewColumn *column;
2480 GtkCellRenderer *renderer;
2482 column = gtk_tree_view_column_new();
2483 gtk_tree_view_column_set_title(column, text);
2484 gtk_tree_view_column_set_min_width(column, 4);
2485 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2486 renderer = gtk_cell_renderer_text_new();
2487 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2488 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
2489 gtk_tree_view_append_column(GTK_TREE_VIEW(rd->listview), column);
2494 static void file_util_rename_multiple_destroy_cb(GtkWidget *widget, gpointer data)
2496 RenameDataMult *rd = data;
2498 if (rd->update_idle_id != -1) g_source_remove(rd->update_idle_id);
2503 static void file_util_rename_multiple_do(GList *source_list, GtkWidget *parent)
2507 GtkWidget *scrolled;
2508 GtkListStore *store;
2509 GtkTreeSelection *selection;
2510 GtkTreeViewColumn *column;
2519 rd = g_new0(RenameDataMult, 1);
2521 rd->fdlg = file_util_file_dlg(_("Rename - Geeqie"),
2522 "Geeqie", "dlg_rename", parent,
2523 file_util_rename_multiple_close_cb, rd);
2524 generic_dialog_add_message(GENERIC_DIALOG(rd->fdlg), NULL, _("Rename multiple files"), NULL);
2525 file_dialog_add_button(rd->fdlg, GTK_STOCK_OK, _("_Rename"), file_util_rename_multiple_cb, TRUE);
2527 rd->fdlg->source_fd = file_data_ref(source_list->data);
2528 rd->fdlg->dest_path = NULL;
2530 rd->rename_type = RENAME_TYPE_MANUAL;
2532 rd->update_idle_id = -1;
2534 vbox = GENERIC_DIALOG(rd->fdlg)->vbox;
2536 pane = gtk_hpaned_new();
2537 gtk_box_pack_start(GTK_BOX(vbox), pane, TRUE, TRUE, 0);
2538 gtk_widget_show(pane);
2540 scrolled = gtk_scrolled_window_new(NULL, NULL);
2541 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2542 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
2543 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2544 gtk_paned_pack1(GTK_PANED(pane), scrolled, TRUE, TRUE);
2545 gtk_widget_show(scrolled);
2547 store = gtk_list_store_new(4, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
2548 rd->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2549 g_object_unref(store);
2551 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(rd->listview), TRUE);
2552 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(rd->listview), FALSE);
2554 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2555 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2556 gtk_tree_selection_set_select_function(selection, file_util_rename_multiple_select_cb, rd, NULL);
2558 file_util_rename_multiple_add_column(rd, _("Original Name"), RENAME_COLUMN_NAME);
2559 column = file_util_rename_multiple_add_column(rd, _("Preview"), RENAME_COLUMN_PREVIEW);
2560 gtk_tree_view_column_set_visible(column, FALSE);
2562 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(rd->listview), TRUE);
2563 g_signal_connect(G_OBJECT(store), "row_changed",
2564 G_CALLBACK(file_util_rename_multiple_preview_order_cb), rd);
2565 gtk_widget_set_size_request(rd->listview, 250, 150);
2567 gtk_container_add(GTK_CONTAINER(scrolled), rd->listview);
2568 gtk_widget_show(rd->listview);
2573 FileData *fd = work->data;
2576 gtk_list_store_append(store, &iter);
2577 gtk_list_store_set(store, &iter, RENAME_COLUMN_FD, fd, RENAME_COLUMN_PATH, fd->path, RENAME_COLUMN_NAME, fd->name, -1);
2582 filelist_free(source_list);
2584 rd->imd = image_new(TRUE);
2585 g_object_set(G_OBJECT(rd->imd->pr), "zoom_expand", FALSE, NULL);
2586 gtk_widget_set_size_request(rd->imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
2587 gtk_paned_pack2(GTK_PANED(pane), rd->imd->widget, FALSE, TRUE);
2588 gtk_widget_show(rd->imd->widget);
2590 hbox = gtk_hbox_new(FALSE, 0);
2591 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2592 gtk_widget_show(hbox);
2594 rd->combo_type = gtk_combo_box_new_text();
2596 gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Manual rename"));
2597 gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Formatted rename"));
2598 gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Auto rename"));
2600 gtk_combo_box_set_active(GTK_COMBO_BOX(rd->combo_type), rd->rename_type);
2602 g_signal_connect(G_OBJECT(rd->combo_type), "changed",
2603 G_CALLBACK(file_util_rename_multiple_auto_change), rd);
2604 gtk_box_pack_end(GTK_BOX(hbox), rd->combo_type, FALSE, FALSE, 0);
2605 gtk_widget_show(rd->combo_type);
2607 rd->rename_box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, 0);
2608 table = pref_table_new(rd->rename_box, 2, 2, FALSE, FALSE);
2610 pref_table_label(table, 0, 0, _("Original name:"), 1.0);
2611 rd->rename_label = pref_table_label(table, 1, 0, rd->fdlg->source_fd->name, 0.0);
2613 pref_table_label(table, 0, 1, _("New name:"), 1.0);
2615 rd->rename_entry = gtk_entry_new();
2616 gtk_table_attach(GTK_TABLE(table), rd->rename_entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 0);
2617 generic_dialog_attach_default(GENERIC_DIALOG(rd->fdlg), rd->rename_entry);
2618 gtk_widget_grab_focus(rd->rename_entry);
2620 name = rd->fdlg->source_fd->name;
2621 gtk_entry_set_text(GTK_ENTRY(rd->rename_entry), name);
2622 gtk_editable_select_region(GTK_EDITABLE(rd->rename_entry), 0, filename_base_length(name));
2623 gtk_widget_show(rd->rename_entry);
2625 rd->auto_box = gtk_vbox_new(FALSE, PREF_PAD_GAP);
2626 gtk_box_pack_start(GTK_BOX(vbox), rd->auto_box, FALSE, FALSE, 0);
2627 /* do not show it here */
2629 hbox = pref_box_new(rd->auto_box, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
2631 box2 = furm_simple_vlabel(hbox, _("Begin text"), TRUE);
2633 combo = history_combo_new(&rd->auto_entry_front, "", "numerical_rename_prefix", -1);
2634 g_signal_connect(G_OBJECT(rd->auto_entry_front), "changed",
2635 G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2636 gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2637 gtk_widget_show(combo);
2639 box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
2641 rd->auto_spin_start = pref_spin_new(box2, NULL, NULL,
2642 0.0, 1000000.0, 1.0, 0, 1.0,
2643 G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2645 box2 = furm_simple_vlabel(hbox, _("End text"), TRUE);
2647 combo = history_combo_new(&rd->auto_entry_end, "", "numerical_rename_suffix", -1);
2648 g_signal_connect(G_OBJECT(rd->auto_entry_end), "changed",
2649 G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2650 gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2651 gtk_widget_show(combo);
2653 rd->auto_spin_pad = pref_spin_new(rd->auto_box, _("Padding:"), NULL,
2654 1.0, 8.0, 1.0, 0, 1.0,
2655 G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2657 rd->format_box = gtk_vbox_new(FALSE, PREF_PAD_GAP);
2658 gtk_box_pack_start(GTK_BOX(vbox), rd->format_box, FALSE, FALSE, 0);
2659 /* do not show it here */
2661 hbox = pref_box_new(rd->format_box, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
2663 box2 = furm_simple_vlabel(hbox, _("Format (* = original name, ## = numbers)"), TRUE);
2665 combo = history_combo_new(&rd->format_entry, "", "auto_rename_format", -1);
2666 g_signal_connect(G_OBJECT(rd->format_entry), "changed",
2667 G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2668 gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2669 gtk_widget_show(combo);
2671 box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
2673 rd->format_spin = pref_spin_new(box2, NULL, NULL,
2674 0.0, 1000000.0, 1.0, 0, 1.0,
2675 G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2677 image_change_fd(rd->imd, rd->fdlg->source_fd, 0.0);
2679 g_signal_connect(G_OBJECT(GENERIC_DIALOG(rd->fdlg)->dialog), "destroy",
2680 G_CALLBACK(file_util_rename_multiple_destroy_cb), rd);
2682 gtk_widget_show(GENERIC_DIALOG(rd->fdlg)->dialog);
2686 * rename single file
2689 static void file_util_rename_single(FileDataSingle *fds);
2691 static void file_util_rename_single_ok_cb(GenericDialog *gd, gpointer data)
2693 FileDataSingle *fds = data;
2694 fds->confirmed = TRUE;
2695 file_util_rename_single(fds);
2698 static void file_util_rename_single_cancel_cb(GenericDialog *gd, gpointer data)
2700 FileDataSingle *fds = data;
2701 file_data_single_free(fds);
2704 static void file_util_rename_single(FileDataSingle *fds)
2706 if (isfile(fds->dest) && !fds->confirmed)
2710 gd = file_util_gen_dlg(_("Overwrite file"), "Geeqie", "dlg_confirm",
2712 file_util_rename_single_cancel_cb, fds);
2714 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
2715 _("Overwrite file?"),
2716 _("Replace existing file by renaming new file."));
2717 pref_spacer(gd->vbox, 0);
2719 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_rename_single_ok_cb, TRUE);
2720 generic_dialog_add_image(gd, NULL,
2721 file_data_new_simple(fds->dest), _("Existing file"),
2722 fds->source_fd, _("New file"), TRUE);
2724 gtk_widget_show(gd->dialog);
2731 GList *list = g_list_append(NULL, file_data_ref(fds->source_fd));
2732 file_util_do_move_list(list, FALSE, TRUE);
2733 filelist_free(list);
2735 if (!file_data_add_change_info(fds->source_fd, FILEDATA_CHANGE_RENAME, fds->source_fd->path, fds->dest) ||
2736 !rename_file_ext(fds->source_fd))
2738 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), fds->source_fd->name, filename_from_path(fds->dest));
2739 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2744 file_data_single_free(fds);
2747 static void file_util_rename_single_cb(FileDialog *fdlg, gpointer data)
2752 name = gtk_entry_get_text(GTK_ENTRY(fdlg->entry));
2753 path = concat_dir_and_file(fdlg->dest_path, name);
2755 if (strlen(name) == 0 || strcmp(fdlg->source_fd->path, path) == 0)
2761 file_util_rename_single(file_data_single_new(fdlg->source_fd, path, fdlg->type));
2764 file_dialog_close(fdlg);
2767 static void file_util_rename_single_close_cb(FileDialog *fdlg, gpointer data)
2769 file_dialog_close(fdlg);
2772 static void file_util_rename_single_do(FileData *source_fd, GtkWidget *parent)
2778 fdlg = file_util_file_dlg(_("Rename - Geeqie"), "Geeqie", "dlg_rename", parent,
2779 file_util_rename_single_close_cb, NULL);
2781 generic_dialog_add_message(GENERIC_DIALOG(fdlg), NULL, _("Rename file"), NULL);
2782 generic_dialog_add_image(GENERIC_DIALOG(fdlg), NULL, source_fd, NULL, NULL, NULL, FALSE);
2784 file_dialog_add_button(fdlg, GTK_STOCK_OK, _("_Rename"), file_util_rename_single_cb, TRUE);
2786 fdlg->source_fd = file_data_ref(source_fd);
2787 fdlg->dest_path = remove_level_from_path(source_fd->path);
2789 table = pref_table_new(GENERIC_DIALOG(fdlg)->vbox, 2, 2, FALSE, FALSE);
2791 pref_table_label(table, 0, 0, _("Original name:"), 1.0);
2792 pref_table_label(table, 1, 0, fdlg->source_fd->name, 0.0);
2794 pref_table_label(table, 0, 1, _("New name:"), 1.0);
2796 fdlg->entry = gtk_entry_new();
2797 gtk_table_attach(GTK_TABLE(table), fdlg->entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 0);
2798 generic_dialog_attach_default(GENERIC_DIALOG(fdlg), fdlg->entry);
2799 gtk_widget_grab_focus(fdlg->entry);
2801 name = fdlg->source_fd->name;
2802 gtk_entry_set_text(GTK_ENTRY(fdlg->entry), name);
2803 gtk_editable_select_region(GTK_EDITABLE(fdlg->entry), 0, filename_base_length(name));
2804 gtk_widget_show(fdlg->entry);
2806 gtk_widget_show(GENERIC_DIALOG(fdlg)->dialog);
2809 void file_util_rename(FileData *source_fd, GList *source_list, GtkWidget *parent)
2811 if (!source_fd && !source_list) return;
2815 file_util_rename_single_do(source_fd, parent);
2817 else if (!source_list->next)
2819 file_util_rename_single_do(source_list->data, parent);
2820 filelist_free(source_list);
2824 file_util_rename_multiple_do(source_list, parent);
2829 *--------------------------------------------------------------------------
2830 * Create directory routines
2831 *--------------------------------------------------------------------------
2834 static void file_util_create_dir_do(const gchar *base, const gchar *name)
2838 path = concat_dir_and_file(base, name);
2842 gchar *text = g_strdup_printf(_("The folder:\n%s\nalready exists."), name);
2843 file_util_warning_dialog(_("Folder exists"), text, GTK_STOCK_DIALOG_INFO, NULL);
2846 else if (isname(path))
2848 gchar *text = g_strdup_printf(_("The path:\n%s\nalready exists as a file."), name);
2849 file_util_warning_dialog(_("Could not create folder"), text, GTK_STOCK_DIALOG_INFO, NULL);
2854 if (!mkdir_utf8(path, 0755))
2856 gchar *text = g_strdup_printf(_("Unable to create folder:\n%s"), name);
2857 file_util_warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2865 static void file_util_create_dir_cb(FileDialog *fdlg, gpointer data)
2869 name = gtk_entry_get_text(GTK_ENTRY(fdlg->entry));
2871 if (strlen(name) == 0) return;
2876 buf = remove_level_from_path(name);
2877 file_util_create_dir_do(buf, filename_from_path(name));
2882 file_util_create_dir_do(fdlg->dest_path, name);
2885 file_dialog_close(fdlg);
2888 static void file_util_create_dir_close_cb(FileDialog *fdlg, gpointer data)
2890 file_dialog_close(fdlg);
2893 void file_util_create_dir(const gchar *path, GtkWidget *parent)
2898 if (!isdir(path)) return;
2900 fdlg = file_util_file_dlg(_("New folder - Geeqie"), "Geeqie", "dlg_newdir", parent,
2901 file_util_create_dir_close_cb, NULL);
2903 text = g_strdup_printf(_("Create folder in:\n%s\nnamed:"), path);
2904 generic_dialog_add_message(GENERIC_DIALOG(fdlg), NULL, NULL, text);
2907 file_dialog_add_button(fdlg, GTK_STOCK_OK, NULL, file_util_create_dir_cb, TRUE);
2909 fdlg->dest_path = g_strdup(path);
2911 fdlg->entry = gtk_entry_new();
2912 gtk_box_pack_start(GTK_BOX(GENERIC_DIALOG(fdlg)->vbox), fdlg->entry, FALSE, FALSE, 0);
2913 generic_dialog_attach_default(GENERIC_DIALOG(fdlg), fdlg->entry);
2914 gtk_widget_grab_focus(fdlg->entry);
2915 gtk_widget_show(fdlg->entry);
2917 gtk_widget_show(GENERIC_DIALOG(fdlg)->dialog);
2920 gint file_util_rename_dir(FileData *old_fd, const gchar *new_path, GtkWidget *parent)
2922 const gchar *old_name;
2923 const gchar *new_name;
2925 if (!old_fd || !new_path || !isdir(old_fd->path)) return FALSE;
2927 old_name = old_fd->name;
2928 new_name = filename_from_path(new_path);
2930 if (isdir(new_path))
2932 gchar *text = g_strdup_printf(_("The folder:\n%s\nalready exists."), new_name);
2933 file_util_warning_dialog(_("Folder exists"), text, GTK_STOCK_DIALOG_INFO, parent);
2939 if (isname(new_path))
2941 gchar *text = g_strdup_printf(_("The path:\n%s\nalready exists as a file."), new_name);
2942 file_util_warning_dialog(_("Rename failed"), text, GTK_STOCK_DIALOG_INFO,parent);
2947 if (!file_data_add_change_info(old_fd, FILEDATA_CHANGE_RENAME, old_fd->path, new_path) ||
2948 !rename_file_ext(old_fd))
2950 gchar *text = g_strdup_printf(_("Failed to rename %s to %s."), old_name, new_name);
2951 file_util_warning_dialog(_("Rename failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
2961 *--------------------------------------------------------------------------
2962 * Delete directory routines
2963 *--------------------------------------------------------------------------
2966 /* The plan is to eventually make all of utilops.c
2967 * use UtilityData and UtilityType.
2968 * And clean up the above mess someday.
2972 UTILITY_TYPE_NONE = 0,
2975 UTILITY_TYPE_RENAME,
2976 UTILITY_TYPE_DELETE,
2977 UTILITY_TYPE_DELETE_LINK,
2978 UTILITY_TYPE_DELETE_FOLDER
2981 typedef struct _UtilityData UtilityData;
2982 struct _UtilityData {
2984 FileData *source_fd;
2992 UTILITY_COLUMN_FD = 0,
2993 UTILITY_COLUMN_PATH,
2994 UTILITY_COLUMN_NAME,
2995 UTILITY_COLUMN_COUNT
2999 #define UTILITY_LIST_MIN_WIDTH 250
3000 #define UTILITY_LIST_MIN_HEIGHT 150
3002 /* thumbnail spec has a max depth of 4 (.thumb??/fail/appname/??.png) */
3003 #define UTILITY_DELETE_MAX_DEPTH 5
3006 static void file_util_data_free(UtilityData *ud)
3010 file_data_unref(ud->source_fd);
3011 filelist_free(ud->dlist);
3012 filelist_free(ud->flist);
3014 if (ud->gd) generic_dialog_close(ud->gd);
3019 static GtkTreeViewColumn *file_util_dialog_add_list_column(GtkWidget *view, const gchar *text, gint n)
3021 GtkTreeViewColumn *column;
3022 GtkCellRenderer *renderer;
3024 column = gtk_tree_view_column_new();
3025 gtk_tree_view_column_set_title(column, text);
3026 gtk_tree_view_column_set_min_width(column, 4);
3027 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
3028 renderer = gtk_cell_renderer_text_new();
3029 gtk_tree_view_column_pack_start(column, renderer, TRUE);
3030 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
3031 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
3036 static GtkWidget *file_util_dialog_add_list(GtkWidget *box, GList *list, gint full_paths)
3038 GtkWidget *scrolled;
3040 GtkListStore *store;
3042 scrolled = gtk_scrolled_window_new(NULL, NULL);
3043 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3044 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
3045 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3046 gtk_box_pack_start(GTK_BOX(box), scrolled, TRUE, TRUE, 0);
3047 gtk_widget_show(scrolled);
3049 store = gtk_list_store_new(UTILITY_COLUMN_COUNT, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING);
3050 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
3051 g_object_unref(store);
3053 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
3054 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), FALSE);
3058 file_util_dialog_add_list_column(view, _("Location"), UTILITY_COLUMN_PATH);
3062 file_util_dialog_add_list_column(view, _("Name"), UTILITY_COLUMN_NAME);
3065 gtk_widget_set_size_request(view, UTILITY_LIST_MIN_WIDTH, UTILITY_LIST_MIN_HEIGHT);
3066 gtk_container_add(GTK_CONTAINER(scrolled), view);
3067 gtk_widget_show(view);
3071 FileData *fd = list->data;
3074 gtk_list_store_append(store, &iter);
3075 gtk_list_store_set(store, &iter, UTILITY_COLUMN_FD, fd,
3076 UTILITY_COLUMN_PATH, fd->path,
3077 UTILITY_COLUMN_NAME, fd->name, -1);
3085 static gboolean file_util_delete_dir_preview_cb(GtkTreeSelection *selection, GtkTreeModel *store,
3086 GtkTreePath *tpath, gboolean path_currently_selected,
3089 UtilityData *ud = data;
3091 FileData *fd = NULL;
3093 if (path_currently_selected ||
3094 !gtk_tree_model_get_iter(store, &iter, tpath)) return TRUE;
3096 gtk_tree_model_get(store, &iter, UTILITY_COLUMN_FD, &fd, -1);
3097 generic_dialog_image_set(ud->gd, fd);
3098 // file_data_unref(fd);
3103 static void file_util_delete_dir_cancel_cb(GenericDialog *gd, gpointer data)
3105 UtilityData *ud = data;
3108 file_util_data_free(ud);
3111 FileData *file_util_delete_dir_empty_path(FileData *fd, gint real_content, gint level)
3113 GList *dlist = NULL;
3114 GList *flist = NULL;
3116 FileData *fail = NULL;
3118 if (debug) printf("deltree into: %s\n", fd->path);
3121 if (level > UTILITY_DELETE_MAX_DEPTH)
3123 printf("folder recursion depth past %d, giving up\n", UTILITY_DELETE_MAX_DEPTH);
3124 return file_data_ref(fd);
3127 if (!filelist_read_lstat(fd->path, &flist, &dlist)) file_data_ref(fd);
3130 while (work && !fail)
3137 fail = file_util_delete_dir_empty_path(lfd, real_content, level);
3141 while (work && !fail)
3148 if (debug) printf("deltree child: %s\n", lfd->path);
3150 if (real_content && !islink(lfd->path))
3152 if (!file_util_unlink(lfd)) fail = file_data_ref(lfd);
3156 if (!unlink_file(lfd->path)) fail = file_data_ref(lfd);
3160 filelist_free(dlist);
3161 filelist_free(flist);
3163 if (!fail && !rmdir_utf8(fd->path))
3165 fail = file_data_ref(fd);
3168 if (debug) printf("deltree done: %s\n", fd->path);
3173 static void file_util_delete_dir_ok_cb(GenericDialog *gd, gpointer data)
3175 UtilityData *ud = data;
3179 if (ud->type == UTILITY_TYPE_DELETE_LINK)
3181 if (!unlink_file(ud->source_fd->path))
3185 text = g_strdup_printf("Unable to remove symbolic link:\n %s", ud->source_fd->path);
3186 file_util_warning_dialog(_("Delete failed"), text,
3187 GTK_STOCK_DIALOG_ERROR, NULL);
3193 FileData *fail = NULL;
3197 while (work && !fail)
3204 fail = file_util_delete_dir_empty_path(fd, FALSE, 0);
3208 while (work && !fail)
3215 if (debug) printf("deltree unlink: %s\n", fd->path);
3217 if (islink(fd->path))
3219 if (!unlink_file(fd->path)) fail = file_data_ref(fd);
3223 if (!file_util_unlink(fd)) fail = file_data_ref(fd);
3229 if (!rmdir_utf8(ud->source_fd->path)) fail = file_data_ref(ud->source_fd);
3236 text = g_strdup_printf(_("Unable to delete folder:\n\n%s"), ud->source_fd->path);
3237 gd = file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
3240 if (fail != ud->source_fd)
3242 pref_spacer(gd->vbox, PREF_PAD_GROUP);
3243 text = g_strdup_printf(_("Removal of folder contents failed at this file:\n\n%s"),
3245 pref_label_new(gd->vbox, text);
3249 file_data_unref(fail);
3253 file_util_data_free(ud);
3256 static GList *file_util_delete_dir_remaining_folders(GList *dlist)
3258 GList *rlist = NULL;
3265 dlist = dlist->next;
3268 (strcmp(fd->name, THUMB_FOLDER_GLOBAL) != 0 &&
3269 strcmp(fd->name, THUMB_FOLDER_LOCAL) != 0 &&
3270 strcmp(fd->name, GQVIEW_CACHE_LOCAL_METADATA) != 0) )
3272 rlist = g_list_prepend(rlist, fd);
3276 return g_list_reverse(rlist);
3279 void file_util_delete_dir(FileData *fd, GtkWidget *parent)
3281 GList *dlist = NULL;
3282 GList *flist = NULL;
3285 if (!isdir(fd->path)) return;
3287 if (islink(fd->path))
3292 ud = g_new0(UtilityData, 1);
3293 ud->type = UTILITY_TYPE_DELETE_LINK;
3294 ud->source_fd = file_data_ref(fd);
3298 ud->gd = file_util_gen_dlg(_("Delete folder"), "Geeqie", "dlg_confirm",
3300 file_util_delete_dir_cancel_cb, ud);
3302 text = g_strdup_printf(_("This will delete the symbolic link:\n\n%s\n\n"
3303 "The folder this link points to will not be deleted."),
3305 generic_dialog_add_message(ud->gd, GTK_STOCK_DIALOG_QUESTION,
3306 _("Delete symbolic link to folder?"),
3310 generic_dialog_add_button(ud->gd, GTK_STOCK_DELETE, NULL, file_util_delete_dir_ok_cb, TRUE);
3312 gtk_widget_show(ud->gd->dialog);
3317 if (!access_file(fd->path, W_OK | X_OK))
3321 text = g_strdup_printf(_("Unable to remove folder %s\n"
3322 "Permissions do not allow writing to the folder."), fd->path);
3323 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
3329 if (!filelist_read_lstat(fd->path, &flist, &dlist))
3333 text = g_strdup_printf(_("Unable to list contents of folder %s"), fd->path);
3334 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
3340 rlist = file_util_delete_dir_remaining_folders(dlist);
3347 gd = file_util_gen_dlg(_("Folder contains subfolders"), "Geeqie", "dlg_warning",
3348 parent, TRUE, NULL, NULL);
3349 generic_dialog_add_button(gd, GTK_STOCK_CLOSE, NULL, NULL, TRUE);
3351 text = g_strdup_printf(_("Unable to delete the folder:\n\n%s\n\n"
3352 "This folder contains subfolders which must be moved before it can be deleted."),
3354 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING,
3355 _("Folder contains subfolders"),
3359 box = pref_group_new(box, TRUE, _("Subfolders:"), GTK_ORIENTATION_VERTICAL);
3361 rlist = filelist_sort_path(rlist);
3362 file_util_dialog_add_list(box, rlist, FALSE);
3364 gtk_widget_show(gd->dialog);
3371 GtkTreeSelection *selection;
3374 ud = g_new0(UtilityData, 1);
3375 ud->type = UTILITY_TYPE_DELETE_FOLDER;
3376 ud->source_fd = file_data_ref(fd);
3379 ud->flist = filelist_sort_path(flist);
3382 ud->gd = file_util_gen_dlg(_("Delete folder"), "Geeqie", "dlg_confirm",
3383 parent, TRUE, file_util_delete_dir_cancel_cb, ud);
3384 generic_dialog_add_button(ud->gd, GTK_STOCK_DELETE, NULL, file_util_delete_dir_ok_cb, TRUE);
3386 text = g_strdup_printf(_("This will delete the folder:\n\n%s\n\n"
3387 "The contents of this folder will also be deleted."),
3389 box = generic_dialog_add_message(ud->gd, GTK_STOCK_DIALOG_QUESTION,
3390 _("Delete folder?"),
3394 box = pref_group_new(box, TRUE, _("Contents:"), GTK_ORIENTATION_HORIZONTAL);
3396 view = file_util_dialog_add_list(box, ud->flist, FALSE);
3397 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
3398 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3399 gtk_tree_selection_set_select_function(selection, file_util_delete_dir_preview_cb, ud, NULL);
3401 generic_dialog_add_image(ud->gd, box, NULL, NULL, NULL, NULL, FALSE);
3403 box_append_safe_delete_status(ud->gd);
3405 gtk_widget_show(ud->gd->dialog);
3409 filelist_free(dlist);
3410 filelist_free(flist);