2 * Copyright (C) 2004 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <glib-object.h>
29 #include "collect-io.h"
35 #include "history-list.h"
37 #include "layout-image.h"
39 #include "main-defines.h"
44 #include "ui-bookmark.h"
45 #include "ui-fileops.h"
47 #include "ui-utildlg.h"
53 *-------------------------------------------------------------------
55 *-------------------------------------------------------------------
65 GtkWidget *dialog_name_entry;
68 SortActionType action;
71 SortSelectionType selection;
73 GtkWidget *folder_group;
74 GtkWidget *collection_group;
76 GtkWidget *add_button;
77 GtkWidget *help_button;
78 GtkWidget *undo_button;
79 SortActionType undo_action;
81 GList *undo_dest_list;
82 gchar *undo_collection;
86 #define SORT_KEY_FOLDERS "sort_manager"
87 #define SORT_KEY_COLLECTIONS "sort_manager_collections"
90 static void bar_sort_undo_set(SortData *sd, GList *src_list, const gchar *dest);
91 static void bar_sort_add_close(SortData *sd);
94 static void bar_sort_collection_list_build(GtkWidget *bookmarks)
100 history_list_free_key(SORT_KEY_COLLECTIONS);
101 bookmark_list_set_key(bookmarks, SORT_KEY_COLLECTIONS);
103 dir_fd = file_data_new_dir(get_collections_dir());
104 filelist_read(dir_fd, &list, nullptr);
105 file_data_unref(dir_fd);
107 list = filelist_sort_path(list);
115 fd = static_cast<FileData *>(work->data);
118 if (file_extension_match(fd->path, GQ_COLLECTION_EXT))
120 name = remove_extension_from_path(fd->name);
124 name = g_strdup(fd->name);
126 bookmark_list_add(bookmarks, name, fd->path);
133 static void bar_sort_mode_sync(SortData *sd, SortModeType mode)
135 gboolean folder_mode;
137 if (sd->mode == mode) return;
140 folder_mode = (sd->mode == BAR_SORT_MODE_FOLDER);
142 bookmark_list_set_no_defaults(sd->bookmarks, !folder_mode);
143 bookmark_list_set_editable(sd->bookmarks, folder_mode);
144 bookmark_list_set_only_directories(sd->bookmarks, folder_mode);
148 gtk_widget_hide(sd->collection_group);
149 gtk_widget_show(sd->folder_group);
150 bookmark_list_set_key(sd->bookmarks, SORT_KEY_FOLDERS);
154 gtk_widget_hide(sd->folder_group);
155 gtk_widget_show(sd->collection_group);
156 bar_sort_collection_list_build(sd->bookmarks);
159 bar_sort_add_close(sd);
161 bar_sort_undo_set(sd, nullptr, nullptr);
164 static void bar_sort_mode_cb(GtkWidget *combo, gpointer data)
166 auto sd = static_cast<SortData *>(data);
168 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) == BAR_SORT_MODE_FOLDER)
170 bar_sort_mode_sync(sd, BAR_SORT_MODE_FOLDER);
174 bar_sort_mode_sync(sd, BAR_SORT_MODE_COLLECTION);
178 /* this takes control of src_list */
179 static void bar_sort_undo_set(SortData *sd, GList *src_list, const gchar *dest)
181 g_list_free_full(sd->undo_src_list, g_free);
182 sd->undo_src_list = filelist_to_path_list(src_list);
186 /* we should create the undo_dest_list to use it later... */
187 g_list_free_full(sd->undo_dest_list, g_free);
188 sd->undo_dest_list=nullptr;
190 GList *work = sd->undo_src_list;
193 gchar *filename = g_strdup(filename_from_path(static_cast<const gchar *>(work->data)));
194 gchar *dest_path = g_build_filename(g_strdup(dest), filename, NULL);
195 sd->undo_dest_list = g_list_prepend(sd->undo_dest_list, g_strdup(dest_path));
198 sd->undo_dest_list = g_list_reverse(sd->undo_dest_list);
201 sd->undo_action = sd->action;
205 gtk_widget_set_sensitive(sd->undo_button,
206 ((sd->undo_src_list ) && sd->undo_dest_list));
210 static void bar_sort_undo_folder(SortData *sd, GtkWidget *button)
214 if (!sd->undo_src_list || !sd->undo_dest_list) return;
216 switch (sd->undo_action)
224 if (sd->undo_src_list)
226 GList *work = nullptr;
228 src_path = g_strdup(static_cast<const gchar *>(sd->undo_src_list->data));
229 src_dir = remove_level_from_path(src_path);
230 list = sd->undo_dest_list;
233 work = g_list_prepend(work, file_data_new_group(static_cast<const gchar *>(list->data)));
236 file_util_move_simple(work, src_dir, sd->lw->window);
244 case BAR_SORT_FILTER:
245 if (sd->undo_src_list)
248 GList *work = nullptr;
250 delete_list = sd->undo_dest_list;
253 work = g_list_append(work, file_data_new_group(static_cast<const gchar *>(delete_list->data)));
254 delete_list = delete_list->next;
256 options->file_ops.safe_delete_enable = TRUE;
257 file_util_delete(nullptr, work, button);
265 layout_refresh(sd->lw);
266 origin = static_cast<gchar *>((sd->undo_src_list)->data);
270 layout_image_set_fd(sd->lw, file_data_new_group(origin));
273 bar_sort_undo_set(sd, nullptr, nullptr);
276 static void bar_sort_undo_collection(SortData *sd)
280 work = sd->undo_src_list;
284 source = static_cast<gchar *>(work->data);
286 collect_manager_remove(file_data_new_group(source), sd->undo_collection);
289 bar_sort_undo_set(sd, nullptr, nullptr);
292 static void bar_sort_undo_cb(GtkWidget *button, gpointer data)
294 auto sd = static_cast<SortData *>(data);
296 if (sd->mode == BAR_SORT_MODE_FOLDER)
298 bar_sort_undo_folder(sd, button);
302 bar_sort_undo_collection(sd);
306 static void bar_sort_bookmark_select_folder(SortData *sd, FileData *, const gchar *path)
310 GList *undo_src_list;
312 if (!isdir(path)) return;
314 orig_list = layout_selection_list(sd->lw);
315 action_list = orig_list;
316 undo_src_list = orig_list;
319 bar_sort_undo_set(sd, undo_src_list, path);
324 file_util_copy_simple(action_list, path, sd->lw->window);
325 action_list = nullptr;
326 layout_image_next(sd->lw);
330 file_util_move_simple(action_list, path, sd->lw->window);
331 action_list = nullptr;
334 case BAR_SORT_FILTER:
335 file_util_start_filter_from_filelist(sd->filter_key, action_list, path, sd->lw->window);
336 layout_image_next(sd->lw);
344 static void bar_sort_bookmark_select_collection(SortData *sd, FileData *source, const gchar *path)
346 GList *list = nullptr;
348 switch (sd->selection)
350 case BAR_SORT_SELECTION_IMAGE:
351 list = g_list_append(nullptr, file_data_ref(source));
353 case BAR_SORT_SELECTION_SELECTED:
354 list = layout_selection_list(sd->lw);
362 bar_sort_undo_set(sd, nullptr, nullptr);
366 bar_sort_undo_set(sd, list, path);
367 sd->undo_collection = g_strdup(path);
373 image_fd = static_cast<FileData *>(list->data);
375 collect_manager_add(image_fd, path);
379 static void bar_sort_bookmark_select(const gchar *path, gpointer data)
381 auto sd = static_cast<SortData *>(data);
384 source = layout_image_get_fd(sd->lw);
385 if (!path || !source) return;
387 if (sd->mode == BAR_SORT_MODE_FOLDER)
389 bar_sort_bookmark_select_folder(sd, source, path);
393 bar_sort_bookmark_select_collection(sd, source, path);
397 static void bar_sort_set_action(SortData *sd, SortActionType action, const gchar *filter_key)
400 if (action == BAR_SORT_FILTER)
402 if (!filter_key) filter_key = "";
403 sd->filter_key = g_strdup(filter_key);
407 sd->filter_key = nullptr;
411 static void bar_sort_set_copy_cb(GtkWidget *button, gpointer data)
413 auto sd = static_cast<SortData *>(data);
414 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) return;
415 bar_sort_set_action(sd, BAR_SORT_COPY, nullptr);
418 static void bar_sort_set_move_cb(GtkWidget *button, gpointer data)
420 auto sd = static_cast<SortData *>(data);
421 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) return;
422 bar_sort_set_action(sd, BAR_SORT_MOVE, nullptr);
425 static void bar_sort_set_filter_cb(GtkWidget *button, gpointer data)
427 auto sd = static_cast<SortData *>(data);
430 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) return;
431 key = static_cast<const gchar *>(g_object_get_data(G_OBJECT(button), "filter_key"));
432 bar_sort_set_action(sd, BAR_SORT_FILTER, key);
435 static void bar_filter_help_cb(GenericDialog *, gpointer)
437 help_window_show("GuidePluginsConfig.html#Geeqieextensions");
440 static void bar_filter_help_dialog()
444 gd = generic_dialog_new(_("Sort Manager Operations"),
445 "sort_manager_operations", nullptr, TRUE, nullptr, nullptr);
446 generic_dialog_add_message(gd, GQ_ICON_DIALOG_INFO,
447 _("Sort Manager Operations"), _("Additional operations utilising plugins\nmay be included by setting:\n\nX-Geeqie-Filter=true\n\nin the plugin file."), TRUE);
448 generic_dialog_add_button(gd, GQ_ICON_HELP, _("Help"), bar_filter_help_cb, TRUE);
449 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", nullptr, TRUE);
451 gtk_widget_show(gd->dialog);
454 static gboolean bar_filter_message_cb(GtkWidget *, GdkEventButton *event, gpointer)
456 if (event->button != MOUSE_BUTTON_RIGHT) return FALSE;
458 bar_filter_help_dialog();
463 static void bar_sort_help_cb(gpointer)
465 bar_filter_help_dialog();
468 static void bar_sort_set_selection(SortData *sd, SortSelectionType selection)
470 sd->selection = selection;
473 static void bar_sort_set_selection_image_cb(GtkWidget *button, gpointer data)
475 auto sd = static_cast<SortData *>(data);
476 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) return;
477 bar_sort_set_selection(sd, BAR_SORT_SELECTION_IMAGE);
480 static void bar_sort_set_selection_selected_cb(GtkWidget *button, gpointer data)
482 auto sd = static_cast<SortData *>(data);
483 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) return;
484 bar_sort_set_selection(sd, BAR_SORT_SELECTION_SELECTED);
487 static void bar_sort_add_close(SortData *sd)
489 if (sd->dialog) file_dialog_close(sd->dialog);
490 sd->dialog_name_entry = nullptr;
491 sd->dialog = nullptr;
494 static void bar_sort_add_ok_cb(FileDialog *fd, gpointer data)
496 auto sd = static_cast<SortData *>(data);
497 const gchar *name = gq_gtk_entry_get_text(GTK_ENTRY(sd->dialog_name_entry));
498 gboolean empty_name = (name[0] == '\0');
500 name = gq_gtk_entry_get_text(GTK_ENTRY(sd->dialog_name_entry));
501 if (sd->mode == BAR_SORT_MODE_FOLDER)
505 name = filename_from_path(fd->dest_path);
508 bookmark_list_add(sd->bookmarks, name, fd->dest_path);
513 gboolean has_extension;
514 auto filename = const_cast<gchar *>(name);
516 if (empty_name) return;
518 has_extension = file_extension_match(name, GQ_COLLECTION_EXT);
521 filename = g_strconcat(name, GQ_COLLECTION_EXT, NULL);
524 path = g_build_filename(get_collections_dir(), filename, NULL);
527 gchar *text = g_strdup_printf(_("The collection:\n%s\nalready exists."), filename);
528 file_util_warning_dialog(_("Collection exists"), text, GQ_ICON_DIALOG_INFO, nullptr);
535 cd = collection_new(path);
536 if (collection_save(cd, path))
538 bar_sort_collection_list_build(sd->bookmarks);
542 gchar *text = g_strdup_printf(_("Failed to save the collection:\n%s"), path);
543 file_util_warning_dialog(_("Save Failed"), text,
544 GQ_ICON_DIALOG_ERROR, GENERIC_DIALOG(fd)->dialog);
547 collection_unref(cd);
550 if (!has_extension) g_free(filename);
554 bar_sort_add_close(sd);
557 static void bar_sort_add_cancel_cb(FileDialog *, gpointer data)
559 auto sd = static_cast<SortData *>(data);
561 bar_sort_add_close(sd);
564 static void bar_sort_add_cb(GtkWidget *button, gpointer data)
566 auto sd = static_cast<SortData *>(data);
572 gtk_window_present(GTK_WINDOW(GENERIC_DIALOG(sd->dialog)->dialog));
576 if (sd->mode == BAR_SORT_MODE_FOLDER)
578 title = _("Add Bookmark");
582 title = _("Add Collection");
585 sd->dialog = file_util_file_dlg(title,
586 "add_bookmark", button,
587 bar_sort_add_cancel_cb, sd);
588 file_dialog_add_button(sd->dialog, GQ_ICON_OK, "OK", bar_sort_add_ok_cb, TRUE);
590 generic_dialog_add_message(GENERIC_DIALOG(sd->dialog), nullptr, title, nullptr, FALSE);
592 if (sd->mode == BAR_SORT_MODE_FOLDER)
594 file_dialog_add_path_widgets(sd->dialog, nullptr, nullptr, "add_bookmark", nullptr, nullptr);
597 hbox = pref_box_new(GENERIC_DIALOG(sd->dialog)->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
599 pref_label_new(hbox, _("Name:"));
601 sd->dialog_name_entry = gtk_entry_new();
602 gq_gtk_box_pack_start(GTK_BOX(hbox), sd->dialog_name_entry, TRUE, TRUE, 0);
603 generic_dialog_attach_default(GENERIC_DIALOG(sd->dialog), sd->dialog_name_entry);
604 gtk_widget_show(sd->dialog_name_entry);
606 if (sd->mode == BAR_SORT_MODE_COLLECTION)
608 gtk_widget_grab_focus(sd->dialog_name_entry);
611 gtk_widget_show(GENERIC_DIALOG(sd->dialog)->dialog);
614 void bar_sort_close(GtkWidget *bar)
618 sd = static_cast<SortData *>(g_object_get_data(G_OBJECT(bar), "bar_sort_data"));
621 gq_gtk_widget_destroy(sd->vbox);
624 static void bar_sort_destroy(GtkWidget *, gpointer data)
626 auto sd = static_cast<SortData *>(data);
628 bar_sort_add_close(sd);
630 g_free(sd->filter_key);
631 g_list_free_full(sd->undo_src_list, g_free);
632 g_list_free_full(sd->undo_dest_list, g_free);
633 g_free(sd->undo_collection);
637 static void bar_sort_edit_button_free(gpointer data)
642 static GtkWidget *bar_sort_new(LayoutWindow *lw, SortActionType action,
643 SortModeType mode, SortSelectionType selection,
644 const gchar *filter_key)
647 GtkWidget *buttongrp;
653 gboolean have_filter;
656 if (!lw) return nullptr;
658 sd = g_new0(SortData, 1);
664 if (sd->action == BAR_SORT_FILTER && (!filter_key || !filter_key[0]))
666 sd->action = BAR_SORT_COPY;
669 sd->selection = selection;
670 sd->undo_src_list = nullptr;
671 sd->undo_dest_list = nullptr;
672 sd->undo_collection = nullptr;
674 sd->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
675 DEBUG_NAME(sd->vbox);
676 g_object_set_data(G_OBJECT(sd->vbox), "bar_sort_data", sd);
677 g_signal_connect(G_OBJECT(sd->vbox), "destroy",
678 G_CALLBACK(bar_sort_destroy), sd);
680 label = gtk_label_new(_("Sort Manager"));
681 pref_label_bold(label, TRUE, FALSE);
682 gq_gtk_box_pack_start(GTK_BOX(sd->vbox), label, FALSE, FALSE, 0);
683 gtk_widget_show(label);
685 combo = gtk_combo_box_text_new();
686 gq_gtk_box_pack_start(GTK_BOX(sd->vbox), combo, FALSE, FALSE, 0);
687 gtk_widget_show(combo);
689 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Folders"));
690 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Collections"));
692 g_signal_connect(G_OBJECT(combo), "changed",
693 G_CALLBACK(bar_sort_mode_cb), sd);
695 sd->folder_group = pref_box_new(sd->vbox, FALSE, GTK_ORIENTATION_VERTICAL, 0);
696 DEBUG_NAME(sd->folder_group);
697 gtk_widget_set_tooltip_text(sd->folder_group, _("See the Help file for additional functions"));
699 buttongrp = pref_radiobutton_new(sd->folder_group, nullptr,
700 _("Copy"), (sd->action == BAR_SORT_COPY),
701 G_CALLBACK(bar_sort_set_copy_cb), sd);
702 g_signal_connect(G_OBJECT(buttongrp), "button_press_event", G_CALLBACK(bar_filter_message_cb), NULL);
703 button = pref_radiobutton_new(sd->folder_group, buttongrp,
704 _("Move"), (sd->action == BAR_SORT_MOVE),
705 G_CALLBACK(bar_sort_set_move_cb), sd);
706 g_signal_connect(G_OBJECT(button), "button_press_event", G_CALLBACK(bar_filter_message_cb), NULL);
710 editors_list = editor_list_get();
715 auto editor = static_cast<EditorDescription *>(work->data);
717 gboolean select = FALSE;
721 if (!editor_is_filter(editor->key)) continue;
723 key = g_strdup(editor->key);
724 if (sd->action == BAR_SORT_FILTER && strcmp(key, filter_key) == 0)
726 bar_sort_set_action(sd, sd->action, key);
731 button = pref_radiobutton_new(sd->folder_group, buttongrp,
732 editor->name, select,
733 G_CALLBACK(bar_sort_set_filter_cb), sd);
734 g_signal_connect(G_OBJECT(button), "button_press_event", G_CALLBACK(bar_filter_message_cb), NULL);
736 g_object_set_data_full(G_OBJECT(button), "filter_key", key, bar_sort_edit_button_free);
738 g_list_free(editors_list);
740 if (sd->action == BAR_SORT_FILTER && !have_filter) sd->action = BAR_SORT_COPY;
742 sd->collection_group = pref_box_new(sd->vbox, FALSE, GTK_ORIENTATION_VERTICAL, 0);
744 buttongrp = pref_radiobutton_new(sd->collection_group, nullptr,
745 _("Add image"), (sd->selection == BAR_SORT_SELECTION_IMAGE),
746 G_CALLBACK(bar_sort_set_selection_image_cb), sd);
747 pref_radiobutton_new(sd->collection_group, buttongrp,
748 _("Add selection"), (sd->selection == BAR_SORT_SELECTION_SELECTED),
749 G_CALLBACK(bar_sort_set_selection_selected_cb), sd);
751 sd->bookmarks = bookmark_list_new(SORT_KEY_FOLDERS, bar_sort_bookmark_select, sd);
752 DEBUG_NAME(sd->bookmarks);
753 gq_gtk_box_pack_start(GTK_BOX(sd->vbox), sd->bookmarks, TRUE, TRUE, 0);
754 gtk_widget_show(sd->bookmarks);
756 tbar = pref_toolbar_new(sd->vbox);
759 sd->add_button = pref_toolbar_button(tbar, GQ_ICON_ADD, _("Add"), FALSE,
761 G_CALLBACK(bar_sort_add_cb), sd);
762 sd->undo_button = pref_toolbar_button(tbar, GQ_ICON_UNDO, _("Undo"), FALSE,
763 _("Undo last image"),
764 G_CALLBACK(bar_sort_undo_cb), sd);
765 sd->help_button = pref_toolbar_button(tbar, GQ_ICON_HELP, _("Help"), FALSE,
766 _("Functions additional to Copy and Move"),
767 G_CALLBACK(bar_sort_help_cb), sd);
769 sd->mode = static_cast<SortModeType>(-1);
770 bar_sort_mode_sync(sd, mode);
771 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), static_cast<gint>(sd->mode));
776 GtkWidget *bar_sort_new_from_config(LayoutWindow *lw, const gchar **, const gchar **)
780 bar = bar_sort_new(lw, lw->options.action, lw->options.mode, lw->options.selection, lw->options.filter_key);
782 if (lw->bar_sort_enabled) gtk_widget_show(bar);
787 * @brief Sets the bar_sort_enabled flag
789 * @param attribute_names
790 * @param attribute_values
792 * Called from rcfile when processing geeqierc.xml on start-up.
793 * It is necessary to set the bar_sort_enabled flag because
794 * the sort manager and desktop files are set up in the idle loop, and
795 * setup is not yet completed during initialisation.
796 * The flag is checked in layout_editors_reload_idle_cb.
798 void bar_sort_cold_start(LayoutWindow *lw, const gchar **attribute_names, const gchar **attribute_values)
800 gboolean enabled = TRUE;
801 gint action = BAR_SORT_COPY;
802 gint mode = BAR_SORT_MODE_FOLDER;
803 gint selection = BAR_SORT_SELECTION_IMAGE;
804 gchar *filter_key = nullptr;
806 while (attribute_names && *attribute_names)
808 const gchar *option = *attribute_names++;
809 const gchar *value = *attribute_values++;
811 if (READ_BOOL_FULL("enabled", enabled)) continue;
812 if (READ_INT_CLAMP_FULL("action", action, 0, BAR_SORT_ACTION_COUNT - 1)) continue;
813 if (READ_INT_CLAMP_FULL("mode", mode, 0, BAR_SORT_MODE_COUNT - 1)) continue;
814 if (READ_INT_CLAMP_FULL("selection", selection, 0, BAR_SORT_SELECTION_COUNT - 1)) continue;
815 if (READ_CHAR_FULL("filter_key", filter_key)) continue;
817 log_printf("unknown attribute %s = %s\n", option, value);
820 lw->options.action = static_cast<SortActionType>(action);
821 lw->options.mode = static_cast<SortModeType>(mode);
822 lw->options.selection = static_cast<SortSelectionType>(selection);
823 lw->options.filter_key = g_strdup(filter_key);
824 lw->bar_sort_enabled = enabled;
829 GtkWidget *bar_sort_new_default(LayoutWindow *lw)
831 return bar_sort_new_from_config(lw, nullptr, nullptr);
834 void bar_sort_write_config(GtkWidget *bar, GString *outstr, gint indent)
839 sd = static_cast<SortData *>(g_object_get_data(G_OBJECT(bar), "bar_sort_data"));
842 WRITE_NL(); WRITE_STRING("<bar_sort ");
843 write_bool_option(outstr, indent, "enabled", gtk_widget_get_visible(bar));
844 WRITE_INT(*sd, mode);
845 WRITE_INT(*sd, action);
846 WRITE_INT(*sd, selection);
847 WRITE_CHAR(*sd, filter_key);
852 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */