2 * Copyright (C) 2006 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.
22 #include "pan-view-filter.h"
29 #include "ui_fileops.h"
30 #include "ui_tabcomp.h"
33 PanViewFilterUi *pan_filter_ui_new(PanWindow *pw)
35 PanViewFilterUi *ui = g_new0(PanViewFilterUi, 1);
39 // Build the actual filter UI.
40 ui->filter_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
41 pref_spacer(ui->filter_box, 0);
42 pref_label_new(ui->filter_box, _("Keyword Filter:"));
44 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
45 gtk_box_pack_start(GTK_BOX(ui->filter_box), hbox, TRUE, TRUE, 0);
46 gtk_widget_show(hbox);
48 combo = tab_completion_new_with_history(&ui->filter_entry, "", "pan_view_filter", -1,
49 pan_filter_activate_cb, pw);
50 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
51 gtk_widget_show(combo);
53 // TODO(xsdg): Figure out whether it's useful to keep this label around.
54 ui->filter_label = gtk_label_new("");
55 //gtk_box_pack_start(GTK_BOX(hbox), ui->filter_label, FALSE, FALSE, 0);
56 //gtk_widget_show(ui->filter_label);
58 ui->filter_kw_hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
59 gtk_box_pack_start(GTK_BOX(hbox), ui->filter_kw_hbox, TRUE, TRUE, 0);
60 gtk_widget_show(ui->filter_kw_hbox);
62 // Build the spin-button to show/hide the filter UI.
63 ui->filter_button = gtk_toggle_button_new();
64 gtk_button_set_relief(GTK_BUTTON(ui->filter_button), GTK_RELIEF_NONE);
65 gtk_button_set_focus_on_click(GTK_BUTTON(ui->filter_button), FALSE);
66 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
67 gtk_container_add(GTK_CONTAINER(ui->filter_button), hbox);
68 gtk_widget_show(hbox);
69 ui->filter_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
70 gtk_box_pack_start(GTK_BOX(hbox), ui->filter_button_arrow, FALSE, FALSE, 0);
71 gtk_widget_show(ui->filter_button_arrow);
72 pref_label_new(hbox, _("Filter"));
74 g_signal_connect(G_OBJECT(ui->filter_button), "clicked",
75 G_CALLBACK(pan_filter_toggle_cb), pw);
77 /* Since we're using the GHashTable as a HashSet (in which key and value pointers
78 * are always identical), specifying key _and_ value destructor callbacks will
79 * cause a double-free.
81 ui->filter_kw_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
86 void pan_filter_ui_destroy(PanViewFilterUi **ui_ptr)
88 if (ui_ptr == NULL || *ui_ptr == NULL) return;
90 // Note that g_clear_pointer handles already-NULL pointers.
91 g_clear_pointer(&(*ui_ptr)->filter_kw_table, g_hash_table_destroy);
97 static void pan_filter_status(PanWindow *pw, const gchar *text)
99 gtk_label_set_text(GTK_LABEL(pw->filter_ui->filter_label), (text) ? text : "");
102 static void pan_filter_kw_button_cb(GtkButton *widget, gpointer data)
104 PanWindow *pw = data;
105 PanViewFilterUi *ui = pw->filter_ui;
107 g_hash_table_remove(ui->filter_kw_table, gtk_button_get_label(GTK_BUTTON(widget)));
108 gtk_widget_destroy(GTK_WIDGET(widget));
110 pan_filter_status(pw, _("Removed keyword…"));
111 pan_layout_update(pw);
114 void pan_filter_activate_cb(const gchar *text, gpointer data)
116 GtkWidget *kw_button;
117 PanWindow *pw = data;
118 PanViewFilterUi *ui = pw->filter_ui;
122 gtk_entry_set_text(GTK_ENTRY(ui->filter_entry), "");
124 if (g_hash_table_contains(ui->filter_kw_table, text))
126 pan_filter_status(pw, _("Already added…"));
130 tab_completion_append_to_history(ui->filter_entry, text);
132 g_hash_table_add(ui->filter_kw_table, g_strdup(text));
134 kw_button = gtk_button_new_with_label(text);
135 gtk_box_pack_start(GTK_BOX(ui->filter_kw_hbox), kw_button, FALSE, FALSE, 0);
136 gtk_widget_show(kw_button);
138 g_signal_connect(G_OBJECT(kw_button), "clicked",
139 G_CALLBACK(pan_filter_kw_button_cb), pw);
141 pan_filter_status(pw, _("Added keyword…"));
142 pan_layout_update(pw);
145 void pan_filter_activate(PanWindow *pw)
149 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->filter_ui->filter_entry)));
150 pan_filter_activate_cb(text, pw);
154 void pan_filter_toggle_cb(GtkWidget *button, gpointer data)
156 PanWindow *pw = data;
157 PanViewFilterUi *ui = pw->filter_ui;
160 visible = gtk_widget_get_visible(ui->filter_box);
161 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
165 gtk_widget_hide(ui->filter_box);
166 gtk_arrow_set(GTK_ARROW(ui->filter_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
170 gtk_widget_show(ui->filter_box);
171 gtk_arrow_set(GTK_ARROW(ui->filter_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
172 gtk_widget_grab_focus(ui->filter_entry);
176 void pan_filter_toggle_visible(PanWindow *pw, gboolean enable)
178 PanViewFilterUi *ui = pw->filter_ui;
183 if (gtk_widget_get_visible(ui->filter_box))
185 gtk_widget_grab_focus(ui->filter_entry);
189 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->filter_button), TRUE);
194 if (gtk_widget_get_visible(ui->filter_entry))
196 if (gtk_widget_has_focus(ui->filter_entry))
198 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
200 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->filter_button), FALSE);
205 gboolean pan_filter_fd_list(GList **fd_list, GHashTable *kw_table, PanViewFilterMode mode)
208 gboolean modified = FALSE;
209 GHashTableIter kw_iter;
213 if (!fd_list || !*fd_list || g_hash_table_size(kw_table) == 0) return modified;
215 // TODO(xsdg): Pay attention to filter mode.
219 FileData *fd = work->data;
220 GList *last_work = work;
223 // TODO(xsdg): OPTIMIZATION Do the search inside of metadata.c to avoid a
224 // bunch of string list copies.
225 GList *img_keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
228 *fd_list = g_list_delete_link(*fd_list, last_work);
233 gint match_count = 0;
235 g_hash_table_iter_init(&kw_iter, kw_table);
236 while (g_hash_table_iter_next(&kw_iter, (void**)&filter_kw, NULL))
238 if (g_list_find_custom(img_keywords, filter_kw, (GCompareFunc)g_strcmp0))
246 if (miss_count > 0) break;
249 string_list_free(img_keywords);
250 if (miss_count > 0 || match_count == 0)
252 *fd_list = g_list_delete_link(*fd_list, last_work);