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, GQ_WMCLASS, "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"), GQ_WMCLASS, "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, GQ_WMCLASS, "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, GQ_WMCLASS, "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"), GQ_WMCLASS, "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, GQ_WMCLASS, "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;
1323 while (safe_delete_size > 0 && list &&
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 if (editor_command[CMD_DELETE])
1454 buf = g_strdup(_("Deletion by external command"));
1458 if (safe_delete_enable)
1461 if (safe_delete_size > 0)
1462 buf2 = g_strdup_printf(_(" (max. %d MB)"), safe_delete_size);
1464 buf2 = g_strdup("");
1466 buf = g_strdup_printf(_("Safe delete: %s%s\nTrash: %s"), _("on"), buf2, safe_delete_path);
1471 buf = g_strdup_printf(_("Safe delete: %s"), _("off"));
1475 label = pref_label_new(gd->vbox, buf);
1478 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1479 gtk_widget_set_sensitive(label, FALSE);
1483 *--------------------------------------------------------------------------
1485 *--------------------------------------------------------------------------
1489 * delete multiple files
1492 static void file_util_delete_multiple_ok_cb(GenericDialog *gd, gpointer data);
1493 static void file_util_delete_multiple_cancel_cb(GenericDialog *gd, gpointer data);
1495 static void file_util_delete_ext_ok_cb(GenericDialog *gd, gpointer data)
1497 editor_resume(data);
1500 static void file_util_delete_ext_cancel_cb(GenericDialog *gd, gpointer data)
1506 static gint file_util_delete_ext_cb(gpointer resume_data, gint flags, GList *list, gpointer data)
1508 gint ret = EDITOR_CB_CONTINUE;
1509 if ((flags & EDITOR_ERROR_MASK) && !(flags & EDITOR_ERROR_SKIPPED))
1511 GString *msg = g_string_new(editor_get_error_str(flags));
1512 g_string_append(msg,_("\nUnable to delete file by external command:\n"));
1516 FileData *fd = list->data;
1518 g_string_append(msg, fd->path);
1519 g_string_append(msg, "\n");
1524 g_string_append(msg, _("\n Continue multiple delete operation?"));
1525 d = file_util_gen_dlg(_("Delete failed"), GQ_WMCLASS, "dlg_confirm",
1527 file_util_delete_ext_cancel_cb, resume_data);
1529 generic_dialog_add_message(d, GTK_STOCK_DIALOG_WARNING, NULL, msg->str);
1531 generic_dialog_add_button(d, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
1532 file_util_delete_ext_ok_cb, TRUE);
1533 gtk_widget_show(d->dialog);
1534 ret = EDITOR_CB_SUSPEND;
1538 file_util_warning_dialog(_("Delete failed"), msg->str, GTK_STOCK_DIALOG_ERROR, NULL);
1540 g_string_free(msg, TRUE);
1547 FileData *fd = list->data;
1548 if (flags & EDITOR_ERROR_MASK)
1549 /* an error occured -> no change -> delete change info */
1550 file_data_change_info_free(NULL, fd);
1552 /* files were successfully deleted, call the maint functions and keep the change info forever */
1553 file_maint_removed(fd, list);
1560 static void file_util_delete_multiple_ok_cb(GenericDialog *gd, gpointer data)
1562 GList *source_list = data;
1563 GList *work = source_list;
1568 FileData *fd = work->data;
1569 if (fd->change) ok = FALSE; /* another operation in progress */
1575 file_util_warning_dialog(_("File deletion failed"), _("Another operation in progress.\n"), GTK_STOCK_DIALOG_ERROR, NULL);
1576 filelist_free(source_list);
1581 if (editor_command[CMD_DELETE])
1587 FileData *fd = work->data;
1588 file_data_add_change_info(fd, FILEDATA_CHANGE_DELETE, NULL, NULL);
1592 if ((flags = start_editor_from_filelist_full(CMD_DELETE, source_list, file_util_delete_ext_cb, NULL)))
1594 gchar *text = g_strdup_printf(_("%s\nUnable to delete files by external command.\n"), editor_get_error_str(flags));
1595 file_util_warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1599 filelist_free(source_list);
1606 FileData *fd = source_list->data;
1608 source_list = g_list_remove(source_list, fd);
1610 if (!file_util_unlink(fd))
1617 d = file_util_gen_dlg(_("Delete failed"), GQ_WMCLASS, "dlg_confirm",
1619 file_util_delete_multiple_cancel_cb, source_list);
1621 text = g_strdup_printf(_("Unable to delete file:\n %s\n Continue multiple delete operation?"), fd->path);
1622 generic_dialog_add_message(d, GTK_STOCK_DIALOG_WARNING, NULL, text);
1625 generic_dialog_add_button(d, GTK_STOCK_GO_FORWARD, _("Co_ntinue"),
1626 file_util_delete_multiple_ok_cb, TRUE);
1627 gtk_widget_show(d->dialog);
1633 text = g_strdup_printf(_("Unable to delete file:\n%s"), fd->path);
1634 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1637 file_data_unref(fd);
1642 file_maint_removed(fd, source_list);
1644 file_data_unref(fd);
1648 static void file_util_delete_multiple_cancel_cb(GenericDialog *gd, gpointer data)
1650 GList *source_list = data;
1652 filelist_free(source_list);
1655 static void file_util_delete_multiple_review_skip(GenericDialog *gd, gint next)
1657 GtkWidget *button_back;
1658 GtkWidget *button_next;
1659 GtkWidget *button_label;
1666 button_back = g_object_get_data(G_OBJECT(gd->dialog), "button_back");
1667 button_next = g_object_get_data(G_OBJECT(gd->dialog), "button_next");
1668 button_label = g_object_get_data(G_OBJECT(gd->dialog), "button_label");
1669 list_point = g_object_get_data(G_OBJECT(gd->dialog), "list_point");
1671 if (!list || !button_label) return;
1677 if (list_point->next) list_point = list_point->next;
1681 if (list_point->prev) list_point = list_point->prev;
1689 if (!list_point) return;
1691 fd = list_point->data;
1692 buf = g_strdup_printf(_("File %d of %d"),
1693 g_list_index(list, (gpointer)fd) + 1,
1694 g_list_length(list));
1695 gtk_label_set_text(GTK_LABEL(button_label), buf);
1698 gtk_widget_set_sensitive(button_back, (list_point->prev != NULL) );
1699 gtk_widget_set_sensitive(button_next, (list_point->next != NULL) );
1701 generic_dialog_image_set(gd, fd);
1703 g_object_set_data(G_OBJECT(gd->dialog), "list_point", list_point);
1706 static void file_util_delete_multiple_review_back(GtkWidget *button, gpointer data)
1708 GenericDialog *gd = data;
1710 file_util_delete_multiple_review_skip(gd, FALSE);
1713 static void file_util_delete_multiple_review_next(GtkWidget *button, gpointer data)
1715 GenericDialog *gd = data;
1717 file_util_delete_multiple_review_skip(gd, TRUE);
1720 static void file_util_delete_multiple_review_button_cb(ImageWindow *imd, gint button, guint32 time,
1721 gdouble x, gdouble y, guint state, gpointer data)
1725 file_util_delete_multiple_review_next(NULL, data);
1727 else if (button == 2 || button == 3)
1729 file_util_delete_multiple_review_back(NULL, data);
1733 static void file_util_delete_multiple_review_scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
1734 gdouble x, gdouble y, guint state, gpointer data)
1736 if (direction == GDK_SCROLL_UP)
1738 file_util_delete_multiple_review_back(NULL, data);
1740 else if (direction == GDK_SCROLL_DOWN)
1742 file_util_delete_multiple_review_next(NULL, data);
1746 static void file_util_delete_multiple(GList *source_list, GtkWidget *parent)
1748 if (!confirm_delete)
1750 file_util_delete_multiple_ok_cb(NULL, source_list);
1761 gd = file_util_gen_dlg(_("Delete files - Geeqie"),
1762 GQ_WMCLASS, "dlg_confirm", parent, TRUE,
1763 file_util_delete_multiple_cancel_cb, source_list);
1765 generic_dialog_add_message(gd, NULL, _("Delete multiple files"), NULL);
1767 generic_dialog_add_image(gd, NULL, NULL, NULL, NULL, NULL, TRUE);
1768 imd = g_object_get_data(G_OBJECT(gd->dialog), "img_image");
1769 image_set_button_func(imd, file_util_delete_multiple_review_button_cb, gd);
1770 image_set_scroll_func(imd, file_util_delete_multiple_review_scroll_cb, gd);
1772 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
1774 button = pref_button_new(hbox, GTK_STOCK_GO_BACK, NULL, TRUE,
1775 G_CALLBACK(file_util_delete_multiple_review_back), gd);
1776 gtk_widget_set_sensitive(button, FALSE);
1777 g_object_set_data(G_OBJECT(gd->dialog), "button_back", button);
1779 button = pref_button_new(hbox, GTK_STOCK_GO_FORWARD, NULL, TRUE,
1780 G_CALLBACK(file_util_delete_multiple_review_next), gd);
1781 g_object_set_data(G_OBJECT(gd->dialog), "button_next", button);
1783 buf = g_strdup_printf(_("Review %d files"), g_list_length(source_list) );
1784 label = pref_label_new(hbox, buf);
1786 g_object_set_data(G_OBJECT(gd->dialog), "button_label", label);
1788 box_append_safe_delete_status(gd);
1790 generic_dialog_add_button(gd, GTK_STOCK_DELETE, NULL, file_util_delete_multiple_ok_cb, TRUE);
1792 gtk_widget_show(gd->dialog);
1797 * delete single file
1800 static void file_util_delete_ok_cb(GenericDialog *gd, gpointer data)
1802 FileData *fd = data;
1804 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_DELETE, NULL, NULL))
1806 file_util_warning_dialog(_("File deletion failed"), _("Another operation in progress.\n"), GTK_STOCK_DIALOG_ERROR, NULL);
1807 file_data_unref(fd);
1812 if (editor_command[CMD_DELETE])
1815 if ((flags = start_editor_from_file_full(CMD_DELETE, fd, file_util_delete_ext_cb, NULL)))
1817 gchar *text = g_strdup_printf(_("%s\nUnable to delete file by external command:\n%s"), editor_get_error_str(flags), fd->path);
1818 file_util_warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1820 file_data_change_info_free(NULL, fd);
1823 else if (!file_util_unlink(fd))
1825 gchar *text = g_strdup_printf(_("Unable to delete file:\n%s"), fd->path);
1826 file_util_warning_dialog(_("File deletion failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
1828 file_data_change_info_free(NULL, fd);
1832 file_maint_removed(fd, NULL);
1835 file_data_unref(fd);
1838 static void file_util_delete_cancel_cb(GenericDialog *gd, gpointer data)
1840 FileData *fd = data;
1842 file_data_unref(fd);
1845 static void file_util_delete_single(FileData *fd, GtkWidget *parent)
1847 if (!confirm_delete)
1849 file_util_delete_ok_cb(NULL, file_data_ref(fd));
1857 gd = file_util_gen_dlg(_("Delete file - Geeqie"), GQ_WMCLASS, "dlg_confirm",
1859 file_util_delete_cancel_cb, file_data_ref(fd));
1861 generic_dialog_add_message(gd, NULL, _("Delete file?"), NULL);
1863 table = pref_table_new(gd->vbox, 2, 2, FALSE, FALSE);
1865 pref_table_label(table, 0, 0, _("File name:"), 1.0);
1866 pref_table_label(table, 1, 0, fd->name, 0.0);
1868 pref_table_label(table, 0, 1, _("Location:"), 1.0);
1870 base = remove_level_from_path(fd->path);
1871 pref_table_label(table, 1, 1, base, 0.0);
1874 generic_dialog_add_image(gd, NULL, fd, NULL, NULL, NULL, FALSE);
1876 box_append_safe_delete_status(gd);
1878 generic_dialog_add_button(gd, GTK_STOCK_DELETE, NULL, file_util_delete_ok_cb, TRUE);
1880 gtk_widget_show(gd->dialog);
1884 void file_util_delete(FileData *source_fd, GList *source_list, GtkWidget *parent)
1886 if (!source_fd && !source_list) return;
1890 file_util_delete_single(source_fd, parent);
1892 else if (!source_list->next)
1894 file_util_delete_single(source_list->data, parent);
1895 filelist_free(source_list);
1899 file_util_delete_multiple(source_list, parent);
1904 *--------------------------------------------------------------------------
1906 *--------------------------------------------------------------------------
1910 * rename multiple files
1914 RENAME_COLUMN_FD = 0,
1917 RENAME_COLUMN_PREVIEW,
1922 RENAME_TYPE_MANUAL = 0,
1923 RENAME_TYPE_FORMATTED,
1927 typedef struct _RenameDataMult RenameDataMult;
1928 struct _RenameDataMult
1932 RenameType rename_type;
1934 GtkWidget *listview;
1935 GtkWidget *combo_type;
1937 GtkWidget *rename_box;
1938 GtkWidget *rename_label;
1939 GtkWidget *rename_entry;
1941 GtkWidget *auto_box;
1942 GtkWidget *auto_entry_front;
1943 GtkWidget *auto_spin_start;
1944 GtkWidget *auto_spin_pad;
1945 GtkWidget *auto_entry_end;
1947 GtkWidget *format_box;
1948 GtkWidget *format_entry;
1949 GtkWidget *format_spin;
1953 gint update_idle_id;
1956 static void file_util_rename_multiple(RenameDataMult *rd);
1958 static void file_util_rename_multiple_ok_cb(GenericDialog *gd, gpointer data)
1960 RenameDataMult *rd = data;
1963 dialog = GENERIC_DIALOG(rd->fdlg)->dialog;
1964 if (!GTK_WIDGET_VISIBLE(dialog)) gtk_widget_show(dialog);
1966 rd->fdlg->type = TRUE;
1967 file_util_rename_multiple(rd);
1970 static void file_util_rename_multiple_cancel_cb(GenericDialog *gd, gpointer data)
1972 RenameDataMult *rd = data;
1975 dialog = GENERIC_DIALOG(rd->fdlg)->dialog;
1976 if (!GTK_WIDGET_VISIBLE(dialog)) gtk_widget_show(dialog);
1979 static gint file_util_rename_multiple_find_row(RenameDataMult *rd, FileData *fd, GtkTreeIter *iter)
1981 GtkTreeModel *store;
1985 store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
1986 valid = gtk_tree_model_get_iter_first(store, iter);
1992 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, RENAME_COLUMN_FD, &fd_n, -1);
1994 // file_data_unref(fd_n);
1995 if (ret) return row;
1997 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
2004 static void file_util_rename_multiple(RenameDataMult *rd)
2010 if (isfile(fdlg->dest_path) && !fdlg->type)
2014 gd = file_util_gen_dlg(_("Overwrite file"), GQ_WMCLASS, "dlg_confirm",
2016 file_util_rename_multiple_cancel_cb, rd);
2018 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
2019 _("Overwrite file?"),
2020 _("Replace existing file by renaming new file."));
2021 pref_spacer(gd->vbox, 0);
2023 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_rename_multiple_ok_cb, TRUE);
2024 generic_dialog_add_image(gd, NULL,
2025 file_data_new_simple(fdlg->dest_path), _("Existing file"),
2026 fdlg->source_fd, _("New file"), TRUE);
2028 gtk_widget_hide(GENERIC_DIALOG(fdlg)->dialog);
2030 gtk_widget_show(gd->dialog);
2035 if (!file_data_add_change_info(fdlg->source_fd, FILEDATA_CHANGE_RENAME, fdlg->source_fd->path, fdlg->dest_path) ||
2036 !rename_file_ext(fdlg->source_fd))
2038 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\n to:\n%s"),
2039 fdlg->source_fd->name,
2040 filename_from_path(fdlg->dest_path));
2041 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2046 GtkTreeModel *store;
2051 store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
2052 row = file_util_rename_multiple_find_row(rd, rd->fdlg->source_fd, &iter);
2055 (gtk_tree_model_iter_nth_child(store, &next, NULL, row + 1) ||
2056 (row > 0 && gtk_tree_model_iter_nth_child(store, &next, NULL, row - 1)) ) )
2058 GtkTreeSelection *selection;
2060 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2061 gtk_tree_selection_select_iter(selection, &next);
2062 gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
2066 if (debug) printf("closed by #%d\n", row);
2068 file_dialog_close(rd->fdlg);
2074 /* format: * = filename without extension, ## = number position, extension is kept */
2075 static gchar *file_util_rename_multiple_auto_format_name(const gchar *format, const gchar *name, gint n)
2086 if (!format || !name) return NULL;
2088 tmp = g_strdup(format);
2089 pad_start = strchr(tmp, '#');
2092 pad_end = pad_start;
2094 while (*pad_end == '#')
2101 parsed = g_strdup_printf("%s%0*d%s", tmp, padding, n, pad_end);
2109 ext = extension_from_path(name);
2111 middle = strchr(parsed, '*');
2119 base = remove_extension_from_path(name);
2120 new_name = g_strconcat(parsed, base, middle, ext, NULL);
2125 new_name = g_strconcat(parsed, ext, NULL);
2133 static void file_util_rename_multiple_auto(RenameDataMult *rd)
2137 const gchar *format;
2141 GtkTreeModel *store;
2146 front = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_front));
2147 end = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_end));
2148 padding = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_pad));
2150 format = gtk_entry_get_text(GTK_ENTRY(rd->format_entry));
2152 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2154 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->format_spin));
2157 (strchr(format, '*') == NULL && strchr(format, '#') == NULL))
2159 file_util_warning_dialog(_("Auto rename"),
2160 _("Format must include at least one of the symbol characters '*' or '#'.\n"),
2161 GTK_STOCK_DIALOG_WARNING, NULL);
2165 history_combo_append_history(rd->format_entry, NULL);
2169 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_start));
2171 history_combo_append_history(rd->auto_entry_front, NULL);
2172 history_combo_append_history(rd->auto_entry_end, NULL);
2175 store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
2177 /* first check for name conflicts */
2180 valid = gtk_tree_model_get_iter_first(store, &iter);
2181 while (valid && success)
2187 gtk_tree_model_get(store, &iter, RENAME_COLUMN_FD, &fd, -1);
2188 base = remove_level_from_path(fd->path);
2190 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2194 new_name = file_util_rename_multiple_auto_format_name(format, fd->name, n);
2195 dest = g_strconcat(base, "/", new_name, NULL);
2200 dest = g_strdup_printf("%s/%s%0*d%s", base, front, padding, n, end);
2203 if (isname(dest)) success = FALSE;
2207 // file_data_unref(fd);
2210 valid = gtk_tree_model_iter_next(store, &iter);
2215 file_util_warning_dialog(_("Auto rename"),
2216 _("Can not auto rename with the selected\nnumber set, one or more files exist that\nmatch the resulting name list.\n"),
2217 GTK_STOCK_DIALOG_WARNING, NULL);
2221 /* select the first iter, so that on fail the correct info is given to user */
2222 if (gtk_tree_model_get_iter_first(store, &iter))
2224 GtkTreeSelection *selection;
2226 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2227 gtk_tree_selection_select_iter(selection, &iter);
2230 /* now do it for real */
2233 while (success && gtk_tree_model_get_iter_first(store, &iter))
2239 gtk_tree_model_get(store, &iter, RENAME_COLUMN_FD, &fd, -1);
2240 base = remove_level_from_path(fd->path);
2242 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2246 new_name = file_util_rename_multiple_auto_format_name(format, fd->name, n);
2247 dest = g_strconcat(base, "/", new_name, NULL);
2252 dest = g_strdup_printf("%s/%s%0*d%s", base, front, padding, n, end);
2255 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_RENAME, fd->path, dest) ||
2256 !rename_file_ext(fd))
2263 // file_data_unref(fd);
2267 gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
2268 if (gtk_tree_model_get_iter_first(store, &iter))
2270 GtkTreeSelection *selection;
2272 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2273 gtk_tree_selection_select_iter(selection, &iter);
2285 gtk_spin_button_set_value(GTK_SPIN_BUTTON(rd->auto_spin_start), (gdouble)n);
2287 buf = g_strdup_printf(_("Failed to rename\n%s\nThe number was %d."), rd->fdlg->source_fd->name, n);
2288 file_util_warning_dialog(_("Auto rename"), buf, GTK_STOCK_DIALOG_ERROR, NULL);
2294 file_dialog_close(rd->fdlg);
2297 static void file_util_rename_multiple_cb(FileDialog *fdlg, gpointer data)
2299 RenameDataMult *rd = data;
2303 if (rd->rename_type != RENAME_TYPE_MANUAL)
2305 file_util_rename_multiple_auto(rd);
2309 name = gtk_entry_get_text(GTK_ENTRY(rd->rename_entry));
2310 base = remove_level_from_path(fdlg->source_fd->path);
2312 g_free(fdlg->dest_path);
2313 fdlg->dest_path = concat_dir_and_file(base, name);
2316 if (strlen(name) == 0 || strcmp(fdlg->source_fd->path, fdlg->dest_path) == 0)
2322 file_util_rename_multiple(rd);
2325 static void file_util_rename_multiple_close_cb(FileDialog *fdlg, gpointer data)
2327 RenameDataMult *rd = data;
2329 file_dialog_close(rd->fdlg);
2332 static gboolean file_util_rename_multiple_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
2333 gboolean path_currently_selected, gpointer data)
2335 RenameDataMult *rd = data;
2338 FileData *fd = NULL;
2340 if (path_currently_selected ||
2341 !gtk_tree_model_get_iter(store, &iter, tpath)) return TRUE;
2342 gtk_tree_model_get(store, &iter, RENAME_COLUMN_FD, &fd, -1);
2344 file_data_unref(rd->fdlg->source_fd);
2345 rd->fdlg->source_fd = file_data_ref(fd);
2347 name = rd->fdlg->source_fd->name;
2348 gtk_label_set_text(GTK_LABEL(rd->rename_label), name);
2349 gtk_entry_set_text(GTK_ENTRY(rd->rename_entry), name);
2351 image_change_fd(rd->imd, rd->fdlg->source_fd, 0.0);
2353 if (GTK_WIDGET_VISIBLE(rd->rename_box))
2355 gtk_widget_grab_focus(rd->rename_entry);
2356 gtk_editable_select_region(GTK_EDITABLE(rd->rename_entry), 0, filename_base_length(name));
2362 static void file_util_rename_multiple_preview_update(RenameDataMult *rd)
2364 GtkTreeModel *store;
2368 const gchar *format;
2374 front = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_front));
2375 end = gtk_entry_get_text(GTK_ENTRY(rd->auto_entry_end));
2376 padding = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_pad));
2378 format = gtk_entry_get_text(GTK_ENTRY(rd->format_entry));
2380 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2382 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->format_spin));
2386 start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rd->auto_spin_start));
2389 store = gtk_tree_view_get_model(GTK_TREE_VIEW(rd->listview));
2391 valid = gtk_tree_model_get_iter_first(store, &iter);
2396 if (rd->rename_type == RENAME_TYPE_FORMATTED)
2400 gtk_tree_model_get(store, &iter, RENAME_COLUMN_FD, &fd, -1);
2401 dest = file_util_rename_multiple_auto_format_name(format, fd->name, n);
2402 // file_data_unref(fd);
2406 dest = g_strdup_printf("%s%0*d%s", front, padding, n, end);
2408 gtk_list_store_set(GTK_LIST_STORE(store), &iter, RENAME_COLUMN_PREVIEW, dest, -1);
2412 valid = gtk_tree_model_iter_next(store, &iter);
2417 static gboolean file_util_rename_multiple_idle_cb(gpointer data)
2419 RenameDataMult *rd = data;
2421 file_util_rename_multiple_preview_update(rd);
2423 rd->update_idle_id = -1;
2427 static void file_util_rename_multiple_preview_order_cb(GtkTreeModel *treemodel, GtkTreePath *tpath,
2428 GtkTreeIter *iter, gpointer data)
2430 RenameDataMult *rd = data;
2432 if (rd->rename_type != RENAME_TYPE_MANUAL && rd->update_idle_id == -1)
2434 rd->update_idle_id = g_idle_add(file_util_rename_multiple_idle_cb, rd);
2438 static void file_util_rename_multiple_preview_entry_cb(GtkWidget *entry, gpointer data)
2440 RenameDataMult *rd = data;
2441 file_util_rename_multiple_preview_update(rd);
2444 static void file_util_rename_multiple_preview_adj_cb(GtkWidget *spin, gpointer data)
2446 RenameDataMult *rd = data;
2447 file_util_rename_multiple_preview_update(rd);
2450 static void file_util_rename_multiple_auto_change(GtkWidget *widget, gpointer data)
2452 RenameDataMult *rd = data;
2453 GtkTreeViewColumn *column;
2455 rd->rename_type = gtk_combo_box_get_active(GTK_COMBO_BOX(rd->combo_type));
2457 switch (rd->rename_type)
2459 case RENAME_TYPE_FORMATTED:
2460 if (GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_hide(rd->rename_box);
2461 if (GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_hide(rd->auto_box);
2462 if (!GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_show(rd->format_box);
2463 file_util_rename_multiple_preview_update(rd);
2465 case RENAME_TYPE_AUTO:
2466 if (GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_hide(rd->format_box);
2467 if (GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_hide(rd->rename_box);
2468 if (!GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_show(rd->auto_box);
2469 file_util_rename_multiple_preview_update(rd);
2471 case RENAME_TYPE_MANUAL:
2473 if (GTK_WIDGET_VISIBLE(rd->format_box)) gtk_widget_hide(rd->format_box);
2474 if (GTK_WIDGET_VISIBLE(rd->auto_box)) gtk_widget_hide(rd->auto_box);
2475 if (!GTK_WIDGET_VISIBLE(rd->rename_box)) gtk_widget_show(rd->rename_box);
2479 column = gtk_tree_view_get_column(GTK_TREE_VIEW(rd->listview), RENAME_COLUMN_PREVIEW - 1);
2480 gtk_tree_view_column_set_visible(column, (rd->rename_type != RENAME_TYPE_MANUAL));
2483 static GtkWidget *furm_simple_vlabel(GtkWidget *box, const gchar *text, gint expand)
2488 vbox = gtk_vbox_new(FALSE, 0);
2489 gtk_box_pack_start(GTK_BOX(box), vbox, expand, expand, 0);
2490 gtk_widget_show(vbox);
2492 label = gtk_label_new(text);
2493 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
2494 gtk_widget_show(label);
2499 static GtkTreeViewColumn *file_util_rename_multiple_add_column(RenameDataMult *rd, const gchar *text, gint n)
2501 GtkTreeViewColumn *column;
2502 GtkCellRenderer *renderer;
2504 column = gtk_tree_view_column_new();
2505 gtk_tree_view_column_set_title(column, text);
2506 gtk_tree_view_column_set_min_width(column, 4);
2507 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2508 renderer = gtk_cell_renderer_text_new();
2509 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2510 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
2511 gtk_tree_view_append_column(GTK_TREE_VIEW(rd->listview), column);
2516 static void file_util_rename_multiple_destroy_cb(GtkWidget *widget, gpointer data)
2518 RenameDataMult *rd = data;
2520 if (rd->update_idle_id != -1) g_source_remove(rd->update_idle_id);
2525 static void file_util_rename_multiple_do(GList *source_list, GtkWidget *parent)
2529 GtkWidget *scrolled;
2530 GtkListStore *store;
2531 GtkTreeSelection *selection;
2532 GtkTreeViewColumn *column;
2541 rd = g_new0(RenameDataMult, 1);
2543 rd->fdlg = file_util_file_dlg(_("Rename - Geeqie"),
2544 GQ_WMCLASS, "dlg_rename", parent,
2545 file_util_rename_multiple_close_cb, rd);
2546 generic_dialog_add_message(GENERIC_DIALOG(rd->fdlg), NULL, _("Rename multiple files"), NULL);
2547 file_dialog_add_button(rd->fdlg, GTK_STOCK_OK, _("_Rename"), file_util_rename_multiple_cb, TRUE);
2549 rd->fdlg->source_fd = file_data_ref(source_list->data);
2550 rd->fdlg->dest_path = NULL;
2552 rd->rename_type = RENAME_TYPE_MANUAL;
2554 rd->update_idle_id = -1;
2556 vbox = GENERIC_DIALOG(rd->fdlg)->vbox;
2558 pane = gtk_hpaned_new();
2559 gtk_box_pack_start(GTK_BOX(vbox), pane, TRUE, TRUE, 0);
2560 gtk_widget_show(pane);
2562 scrolled = gtk_scrolled_window_new(NULL, NULL);
2563 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2564 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
2565 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2566 gtk_paned_pack1(GTK_PANED(pane), scrolled, TRUE, TRUE);
2567 gtk_widget_show(scrolled);
2569 store = gtk_list_store_new(4, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
2570 rd->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2571 g_object_unref(store);
2573 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(rd->listview), TRUE);
2574 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(rd->listview), FALSE);
2576 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->listview));
2577 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2578 gtk_tree_selection_set_select_function(selection, file_util_rename_multiple_select_cb, rd, NULL);
2580 file_util_rename_multiple_add_column(rd, _("Original Name"), RENAME_COLUMN_NAME);
2581 column = file_util_rename_multiple_add_column(rd, _("Preview"), RENAME_COLUMN_PREVIEW);
2582 gtk_tree_view_column_set_visible(column, FALSE);
2584 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(rd->listview), TRUE);
2585 g_signal_connect(G_OBJECT(store), "row_changed",
2586 G_CALLBACK(file_util_rename_multiple_preview_order_cb), rd);
2587 gtk_widget_set_size_request(rd->listview, 250, 150);
2589 gtk_container_add(GTK_CONTAINER(scrolled), rd->listview);
2590 gtk_widget_show(rd->listview);
2595 FileData *fd = work->data;
2598 gtk_list_store_append(store, &iter);
2599 gtk_list_store_set(store, &iter, RENAME_COLUMN_FD, fd, RENAME_COLUMN_PATH, fd->path, RENAME_COLUMN_NAME, fd->name, -1);
2604 filelist_free(source_list);
2606 rd->imd = image_new(TRUE);
2607 g_object_set(G_OBJECT(rd->imd->pr), "zoom_expand", FALSE, NULL);
2608 gtk_widget_set_size_request(rd->imd->widget, DIALOG_DEF_IMAGE_DIM_X, DIALOG_DEF_IMAGE_DIM_Y);
2609 gtk_paned_pack2(GTK_PANED(pane), rd->imd->widget, FALSE, TRUE);
2610 gtk_widget_show(rd->imd->widget);
2612 hbox = gtk_hbox_new(FALSE, 0);
2613 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2614 gtk_widget_show(hbox);
2616 rd->combo_type = gtk_combo_box_new_text();
2618 gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Manual rename"));
2619 gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Formatted rename"));
2620 gtk_combo_box_append_text(GTK_COMBO_BOX(rd->combo_type), _("Auto rename"));
2622 gtk_combo_box_set_active(GTK_COMBO_BOX(rd->combo_type), rd->rename_type);
2624 g_signal_connect(G_OBJECT(rd->combo_type), "changed",
2625 G_CALLBACK(file_util_rename_multiple_auto_change), rd);
2626 gtk_box_pack_end(GTK_BOX(hbox), rd->combo_type, FALSE, FALSE, 0);
2627 gtk_widget_show(rd->combo_type);
2629 rd->rename_box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, 0);
2630 table = pref_table_new(rd->rename_box, 2, 2, FALSE, FALSE);
2632 pref_table_label(table, 0, 0, _("Original name:"), 1.0);
2633 rd->rename_label = pref_table_label(table, 1, 0, rd->fdlg->source_fd->name, 0.0);
2635 pref_table_label(table, 0, 1, _("New name:"), 1.0);
2637 rd->rename_entry = gtk_entry_new();
2638 gtk_table_attach(GTK_TABLE(table), rd->rename_entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 0);
2639 generic_dialog_attach_default(GENERIC_DIALOG(rd->fdlg), rd->rename_entry);
2640 gtk_widget_grab_focus(rd->rename_entry);
2642 name = rd->fdlg->source_fd->name;
2643 gtk_entry_set_text(GTK_ENTRY(rd->rename_entry), name);
2644 gtk_editable_select_region(GTK_EDITABLE(rd->rename_entry), 0, filename_base_length(name));
2645 gtk_widget_show(rd->rename_entry);
2647 rd->auto_box = gtk_vbox_new(FALSE, PREF_PAD_GAP);
2648 gtk_box_pack_start(GTK_BOX(vbox), rd->auto_box, FALSE, FALSE, 0);
2649 /* do not show it here */
2651 hbox = pref_box_new(rd->auto_box, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
2653 box2 = furm_simple_vlabel(hbox, _("Begin text"), TRUE);
2655 combo = history_combo_new(&rd->auto_entry_front, "", "numerical_rename_prefix", -1);
2656 g_signal_connect(G_OBJECT(rd->auto_entry_front), "changed",
2657 G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2658 gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2659 gtk_widget_show(combo);
2661 box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
2663 rd->auto_spin_start = pref_spin_new(box2, NULL, NULL,
2664 0.0, 1000000.0, 1.0, 0, 1.0,
2665 G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2667 box2 = furm_simple_vlabel(hbox, _("End text"), TRUE);
2669 combo = history_combo_new(&rd->auto_entry_end, "", "numerical_rename_suffix", -1);
2670 g_signal_connect(G_OBJECT(rd->auto_entry_end), "changed",
2671 G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2672 gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2673 gtk_widget_show(combo);
2675 rd->auto_spin_pad = pref_spin_new(rd->auto_box, _("Padding:"), NULL,
2676 1.0, 8.0, 1.0, 0, 1.0,
2677 G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2679 rd->format_box = gtk_vbox_new(FALSE, PREF_PAD_GAP);
2680 gtk_box_pack_start(GTK_BOX(vbox), rd->format_box, FALSE, FALSE, 0);
2681 /* do not show it here */
2683 hbox = pref_box_new(rd->format_box, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
2685 box2 = furm_simple_vlabel(hbox, _("Format (* = original name, ## = numbers)"), TRUE);
2687 combo = history_combo_new(&rd->format_entry, "", "auto_rename_format", -1);
2688 g_signal_connect(G_OBJECT(rd->format_entry), "changed",
2689 G_CALLBACK(file_util_rename_multiple_preview_entry_cb), rd);
2690 gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
2691 gtk_widget_show(combo);
2693 box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
2695 rd->format_spin = pref_spin_new(box2, NULL, NULL,
2696 0.0, 1000000.0, 1.0, 0, 1.0,
2697 G_CALLBACK(file_util_rename_multiple_preview_adj_cb), rd);
2699 image_change_fd(rd->imd, rd->fdlg->source_fd, 0.0);
2701 g_signal_connect(G_OBJECT(GENERIC_DIALOG(rd->fdlg)->dialog), "destroy",
2702 G_CALLBACK(file_util_rename_multiple_destroy_cb), rd);
2704 gtk_widget_show(GENERIC_DIALOG(rd->fdlg)->dialog);
2708 * rename single file
2711 static void file_util_rename_single(FileDataSingle *fds);
2713 static void file_util_rename_single_ok_cb(GenericDialog *gd, gpointer data)
2715 FileDataSingle *fds = data;
2716 fds->confirmed = TRUE;
2717 file_util_rename_single(fds);
2720 static void file_util_rename_single_cancel_cb(GenericDialog *gd, gpointer data)
2722 FileDataSingle *fds = data;
2723 file_data_single_free(fds);
2726 static void file_util_rename_single(FileDataSingle *fds)
2728 if (isfile(fds->dest) && !fds->confirmed)
2732 gd = file_util_gen_dlg(_("Overwrite file"), GQ_WMCLASS, "dlg_confirm",
2734 file_util_rename_single_cancel_cb, fds);
2736 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
2737 _("Overwrite file?"),
2738 _("Replace existing file by renaming new file."));
2739 pref_spacer(gd->vbox, 0);
2741 generic_dialog_add_button(gd, GTK_STOCK_OK, _("_Overwrite"), file_util_rename_single_ok_cb, TRUE);
2742 generic_dialog_add_image(gd, NULL,
2743 file_data_new_simple(fds->dest), _("Existing file"),
2744 fds->source_fd, _("New file"), TRUE);
2746 gtk_widget_show(gd->dialog);
2753 GList *list = g_list_append(NULL, file_data_ref(fds->source_fd));
2754 file_util_do_move_list(list, FALSE, TRUE);
2755 filelist_free(list);
2757 if (!file_data_add_change_info(fds->source_fd, FILEDATA_CHANGE_RENAME, fds->source_fd->path, fds->dest) ||
2758 !rename_file_ext(fds->source_fd))
2760 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), fds->source_fd->name, filename_from_path(fds->dest));
2761 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2766 file_data_single_free(fds);
2769 static void file_util_rename_single_cb(FileDialog *fdlg, gpointer data)
2774 name = gtk_entry_get_text(GTK_ENTRY(fdlg->entry));
2775 path = concat_dir_and_file(fdlg->dest_path, name);
2777 if (strlen(name) == 0 || strcmp(fdlg->source_fd->path, path) == 0)
2783 file_util_rename_single(file_data_single_new(fdlg->source_fd, path, fdlg->type));
2786 file_dialog_close(fdlg);
2789 static void file_util_rename_single_close_cb(FileDialog *fdlg, gpointer data)
2791 file_dialog_close(fdlg);
2794 static void file_util_rename_single_do(FileData *source_fd, GtkWidget *parent)
2800 fdlg = file_util_file_dlg(_("Rename - Geeqie"), GQ_WMCLASS, "dlg_rename", parent,
2801 file_util_rename_single_close_cb, NULL);
2803 generic_dialog_add_message(GENERIC_DIALOG(fdlg), NULL, _("Rename file"), NULL);
2804 generic_dialog_add_image(GENERIC_DIALOG(fdlg), NULL, source_fd, NULL, NULL, NULL, FALSE);
2806 file_dialog_add_button(fdlg, GTK_STOCK_OK, _("_Rename"), file_util_rename_single_cb, TRUE);
2808 fdlg->source_fd = file_data_ref(source_fd);
2809 fdlg->dest_path = remove_level_from_path(source_fd->path);
2811 table = pref_table_new(GENERIC_DIALOG(fdlg)->vbox, 2, 2, FALSE, FALSE);
2813 pref_table_label(table, 0, 0, _("Original name:"), 1.0);
2814 pref_table_label(table, 1, 0, fdlg->source_fd->name, 0.0);
2816 pref_table_label(table, 0, 1, _("New name:"), 1.0);
2818 fdlg->entry = gtk_entry_new();
2819 gtk_table_attach(GTK_TABLE(table), fdlg->entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 0);
2820 generic_dialog_attach_default(GENERIC_DIALOG(fdlg), fdlg->entry);
2821 gtk_widget_grab_focus(fdlg->entry);
2823 name = fdlg->source_fd->name;
2824 gtk_entry_set_text(GTK_ENTRY(fdlg->entry), name);
2825 gtk_editable_select_region(GTK_EDITABLE(fdlg->entry), 0, filename_base_length(name));
2826 gtk_widget_show(fdlg->entry);
2828 gtk_widget_show(GENERIC_DIALOG(fdlg)->dialog);
2831 void file_util_rename(FileData *source_fd, GList *source_list, GtkWidget *parent)
2833 if (!source_fd && !source_list) return;
2837 file_util_rename_single_do(source_fd, parent);
2839 else if (!source_list->next)
2841 file_util_rename_single_do(source_list->data, parent);
2842 filelist_free(source_list);
2846 file_util_rename_multiple_do(source_list, parent);
2851 *--------------------------------------------------------------------------
2852 * Create directory routines
2853 *--------------------------------------------------------------------------
2856 static void file_util_create_dir_do(const gchar *base, const gchar *name)
2860 path = concat_dir_and_file(base, name);
2864 gchar *text = g_strdup_printf(_("The folder:\n%s\nalready exists."), name);
2865 file_util_warning_dialog(_("Folder exists"), text, GTK_STOCK_DIALOG_INFO, NULL);
2868 else if (isname(path))
2870 gchar *text = g_strdup_printf(_("The path:\n%s\nalready exists as a file."), name);
2871 file_util_warning_dialog(_("Could not create folder"), text, GTK_STOCK_DIALOG_INFO, NULL);
2876 if (!mkdir_utf8(path, 0755))
2878 gchar *text = g_strdup_printf(_("Unable to create folder:\n%s"), name);
2879 file_util_warning_dialog(_("Error creating folder"), text, GTK_STOCK_DIALOG_ERROR, NULL);
2887 static void file_util_create_dir_cb(FileDialog *fdlg, gpointer data)
2891 name = gtk_entry_get_text(GTK_ENTRY(fdlg->entry));
2893 if (strlen(name) == 0) return;
2898 buf = remove_level_from_path(name);
2899 file_util_create_dir_do(buf, filename_from_path(name));
2904 file_util_create_dir_do(fdlg->dest_path, name);
2907 file_dialog_close(fdlg);
2910 static void file_util_create_dir_close_cb(FileDialog *fdlg, gpointer data)
2912 file_dialog_close(fdlg);
2915 void file_util_create_dir(const gchar *path, GtkWidget *parent)
2920 if (!isdir(path)) return;
2922 fdlg = file_util_file_dlg(_("New folder - Geeqie"), GQ_WMCLASS, "dlg_newdir", parent,
2923 file_util_create_dir_close_cb, NULL);
2925 text = g_strdup_printf(_("Create folder in:\n%s\nnamed:"), path);
2926 generic_dialog_add_message(GENERIC_DIALOG(fdlg), NULL, NULL, text);
2929 file_dialog_add_button(fdlg, GTK_STOCK_OK, NULL, file_util_create_dir_cb, TRUE);
2931 fdlg->dest_path = g_strdup(path);
2933 fdlg->entry = gtk_entry_new();
2934 gtk_box_pack_start(GTK_BOX(GENERIC_DIALOG(fdlg)->vbox), fdlg->entry, FALSE, FALSE, 0);
2935 generic_dialog_attach_default(GENERIC_DIALOG(fdlg), fdlg->entry);
2936 gtk_widget_grab_focus(fdlg->entry);
2937 gtk_widget_show(fdlg->entry);
2939 gtk_widget_show(GENERIC_DIALOG(fdlg)->dialog);
2942 gint file_util_rename_dir(FileData *old_fd, const gchar *new_path, GtkWidget *parent)
2944 const gchar *old_name;
2945 const gchar *new_name;
2947 if (!old_fd || !new_path || !isdir(old_fd->path)) return FALSE;
2949 old_name = old_fd->name;
2950 new_name = filename_from_path(new_path);
2952 if (isdir(new_path))
2954 gchar *text = g_strdup_printf(_("The folder:\n%s\nalready exists."), new_name);
2955 file_util_warning_dialog(_("Folder exists"), text, GTK_STOCK_DIALOG_INFO, parent);
2961 if (isname(new_path))
2963 gchar *text = g_strdup_printf(_("The path:\n%s\nalready exists as a file."), new_name);
2964 file_util_warning_dialog(_("Rename failed"), text, GTK_STOCK_DIALOG_INFO,parent);
2969 if (!file_data_add_change_info(old_fd, FILEDATA_CHANGE_RENAME, old_fd->path, new_path) ||
2970 !rename_file_ext(old_fd))
2972 gchar *text = g_strdup_printf(_("Failed to rename %s to %s."), old_name, new_name);
2973 file_util_warning_dialog(_("Rename failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
2983 *--------------------------------------------------------------------------
2984 * Delete directory routines
2985 *--------------------------------------------------------------------------
2988 /* The plan is to eventually make all of utilops.c
2989 * use UtilityData and UtilityType.
2990 * And clean up the above mess someday.
2994 UTILITY_TYPE_NONE = 0,
2997 UTILITY_TYPE_RENAME,
2998 UTILITY_TYPE_DELETE,
2999 UTILITY_TYPE_DELETE_LINK,
3000 UTILITY_TYPE_DELETE_FOLDER
3003 typedef struct _UtilityData UtilityData;
3004 struct _UtilityData {
3006 FileData *source_fd;
3014 UTILITY_COLUMN_FD = 0,
3015 UTILITY_COLUMN_PATH,
3016 UTILITY_COLUMN_NAME,
3017 UTILITY_COLUMN_COUNT
3021 #define UTILITY_LIST_MIN_WIDTH 250
3022 #define UTILITY_LIST_MIN_HEIGHT 150
3024 /* thumbnail spec has a max depth of 4 (.thumb??/fail/appname/??.png) */
3025 #define UTILITY_DELETE_MAX_DEPTH 5
3028 static void file_util_data_free(UtilityData *ud)
3032 file_data_unref(ud->source_fd);
3033 filelist_free(ud->dlist);
3034 filelist_free(ud->flist);
3036 if (ud->gd) generic_dialog_close(ud->gd);
3041 static GtkTreeViewColumn *file_util_dialog_add_list_column(GtkWidget *view, const gchar *text, gint n)
3043 GtkTreeViewColumn *column;
3044 GtkCellRenderer *renderer;
3046 column = gtk_tree_view_column_new();
3047 gtk_tree_view_column_set_title(column, text);
3048 gtk_tree_view_column_set_min_width(column, 4);
3049 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
3050 renderer = gtk_cell_renderer_text_new();
3051 gtk_tree_view_column_pack_start(column, renderer, TRUE);
3052 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
3053 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
3058 static GtkWidget *file_util_dialog_add_list(GtkWidget *box, GList *list, gint full_paths)
3060 GtkWidget *scrolled;
3062 GtkListStore *store;
3064 scrolled = gtk_scrolled_window_new(NULL, NULL);
3065 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3066 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled),
3067 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3068 gtk_box_pack_start(GTK_BOX(box), scrolled, TRUE, TRUE, 0);
3069 gtk_widget_show(scrolled);
3071 store = gtk_list_store_new(UTILITY_COLUMN_COUNT, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING);
3072 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
3073 g_object_unref(store);
3075 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
3076 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), FALSE);
3080 file_util_dialog_add_list_column(view, _("Location"), UTILITY_COLUMN_PATH);
3084 file_util_dialog_add_list_column(view, _("Name"), UTILITY_COLUMN_NAME);
3087 gtk_widget_set_size_request(view, UTILITY_LIST_MIN_WIDTH, UTILITY_LIST_MIN_HEIGHT);
3088 gtk_container_add(GTK_CONTAINER(scrolled), view);
3089 gtk_widget_show(view);
3093 FileData *fd = list->data;
3096 gtk_list_store_append(store, &iter);
3097 gtk_list_store_set(store, &iter, UTILITY_COLUMN_FD, fd,
3098 UTILITY_COLUMN_PATH, fd->path,
3099 UTILITY_COLUMN_NAME, fd->name, -1);
3107 static gboolean file_util_delete_dir_preview_cb(GtkTreeSelection *selection, GtkTreeModel *store,
3108 GtkTreePath *tpath, gboolean path_currently_selected,
3111 UtilityData *ud = data;
3113 FileData *fd = NULL;
3115 if (path_currently_selected ||
3116 !gtk_tree_model_get_iter(store, &iter, tpath)) return TRUE;
3118 gtk_tree_model_get(store, &iter, UTILITY_COLUMN_FD, &fd, -1);
3119 generic_dialog_image_set(ud->gd, fd);
3120 // file_data_unref(fd);
3125 static void file_util_delete_dir_cancel_cb(GenericDialog *gd, gpointer data)
3127 UtilityData *ud = data;
3130 file_util_data_free(ud);
3133 FileData *file_util_delete_dir_empty_path(FileData *fd, gint real_content, gint level)
3135 GList *dlist = NULL;
3136 GList *flist = NULL;
3138 FileData *fail = NULL;
3140 if (debug) printf("deltree into: %s\n", fd->path);
3143 if (level > UTILITY_DELETE_MAX_DEPTH)
3145 printf("folder recursion depth past %d, giving up\n", UTILITY_DELETE_MAX_DEPTH);
3146 return file_data_ref(fd);
3149 if (!filelist_read_lstat(fd->path, &flist, &dlist)) file_data_ref(fd);
3152 while (work && !fail)
3159 fail = file_util_delete_dir_empty_path(lfd, real_content, level);
3163 while (work && !fail)
3170 if (debug) printf("deltree child: %s\n", lfd->path);
3172 if (real_content && !islink(lfd->path))
3174 if (!file_util_unlink(lfd)) fail = file_data_ref(lfd);
3178 if (!unlink_file(lfd->path)) fail = file_data_ref(lfd);
3182 filelist_free(dlist);
3183 filelist_free(flist);
3185 if (!fail && !rmdir_utf8(fd->path))
3187 fail = file_data_ref(fd);
3190 if (debug) printf("deltree done: %s\n", fd->path);
3195 static void file_util_delete_dir_ok_cb(GenericDialog *gd, gpointer data)
3197 UtilityData *ud = data;
3201 if (ud->type == UTILITY_TYPE_DELETE_LINK)
3203 if (!unlink_file(ud->source_fd->path))
3207 text = g_strdup_printf("Unable to remove symbolic link:\n %s", ud->source_fd->path);
3208 file_util_warning_dialog(_("Delete failed"), text,
3209 GTK_STOCK_DIALOG_ERROR, NULL);
3215 FileData *fail = NULL;
3219 while (work && !fail)
3226 fail = file_util_delete_dir_empty_path(fd, FALSE, 0);
3230 while (work && !fail)
3237 if (debug) printf("deltree unlink: %s\n", fd->path);
3239 if (islink(fd->path))
3241 if (!unlink_file(fd->path)) fail = file_data_ref(fd);
3245 if (!file_util_unlink(fd)) fail = file_data_ref(fd);
3251 if (!rmdir_utf8(ud->source_fd->path)) fail = file_data_ref(ud->source_fd);
3258 text = g_strdup_printf(_("Unable to delete folder:\n\n%s"), ud->source_fd->path);
3259 gd = file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, NULL);
3262 if (fail != ud->source_fd)
3264 pref_spacer(gd->vbox, PREF_PAD_GROUP);
3265 text = g_strdup_printf(_("Removal of folder contents failed at this file:\n\n%s"),
3267 pref_label_new(gd->vbox, text);
3271 file_data_unref(fail);
3275 file_util_data_free(ud);
3278 static GList *file_util_delete_dir_remaining_folders(GList *dlist)
3280 GList *rlist = NULL;
3287 dlist = dlist->next;
3290 (strcmp(fd->name, THUMB_FOLDER_GLOBAL) != 0 &&
3291 strcmp(fd->name, THUMB_FOLDER_LOCAL) != 0 &&
3292 strcmp(fd->name, GQVIEW_CACHE_LOCAL_METADATA) != 0) )
3294 rlist = g_list_prepend(rlist, fd);
3298 return g_list_reverse(rlist);
3301 void file_util_delete_dir(FileData *fd, GtkWidget *parent)
3303 GList *dlist = NULL;
3304 GList *flist = NULL;
3307 if (!isdir(fd->path)) return;
3309 if (islink(fd->path))
3314 ud = g_new0(UtilityData, 1);
3315 ud->type = UTILITY_TYPE_DELETE_LINK;
3316 ud->source_fd = file_data_ref(fd);
3320 ud->gd = file_util_gen_dlg(_("Delete folder"), GQ_WMCLASS, "dlg_confirm",
3322 file_util_delete_dir_cancel_cb, ud);
3324 text = g_strdup_printf(_("This will delete the symbolic link:\n\n%s\n\n"
3325 "The folder this link points to will not be deleted."),
3327 generic_dialog_add_message(ud->gd, GTK_STOCK_DIALOG_QUESTION,
3328 _("Delete symbolic link to folder?"),
3332 generic_dialog_add_button(ud->gd, GTK_STOCK_DELETE, NULL, file_util_delete_dir_ok_cb, TRUE);
3334 gtk_widget_show(ud->gd->dialog);
3339 if (!access_file(fd->path, W_OK | X_OK))
3343 text = g_strdup_printf(_("Unable to remove folder %s\n"
3344 "Permissions do not allow writing to the folder."), fd->path);
3345 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
3351 if (!filelist_read_lstat(fd->path, &flist, &dlist))
3355 text = g_strdup_printf(_("Unable to list contents of folder %s"), fd->path);
3356 file_util_warning_dialog(_("Delete failed"), text, GTK_STOCK_DIALOG_ERROR, parent);
3362 rlist = file_util_delete_dir_remaining_folders(dlist);
3369 gd = file_util_gen_dlg(_("Folder contains subfolders"), GQ_WMCLASS, "dlg_warning",
3370 parent, TRUE, NULL, NULL);
3371 generic_dialog_add_button(gd, GTK_STOCK_CLOSE, NULL, NULL, TRUE);
3373 text = g_strdup_printf(_("Unable to delete the folder:\n\n%s\n\n"
3374 "This folder contains subfolders which must be moved before it can be deleted."),
3376 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_WARNING,
3377 _("Folder contains subfolders"),
3381 box = pref_group_new(box, TRUE, _("Subfolders:"), GTK_ORIENTATION_VERTICAL);
3383 rlist = filelist_sort_path(rlist);
3384 file_util_dialog_add_list(box, rlist, FALSE);
3386 gtk_widget_show(gd->dialog);
3393 GtkTreeSelection *selection;
3396 ud = g_new0(UtilityData, 1);
3397 ud->type = UTILITY_TYPE_DELETE_FOLDER;
3398 ud->source_fd = file_data_ref(fd);
3401 ud->flist = filelist_sort_path(flist);
3404 ud->gd = file_util_gen_dlg(_("Delete folder"), GQ_WMCLASS, "dlg_confirm",
3405 parent, TRUE, file_util_delete_dir_cancel_cb, ud);
3406 generic_dialog_add_button(ud->gd, GTK_STOCK_DELETE, NULL, file_util_delete_dir_ok_cb, TRUE);
3408 text = g_strdup_printf(_("This will delete the folder:\n\n%s\n\n"
3409 "The contents of this folder will also be deleted."),
3411 box = generic_dialog_add_message(ud->gd, GTK_STOCK_DIALOG_QUESTION,
3412 _("Delete folder?"),
3416 box = pref_group_new(box, TRUE, _("Contents:"), GTK_ORIENTATION_HORIZONTAL);
3418 view = file_util_dialog_add_list(box, ud->flist, FALSE);
3419 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
3420 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3421 gtk_tree_selection_set_select_function(selection, file_util_delete_dir_preview_cb, ud, NULL);
3423 generic_dialog_add_image(ud->gd, box, NULL, NULL, NULL, NULL, FALSE);
3425 box_append_safe_delete_status(ud->gd);
3427 gtk_widget_show(ud->gd->dialog);
3431 filelist_free(dlist);
3432 filelist_free(flist);