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-search.h"
28 #include <glib-object.h>
34 #include "main-defines.h"
36 #include "pan-calendar.h"
38 #include "pan-types.h"
42 #include "ui-tabcomp.h"
44 PanViewSearchUi *pan_search_ui_new(PanWindow *pw)
46 auto ui = g_new0(PanViewSearchUi, 1);
50 // Build the actual search UI.
51 ui->search_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
52 pref_spacer(ui->search_box, 0);
53 pref_label_new(ui->search_box, _("Find:"));
55 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
56 gq_gtk_box_pack_start(GTK_BOX(ui->search_box), hbox, TRUE, TRUE, 0);
57 gtk_widget_show(hbox);
59 combo = tab_completion_new_with_history(&ui->search_entry, "", "pan_view_search", -1,
60 pan_search_activate_cb, pw);
61 gq_gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
62 gtk_widget_show(combo);
64 ui->search_label = gtk_label_new("");
65 gq_gtk_box_pack_start(GTK_BOX(hbox), ui->search_label, TRUE, TRUE, 0);
66 gtk_widget_show(ui->search_label);
68 // Build the spin-button to show/hide the search UI.
69 ui->search_button = gtk_toggle_button_new();
70 gtk_button_set_relief(GTK_BUTTON(ui->search_button), GTK_RELIEF_NONE);
71 gtk_widget_set_focus_on_click(ui->search_button, FALSE);
72 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
73 gtk_container_add(GTK_CONTAINER(ui->search_button), hbox);
74 gtk_widget_show(hbox);
75 ui->search_button_arrow = gtk_image_new_from_icon_name(GQ_ICON_PAN_UP, GTK_ICON_SIZE_BUTTON);
76 gq_gtk_box_pack_start(GTK_BOX(hbox), ui->search_button_arrow, FALSE, FALSE, 0);
77 gtk_widget_show(ui->search_button_arrow);
78 pref_label_new(hbox, _("Find"));
80 g_signal_connect(G_OBJECT(ui->search_button), "clicked",
81 G_CALLBACK(pan_search_toggle_cb), pw);
86 void pan_search_ui_destroy(PanViewSearchUi **ui_ptr)
88 if (ui_ptr == nullptr || *ui_ptr == nullptr) return;
94 static void pan_search_status(PanWindow *pw, const gchar *text)
96 gtk_label_set_text(GTK_LABEL(pw->search_ui->search_label), (text) ? text : "");
99 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
107 type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
109 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
110 if (!list) return FALSE;
112 found = g_list_find(list, pw->click_pi);
113 if (found && found->next)
116 pi = static_cast<PanItem *>(found->data);
120 pi = static_cast<PanItem *>(list->data);
123 pan_info_update(pw, pi);
124 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
126 buf = g_strdup_printf("%s ( %d / %d )",
127 (path[0] == G_DIR_SEPARATOR) ? _("path found") : _("filename found"),
128 g_list_index(list, pi) + 1,
129 g_list_length(list));
130 pan_search_status(pw, buf);
138 static gboolean pan_search_by_partial(PanWindow *pw, const gchar *text)
146 type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
148 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
149 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
154 needle = g_utf8_strdown(text, -1);
155 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
158 if (!list) return FALSE;
160 found = g_list_find(list, pw->click_pi);
161 if (found && found->next)
164 pi = static_cast<PanItem *>(found->data);
168 pi = static_cast<PanItem *>(list->data);
171 pan_info_update(pw, pi);
172 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
174 buf = g_strdup_printf("%s ( %d / %d )",
176 g_list_index(list, pi) + 1,
177 g_list_length(list));
178 pan_search_status(pw, buf);
186 static gboolean valid_date_separator(gchar c)
188 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
191 static GList *pan_search_by_date_val(PanWindow *pw, PanItemType type,
192 gint year, gint month, gint day,
195 GList *list = nullptr;
198 work = g_list_last(pw->list_static);
203 pi = static_cast<PanItem *>(work->data);
206 if (pi->fd && (pi->type == type || type == PAN_ITEM_NONE) &&
207 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
211 tl = localtime(&pi->fd->date);
216 match = (tl->tm_year == year - 1900);
217 if (match && month >= 0) match = (tl->tm_mon == month - 1);
218 if (match && day > 0) match = (tl->tm_mday == day);
220 if (match) list = g_list_prepend(list, pi);
225 return g_list_reverse(list);
228 static gboolean pan_search_by_date(PanWindow *pw, const gchar *text)
230 PanItem *pi = nullptr;
231 GList *list = nullptr;
244 if (!text) return FALSE;
246 ptr = const_cast<gchar *>(text);
249 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
254 if (t == -1) return FALSE;
256 if (!lt) return FALSE;
258 if (valid_date_separator(*text))
261 mptr = const_cast<gchar *>(text);
265 year = static_cast<gint>(strtol(text, &mptr, 10));
266 if (mptr == text) return FALSE;
269 if (*mptr != '\0' && valid_date_separator(*mptr))
274 month = strtol(mptr, &dptr, 10);
277 if (valid_date_separator(*dptr))
279 month = lt->tm_mon + 1;
287 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
291 day = strtol(dptr, &eptr, 10);
301 year = lt->tm_year + 1900;
312 month < -1 || month == 0 || month > 12 ||
313 day < -1 || day == 0 || day > 31) return FALSE;
315 t = pan_date_to_time(year, month, day);
316 if (t < 0) return FALSE;
318 if (pw->layout == PAN_LAYOUT_CALENDAR)
320 list = pan_search_by_date_val(pw, PAN_ITEM_BOX, year, month, day, "day");
326 type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
327 list = pan_search_by_date_val(pw, type, year, month, day, nullptr);
332 found = g_list_find(list, pw->search_pi);
333 if (found && found->next)
336 pi = static_cast<PanItem *>(found->data);
340 pi = static_cast<PanItem *>(list->data);
346 if (pw->layout == PAN_LAYOUT_CALENDAR && pi && pi->type == PAN_ITEM_BOX)
348 pan_info_update(pw, nullptr);
349 pan_calendar_update(pw, pi);
350 image_scroll_to_point(pw->imd,
351 pi->x + pi->width / 2,
352 pi->y + pi->height / 2, 0.5, 0.5);
356 pan_info_update(pw, pi);
357 image_scroll_to_point(pw->imd,
358 pi->x - PAN_BOX_BORDER * 5 / 2,
364 buf = pan_date_value_string(t, PAN_DATE_LENGTH_MONTH);
369 buf = g_strdup_printf("%d %s", day, tmp);
375 buf = pan_date_value_string(t, PAN_DATE_LENGTH_YEAR);
380 buf_count = g_strdup_printf("( %d / %d )",
381 g_list_index(list, pi) + 1,
382 g_list_length(list));
386 buf_count = g_strdup_printf("(%s)", _("no match"));
389 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
392 pan_search_status(pw, message);
400 void pan_search_activate_cb(const gchar *text, gpointer data)
402 auto pw = static_cast<PanWindow *>(data);
406 tab_completion_append_to_history(pw->search_ui->search_entry, text);
408 if (pan_search_by_path(pw, text)) return;
410 if ((pw->layout == PAN_LAYOUT_TIMELINE ||
411 pw->layout == PAN_LAYOUT_CALENDAR) &&
412 pan_search_by_date(pw, text))
417 if (pan_search_by_partial(pw, text)) return;
419 pan_search_status(pw, _("no match"));
422 void pan_search_activate(PanWindow *pw)
426 text = g_strdup(gq_gtk_entry_get_text(GTK_ENTRY(pw->search_ui->search_entry)));
427 pan_search_activate_cb(text, pw);
431 void pan_search_toggle_cb(GtkWidget *button, gpointer data)
433 auto pw = static_cast<PanWindow *>(data);
434 PanViewSearchUi *ui = pw->search_ui;
438 visible = gtk_widget_get_visible(ui->search_box);
439 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
443 gtk_widget_hide(ui->search_box);
445 parent = gtk_widget_get_parent(ui->search_button_arrow);
447 gtk_container_remove(GTK_CONTAINER(parent), ui->search_button_arrow);
448 ui->search_button_arrow = gtk_image_new_from_icon_name(GQ_ICON_PAN_UP, GTK_ICON_SIZE_BUTTON);
450 gq_gtk_box_pack_start(GTK_BOX(parent), ui->search_button_arrow, FALSE, FALSE, 0);
451 gtk_box_reorder_child(GTK_BOX(parent), ui->search_button_arrow, 0);
453 gtk_widget_show(ui->search_button_arrow);
457 gtk_widget_show(ui->search_box);
459 parent = gtk_widget_get_parent(ui->search_button_arrow);
461 gtk_container_remove(GTK_CONTAINER(parent), ui->search_button_arrow);
462 ui->search_button_arrow = gtk_image_new_from_icon_name(GQ_ICON_PAN_DOWN, GTK_ICON_SIZE_BUTTON);
464 gq_gtk_box_pack_start(GTK_BOX(parent), ui->search_button_arrow, FALSE, FALSE, 0);
465 gtk_box_reorder_child(GTK_BOX(parent), ui->search_button_arrow, 0);
467 gtk_widget_show(ui->search_button_arrow);
468 gtk_widget_grab_focus(ui->search_entry);
472 void pan_search_toggle_visible(PanWindow *pw, gboolean enable)
474 PanViewSearchUi *ui = pw->search_ui;
479 if (gtk_widget_get_visible(ui->search_box))
481 gtk_widget_grab_focus(ui->search_entry);
485 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->search_button), TRUE);
490 if (gtk_widget_get_visible(ui->search_entry))
492 if (gtk_widget_has_focus(ui->search_entry))
494 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
496 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->search_button), FALSE);