<listitem>\r
<para>\r
Enable this to save thumbnails to disk, subsequent requests for a thumbnail will be faster. Thumbnails are cached into:\r
- <programlisting>$HOME/.cache/.geeqie/thumbnails</programlisting>\r
+ <programlisting>$XDG_CACHE_HOME/geeqie/thumbnails/</programlisting>\r
+ <programlisting>($~/.cache/geeqie/thumbnails/)</programlisting>\r
Refer to\r
<link linkend="GuideReferenceThumbnails">Thumbnails Reference</link>\r
for additional details.\r
<section id="GuideReferenceConfig">\r
<title id="titleGuideReferenceConfig">Configuration Files and Locations</title>\r
<para>The following data lists the locations Geeqie uses for various actions. The uppercase symbols are environment variables. If they are not set on your system the fallback locations are listed in parentheses.</para>\r
+ <para>\r
+ Geqqie will first attempt to load a configuration file from:\r
+ <programlisting xml:space="preserve">/etc/geeqie/geeqierc.xml</programlisting>\r
+ </para>\r
+ <para>It will then continue with the following locations.</para>\r
<para>\r
Most of Geeqie's configuration files are contained in the folder, and sub-folders of:\r
- <programlisting xml:space="preserve">$HOME/$XDG_CONFIG_HOME/geeqie/</programlisting>\r
+ <programlisting xml:space="preserve">$XDG_CONFIG_HOME/geeqie/</programlisting>\r
<programlisting xml:space="preserve">($~/.config/geeqie/)</programlisting>\r
</para>\r
<para>\r
<programlisting xml:space="preserve">.../accels</programlisting>\r
</para>\r
<para>\r
- The default location for Collections is in the folder:\r
- <programlisting xml:space="preserve">$HOME/.local/share/geeqie/collections</programlisting>\r
+ The location for Collections is in the folder:\r
+ <programlisting xml:space="preserve">$XDG_DATA_HOME/geeqie/collections</programlisting>\r
+ <programlisting xml:space="preserve">($~/.local/share/geeqie/collections)</programlisting>\r
</para>\r
<para>\r
The lirc\r
if (info == TARGET_TEXT_PLAIN)
{
- location = decode_geo_parameters(gtk_selection_data_get_data(selection_data));
+ location = decode_geo_parameters((gchar *)gtk_selection_data_get_data(selection_data));
if (!(g_strstr_len(location,-1,"Error")))
{
latlong = g_strsplit(location, " ", 2);
gboolean still_have_a_file = TRUE;
gsize base_length;
const gchar *cache_folder;
+ gboolean filter_disable;
if (cm->metadata)
{
DEBUG_1("purge chk (%d) \"%s\"", (cm->clear && !cm->metadata), fd->path);
+/**
+ * It is necessary to disable the file filter when clearing the cache,
+ * otherwise the .sim (file similarity) files are not deleted.
+ */
+ filter_disable = options->file_filter.disable;
+ options->file_filter.disable = TRUE;
+
if (g_list_find(cm->done_list, fd) == NULL)
{
cm->done_list = g_list_prepend(cm->done_list, fd);
}
}
}
+ options->file_filter.disable = filter_disable;
+
filelist_free(list);
cm->list = g_list_concat(dlist, cm->list);
button = pref_table_button(table, 0, 1, GTK_STOCK_DELETE, _("Clear cache"), FALSE,
G_CALLBACK(cache_manager_main_clear_cb), cache_manager);
gtk_size_group_add_widget(sizegroup, button);
- pref_table_label(table, 1, 1, _("Delete all cached thumbnails."), 0.0);
+ pref_table_label(table, 1, 1, _("Delete all cached data."), 0.0);
group = pref_group_new(gd->vbox, FALSE, _("Shared thumbnail cache"), GTK_ORIENTATION_VERTICAL);
GdkPixbuf *pixbuf;
const gchar *text;
GdkRectangle cell_rect;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStateFlags state;
+#else
GtkStateType state;
+#endif
gint xpad, ypad;
if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
{
if (gtk_widget_has_focus(widget))
+#if GTK_CHECK_VERSION(3,0,0)
+ state = GTK_STATE_FLAG_SELECTED;
+ else
+ state = GTK_STATE_FLAG_ACTIVE;
+#else
state = GTK_STATE_SELECTED;
else
state = GTK_STATE_ACTIVE;
+#endif
}
else
{
if (gtk_widget_get_state(widget) == GTK_STATE_INSENSITIVE)
+#if GTK_CHECK_VERSION(3,0,0)
+ state = GTK_STATE_FLAG_INSENSITIVE;
+ else
+ state = GTK_STATE_FLAG_NORMAL;
+#else
state = GTK_STATE_INSENSITIVE;
else
state = GTK_STATE_NORMAL;
+#endif
}
#if GTK_CHECK_VERSION(3,0,0)
%D%/pan-util.c \
%D%/pan-util.h \
%D%/pan-view.c \
- %D%/pan-view.h
+ %D%/pan-view.h \
+ %D%/pan-view-filter.c \
+ %D%/pan-view-filter.h \
+ %D%/pan-view-search.c \
+ %D%/pan-view-search.h
+
#include "pan-util.h"
#include "pan-view.h"
+#include "pan-view-filter.h"
#include "pixbuf_util.h"
#define PAN_CAL_POPUP_COLOR 220, 220, 220
gint end_month = 0;
list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
+ pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
if (pw->cache_list && pw->exif_date_enable)
{
#include "pan-item.h"
#include "pan-util.h"
+#include "pan-view-filter.h"
static void pan_flower_size(PanWindow *pw, gint *width, gint *height)
{
f = filelist_sort(f, SORT_NAME, TRUE);
d = filelist_sort(d, SORT_NAME, TRUE);
+ pan_filter_fd_list(&f, pw->filter_ui->filter_elements);
+
pi_box = pan_item_text_new(pw, x, y, dir_fd->path, PAN_TEXT_ATTR_NONE,
PAN_TEXT_BORDER_SIZE,
PAN_TEXT_COLOR, 255);
f = filelist_sort(f, SORT_NAME, TRUE);
d = filelist_sort(d, SORT_NAME, TRUE);
+ pan_filter_fd_list(&f, pw->filter_ui->filter_elements);
+
*x = PAN_BOX_BORDER + ((*level) * MAX(PAN_BOX_BORDER, PAN_THUMB_GAP));
pi_box = pan_item_text_new(pw, *x, *y, dir_fd->path, PAN_TEXT_ATTR_NONE,
#include "pan-item.h"
#include "pan-util.h"
+#include "pan-view-filter.h"
void pan_grid_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
{
gint next_y;
list = pan_list_tree(dir_fd, SORT_NAME, TRUE, pw->ignore_symlinks);
+ pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
grid_size = (gint)sqrt((gdouble)g_list_length(list));
if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
#include "pan-item.h"
#include "pan-util.h"
#include "pan-view.h"
+#include "pan-view-filter.h"
void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
{
GList *list;
GList *work;
gint x, y;
- time_t tc;
+ time_t group_start_date;
gint total;
gint count;
PanItem *pi_month = NULL;
gint y_height;
list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
+ pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
if (pw->cache_list && pw->exif_date_enable)
{
day_start = month_start;
x_width = 0;
y_height = 0;
- tc = 0;
+ group_start_date = 0;
+ // total and count are used to enforce a stride of PAN_GROUP_MAX thumbs.
total = 0;
count = 0;
work = list;
fd = work->data;
work = work->next;
- if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_DAY))
+ if (!pan_date_compare(fd->date, group_start_date, PAN_DATE_LENGTH_DAY))
{
+ // FD starts a new day group.
GList *needle;
gchar *buf;
- if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_MONTH))
+ if (!pan_date_compare(fd->date, group_start_date, PAN_DATE_LENGTH_MONTH))
{
+ // FD starts a new month group.
pi_day = NULL;
if (pi_month)
if (pi_day) x = pi_day->x + pi_day->width + PAN_BOX_BORDER;
- tc = fd->date;
+ group_start_date = fd->date;
total = 1;
count = 0;
FileData *nfd;
nfd = needle->data;
- if (pan_date_compare(nfd->date, tc, PAN_DATE_LENGTH_DAY))
+ if (pan_date_compare(nfd->date, group_start_date, PAN_DATE_LENGTH_DAY))
{
needle = needle->next;
total++;
gboolean queued;
};
+typedef struct _PanViewSearchUi PanViewSearchUi;
+struct _PanViewSearchUi
+{
+ GtkWidget *search_box;
+ GtkWidget *search_entry;
+ GtkWidget *search_label;
+ GtkWidget *search_button;
+ GtkWidget *search_button_arrow;
+};
+
+// Defined in pan-view-filter.h
+typedef struct _PanViewFilterUi PanViewFilterUi;
+
typedef struct _PanWindow PanWindow;
struct _PanWindow
{
GtkWidget *label_message;
GtkWidget *label_zoom;
- GtkWidget *search_box;
- GtkWidget *search_entry;
- GtkWidget *search_label;
- GtkWidget *search_button;
- GtkWidget *search_button_arrow;
+ PanViewSearchUi *search_ui;
+ PanViewFilterUi *filter_ui;
GtkWidget *date_button;
--- /dev/null
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-view-filter.h"
+
+#include "image.h"
+#include "metadata.h"
+#include "pan-item.h"
+#include "pan-util.h"
+#include "pan-view.h"
+#include "ui_fileops.h"
+#include "ui_tabcomp.h"
+#include "ui_misc.h"
+
+PanViewFilterUi *pan_filter_ui_new(PanWindow *pw)
+{
+ PanViewFilterUi *ui = g_new0(PanViewFilterUi, 1);
+ GtkWidget *combo;
+ GtkWidget *hbox;
+
+ /* Since we're using the GHashTable as a HashSet (in which key and value pointers
+ * are always identical), specifying key _and_ value destructor callbacks will
+ * cause a double-free.
+ */
+ {
+ GtkTreeIter iter;
+ ui->filter_mode_model = gtk_list_store_new(3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_list_store_append(ui->filter_mode_model, &iter);
+ gtk_list_store_set(ui->filter_mode_model, &iter,
+ 0, PAN_VIEW_FILTER_REQUIRE, 1, _("Require"), 2, _("R"), -1);
+ gtk_list_store_append(ui->filter_mode_model, &iter);
+ gtk_list_store_set(ui->filter_mode_model, &iter,
+ 0, PAN_VIEW_FILTER_EXCLUDE, 1, _("Exclude"), 2, _("E"), -1);
+ gtk_list_store_append(ui->filter_mode_model, &iter);
+ gtk_list_store_set(ui->filter_mode_model, &iter,
+ 0, PAN_VIEW_FILTER_INCLUDE, 1, _("Include"), 2, _("I"), -1);
+ gtk_list_store_append(ui->filter_mode_model, &iter);
+ gtk_list_store_set(ui->filter_mode_model, &iter,
+ 0, PAN_VIEW_FILTER_GROUP, 1, _("Group"), 2, _("G"), -1);
+
+ ui->filter_mode_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(ui->filter_mode_model));
+ gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(ui->filter_mode_combo), FALSE);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(ui->filter_mode_combo), 0);
+
+ GtkCellRenderer *render = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ui->filter_mode_combo), render, TRUE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ui->filter_mode_combo), render, "text", 1, NULL);
+ }
+
+ // Build the actual filter UI.
+ ui->filter_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+ pref_spacer(ui->filter_box, 0);
+ pref_label_new(ui->filter_box, _("Keyword Filter:"));
+
+ gtk_box_pack_start(GTK_BOX(ui->filter_box), ui->filter_mode_combo, TRUE, TRUE, 0);
+ gtk_widget_show(ui->filter_mode_combo);
+
+ hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
+ gtk_box_pack_start(GTK_BOX(ui->filter_box), hbox, TRUE, TRUE, 0);
+ gtk_widget_show(hbox);
+
+ combo = tab_completion_new_with_history(&ui->filter_entry, "", "pan_view_filter", -1,
+ pan_filter_activate_cb, pw);
+ gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show(combo);
+
+ // TODO(xsdg): Figure out whether it's useful to keep this label around.
+ ui->filter_label = gtk_label_new("");
+ //gtk_box_pack_start(GTK_BOX(hbox), ui->filter_label, FALSE, FALSE, 0);
+ //gtk_widget_show(ui->filter_label);
+
+ ui->filter_kw_hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+ gtk_box_pack_start(GTK_BOX(hbox), ui->filter_kw_hbox, TRUE, TRUE, 0);
+ gtk_widget_show(ui->filter_kw_hbox);
+
+ // Build the spin-button to show/hide the filter UI.
+ ui->filter_button = gtk_toggle_button_new();
+ gtk_button_set_relief(GTK_BUTTON(ui->filter_button), GTK_RELIEF_NONE);
+ gtk_button_set_focus_on_click(GTK_BUTTON(ui->filter_button), FALSE);
+ hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
+ gtk_container_add(GTK_CONTAINER(ui->filter_button), hbox);
+ gtk_widget_show(hbox);
+ ui->filter_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
+ gtk_box_pack_start(GTK_BOX(hbox), ui->filter_button_arrow, FALSE, FALSE, 0);
+ gtk_widget_show(ui->filter_button_arrow);
+ pref_label_new(hbox, _("Filter"));
+
+ g_signal_connect(G_OBJECT(ui->filter_button), "clicked",
+ G_CALLBACK(pan_filter_toggle_cb), pw);
+
+ return ui;
+}
+
+void pan_filter_ui_destroy(PanViewFilterUi **ui_ptr)
+{
+ if (ui_ptr == NULL || *ui_ptr == NULL) return;
+
+ // Note that g_clear_pointer handles already-NULL pointers.
+ //g_clear_pointer(&(*ui_ptr)->filter_kw_table, g_hash_table_destroy);
+
+ g_free(*ui_ptr);
+ *ui_ptr = NULL;
+}
+
+static void pan_filter_status(PanWindow *pw, const gchar *text)
+{
+ gtk_label_set_text(GTK_LABEL(pw->filter_ui->filter_label), (text) ? text : "");
+}
+
+static void pan_filter_kw_button_cb(GtkButton *widget, gpointer data)
+{
+ PanFilterCallbackState *cb_state = data;
+ PanWindow *pw = cb_state->pw;
+ PanViewFilterUi *ui = pw->filter_ui;
+
+ // TODO(xsdg): Fix filter element pointed object memory leak.
+ ui->filter_elements = g_list_delete_link(ui->filter_elements, cb_state->filter_element);
+ gtk_widget_destroy(GTK_WIDGET(widget));
+ g_free(cb_state);
+
+ pan_filter_status(pw, _("Removed keyword…"));
+ pan_layout_update(pw);
+}
+
+void pan_filter_activate_cb(const gchar *text, gpointer data)
+{
+ GtkWidget *kw_button;
+ PanWindow *pw = data;
+ PanViewFilterUi *ui = pw->filter_ui;
+ GtkTreeIter iter;
+
+ if (!text) return;
+
+ // Get all relevant state and reset UI.
+ gtk_combo_box_get_active_iter(GTK_COMBO_BOX(ui->filter_mode_combo), &iter);
+ gtk_entry_set_text(GTK_ENTRY(ui->filter_entry), "");
+ tab_completion_append_to_history(ui->filter_entry, text);
+
+ // Add new filter element.
+ PanViewFilterElement *element = g_new0(PanViewFilterElement, 1);
+ gtk_tree_model_get(GTK_TREE_MODEL(ui->filter_mode_model), &iter, 0, &element->mode, -1);
+ element->keyword = g_strdup(text);
+ if (g_strcmp0(text, g_regex_escape_string(text, -1)))
+ {
+ // It's an actual regex, so compile
+ element->kw_regex = g_regex_new(text, G_REGEX_ANCHORED | G_REGEX_OPTIMIZE, G_REGEX_MATCH_ANCHORED, NULL);
+ }
+ ui->filter_elements = g_list_append(ui->filter_elements, element);
+
+ // Get the short version of the mode value.
+ gchar *short_mode;
+ gtk_tree_model_get(GTK_TREE_MODEL(ui->filter_mode_model), &iter, 2, &short_mode, -1);
+
+ // Create the button.
+ // TODO(xsdg): Use MVC so that the button list is an actual representation of the GList
+ gchar *label = g_strdup_printf("(%s) %s", short_mode, text);
+ kw_button = gtk_button_new_with_label(label);
+ g_clear_pointer(&label, g_free);
+
+ gtk_box_pack_start(GTK_BOX(ui->filter_kw_hbox), kw_button, FALSE, FALSE, 0);
+ gtk_widget_show(kw_button);
+
+ PanFilterCallbackState *cb_state = g_new0(PanFilterCallbackState, 1);
+ cb_state->pw = pw;
+ cb_state->filter_element = g_list_last(ui->filter_elements);
+
+ g_signal_connect(G_OBJECT(kw_button), "clicked",
+ G_CALLBACK(pan_filter_kw_button_cb), cb_state);
+
+ pan_layout_update(pw);
+}
+
+void pan_filter_activate(PanWindow *pw)
+{
+ gchar *text;
+
+ text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->filter_ui->filter_entry)));
+ pan_filter_activate_cb(text, pw);
+ g_free(text);
+}
+
+void pan_filter_toggle_cb(GtkWidget *button, gpointer data)
+{
+ PanWindow *pw = data;
+ PanViewFilterUi *ui = pw->filter_ui;
+ gboolean visible;
+
+ visible = gtk_widget_get_visible(ui->filter_box);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
+
+ if (visible)
+ {
+ gtk_widget_hide(ui->filter_box);
+ gtk_arrow_set(GTK_ARROW(ui->filter_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
+ }
+ else
+ {
+ gtk_widget_show(ui->filter_box);
+ gtk_arrow_set(GTK_ARROW(ui->filter_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_widget_grab_focus(ui->filter_entry);
+ }
+}
+
+void pan_filter_toggle_visible(PanWindow *pw, gboolean enable)
+{
+ PanViewFilterUi *ui = pw->filter_ui;
+ if (pw->fs) return;
+
+ if (enable)
+ {
+ if (gtk_widget_get_visible(ui->filter_box))
+ {
+ gtk_widget_grab_focus(ui->filter_entry);
+ }
+ else
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->filter_button), TRUE);
+ }
+ }
+ else
+ {
+ if (gtk_widget_get_visible(ui->filter_entry))
+ {
+ if (gtk_widget_has_focus(ui->filter_entry))
+ {
+ gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
+ }
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->filter_button), FALSE);
+ }
+ }
+}
+
+static gboolean pan_view_list_contains_kw_pattern(GList *haystack, PanViewFilterElement *filter, gchar **found_kw)
+{
+ if (filter->kw_regex)
+ {
+ // regex compile succeeded; attempt regex match.
+ GList *work = g_list_first(haystack);
+ while (work)
+ {
+ gchar *keyword = work->data;
+ work = work->next;
+ if (g_regex_match(filter->kw_regex, keyword, 0x0, NULL))
+ {
+ if (found_kw) *found_kw = keyword;
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ else
+ {
+ // regex compile failed; fall back to exact string match.
+ GList *found_elem = g_list_find_custom(haystack, filter->keyword, (GCompareFunc)g_strcmp0);
+ if (found_elem && found_kw) *found_kw = found_elem->data;
+ return !!found_elem;
+ }
+}
+
+gboolean pan_filter_fd_list(GList **fd_list, GList *filter_elements)
+{
+ GList *work;
+ gboolean modified = FALSE;
+ GHashTable *seen_kw_table = NULL;
+
+ if (!fd_list || !*fd_list || !filter_elements) return modified;
+
+ // seen_kw_table is only valid in this scope, so don't take ownership of any strings.
+ seen_kw_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+
+ work = *fd_list;
+ while (work)
+ {
+ FileData *fd = work->data;
+ GList *last_work = work;
+ work = work->next;
+
+ // TODO(xsdg): OPTIMIZATION Do the search inside of metadata.c to avoid a
+ // bunch of string list copies.
+ GList *img_keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
+
+ // TODO(xsdg): OPTIMIZATION Determine a heuristic for when to linear-search the
+ // keywords list, and when to build a hash table for the image's keywords.
+ gboolean should_reject = FALSE;
+ gchar *group_kw = NULL;
+ GList *filter_element = filter_elements;
+ while (filter_element)
+ {
+ PanViewFilterElement *filter = filter_element->data;
+ filter_element = filter_element->next;
+ gchar *found_kw = NULL;
+ gboolean has_kw = pan_view_list_contains_kw_pattern(img_keywords, filter, &found_kw);
+
+ switch (filter->mode)
+ {
+ case PAN_VIEW_FILTER_REQUIRE:
+ should_reject |= !has_kw;
+ break;
+ case PAN_VIEW_FILTER_EXCLUDE:
+ should_reject |= has_kw;
+ break;
+ case PAN_VIEW_FILTER_INCLUDE:
+ if (has_kw) should_reject = FALSE;
+ break;
+ case PAN_VIEW_FILTER_GROUP:
+ if (has_kw)
+ {
+ if (g_hash_table_contains(seen_kw_table, found_kw))
+ {
+ should_reject = TRUE;
+ }
+ else if (group_kw == NULL)
+ {
+ group_kw = found_kw;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!should_reject && group_kw != NULL) g_hash_table_add(seen_kw_table, group_kw);
+
+ group_kw = NULL; // group_kw references an item from img_keywords.
+ string_list_free(img_keywords);
+
+ if (should_reject)
+ {
+ *fd_list = g_list_delete_link(*fd_list, last_work);
+ modified = TRUE;
+ }
+ }
+
+ g_hash_table_destroy(seen_kw_table);
+ return modified;
+}
--- /dev/null
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_VIEW_FILTER_H
+#define PAN_VIEW_PAN_VIEW_FILTER_H
+
+#include "main.h"
+#include "pan-types.h"
+
+typedef enum {
+ PAN_VIEW_FILTER_REQUIRE,
+ PAN_VIEW_FILTER_EXCLUDE,
+ PAN_VIEW_FILTER_INCLUDE,
+ PAN_VIEW_FILTER_GROUP
+} PanViewFilterMode;
+
+typedef struct _PanViewFilterElement PanViewFilterElement;
+struct _PanViewFilterElement
+{
+ PanViewFilterMode mode;
+ gchar *keyword;
+ GRegex *kw_regex;
+};
+
+typedef struct _PanFilterCallbackState PanFilterCallbackState;
+struct _PanFilterCallbackState
+{
+ PanWindow *pw;
+ GList *filter_element;
+};
+
+struct _PanViewFilterUi
+{
+ GtkWidget *filter_box;
+ GtkWidget *filter_entry;
+ GtkWidget *filter_label;
+ GtkWidget *filter_button;
+ GtkWidget *filter_button_arrow;
+ GtkWidget *filter_kw_hbox;
+ GtkListStore *filter_mode_model;
+ GtkWidget *filter_mode_combo;
+ GList *filter_elements; // List of PanViewFilterElement.
+};
+
+void pan_filter_toggle_visible(PanWindow *pw, gboolean enable);
+void pan_filter_activate(PanWindow *pw);
+void pan_filter_activate_cb(const gchar *text, gpointer data);
+void pan_filter_toggle_cb(GtkWidget *button, gpointer data);
+
+// Creates a new PanViewFilterUi instance and returns it.
+PanViewFilterUi *pan_filter_ui_new(PanWindow *pw);
+
+// Destroys the specified PanViewFilterUi and sets the pointer to NULL.
+void pan_filter_ui_destroy(PanViewFilterUi **ui);
+
+gboolean pan_filter_fd_list(GList **fd_list, GList *filter_elements);
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-view-search.h"
+
+#include "image.h"
+#include "pan-calendar.h"
+#include "pan-item.h"
+#include "pan-util.h"
+#include "pan-view.h"
+#include "ui_tabcomp.h"
+#include "ui_misc.h"
+
+PanViewSearchUi *pan_search_ui_new(PanWindow *pw)
+{
+ PanViewSearchUi *ui = g_new0(PanViewSearchUi, 1);
+ GtkWidget *combo;
+ GtkWidget *hbox;
+
+ // Build the actual search UI.
+ ui->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+ pref_spacer(ui->search_box, 0);
+ pref_label_new(ui->search_box, _("Find:"));
+
+ hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
+ gtk_box_pack_start(GTK_BOX(ui->search_box), hbox, TRUE, TRUE, 0);
+ gtk_widget_show(hbox);
+
+ combo = tab_completion_new_with_history(&ui->search_entry, "", "pan_view_search", -1,
+ pan_search_activate_cb, pw);
+ gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show(combo);
+
+ ui->search_label = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(hbox), ui->search_label, TRUE, TRUE, 0);
+ gtk_widget_show(ui->search_label);
+
+ // Build the spin-button to show/hide the search UI.
+ ui->search_button = gtk_toggle_button_new();
+ gtk_button_set_relief(GTK_BUTTON(ui->search_button), GTK_RELIEF_NONE);
+ gtk_button_set_focus_on_click(GTK_BUTTON(ui->search_button), FALSE);
+ hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
+ gtk_container_add(GTK_CONTAINER(ui->search_button), hbox);
+ gtk_widget_show(hbox);
+ ui->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
+ gtk_box_pack_start(GTK_BOX(hbox), ui->search_button_arrow, FALSE, FALSE, 0);
+ gtk_widget_show(ui->search_button_arrow);
+ pref_label_new(hbox, _("Find"));
+
+ g_signal_connect(G_OBJECT(ui->search_button), "clicked",
+ G_CALLBACK(pan_search_toggle_cb), pw);
+
+ return ui;
+}
+
+void pan_search_ui_destroy(PanViewSearchUi **ui_ptr)
+{
+ if (ui_ptr == NULL || *ui_ptr == NULL) return;
+
+ g_free(*ui_ptr);
+ *ui_ptr = NULL;
+}
+
+static void pan_search_status(PanWindow *pw, const gchar *text)
+{
+ gtk_label_set_text(GTK_LABEL(pw->search_ui->search_label), (text) ? text : "");
+}
+
+static gint pan_search_by_path(PanWindow *pw, const gchar *path)
+{
+ PanItem *pi;
+ GList *list;
+ GList *found;
+ PanItemType type;
+ gchar *buf;
+
+ type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+
+ list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
+ if (!list) return FALSE;
+
+ found = g_list_find(list, pw->click_pi);
+ if (found && found->next)
+ {
+ found = found->next;
+ pi = found->data;
+ }
+ else
+ {
+ pi = list->data;
+ }
+
+ pan_info_update(pw, pi);
+ image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
+
+ buf = g_strdup_printf("%s ( %d / %d )",
+ (path[0] == G_DIR_SEPARATOR) ? _("path found") : _("filename found"),
+ g_list_index(list, pi) + 1,
+ g_list_length(list));
+ pan_search_status(pw, buf);
+ g_free(buf);
+
+ g_list_free(list);
+
+ return TRUE;
+}
+
+static gboolean pan_search_by_partial(PanWindow *pw, const gchar *text)
+{
+ PanItem *pi;
+ GList *list;
+ GList *found;
+ PanItemType type;
+ gchar *buf;
+
+ type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+
+ list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
+ if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
+ if (!list)
+ {
+ gchar *needle;
+
+ needle = g_utf8_strdown(text, -1);
+ list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
+ g_free(needle);
+ }
+ if (!list) return FALSE;
+
+ found = g_list_find(list, pw->click_pi);
+ if (found && found->next)
+ {
+ found = found->next;
+ pi = found->data;
+ }
+ else
+ {
+ pi = list->data;
+ }
+
+ pan_info_update(pw, pi);
+ image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
+
+ buf = g_strdup_printf("%s ( %d / %d )",
+ _("partial match"),
+ g_list_index(list, pi) + 1,
+ g_list_length(list));
+ pan_search_status(pw, buf);
+ g_free(buf);
+
+ g_list_free(list);
+
+ return TRUE;
+}
+
+static gboolean valid_date_separator(gchar c)
+{
+ return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
+}
+
+static GList *pan_search_by_date_val(PanWindow *pw, PanItemType type,
+ gint year, gint month, gint day,
+ const gchar *key)
+{
+ GList *list = NULL;
+ GList *work;
+
+ work = g_list_last(pw->list_static);
+ while (work)
+ {
+ PanItem *pi;
+
+ pi = work->data;
+ work = work->prev;
+
+ if (pi->fd && (pi->type == type || type == PAN_ITEM_NONE) &&
+ ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
+ {
+ struct tm *tl;
+
+ tl = localtime(&pi->fd->date);
+ if (tl)
+ {
+ gint match;
+
+ match = (tl->tm_year == year - 1900);
+ if (match && month >= 0) match = (tl->tm_mon == month - 1);
+ if (match && day > 0) match = (tl->tm_mday == day);
+
+ if (match) list = g_list_prepend(list, pi);
+ }
+ }
+ }
+
+ return g_list_reverse(list);
+}
+
+static gboolean pan_search_by_date(PanWindow *pw, const gchar *text)
+{
+ PanItem *pi = NULL;
+ GList *list = NULL;
+ GList *found;
+ gint year;
+ gint month = -1;
+ gint day = -1;
+ gchar *ptr;
+ gchar *mptr;
+ struct tm *lt;
+ time_t t;
+ gchar *message;
+ gchar *buf;
+ gchar *buf_count;
+
+ if (!text) return FALSE;
+
+ ptr = (gchar *)text;
+ while (*ptr != '\0')
+ {
+ if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
+ ptr++;
+ }
+
+ t = time(NULL);
+ if (t == -1) return FALSE;
+ lt = localtime(&t);
+ if (!lt) return FALSE;
+
+ if (valid_date_separator(*text))
+ {
+ year = -1;
+ mptr = (gchar *)text;
+ }
+ else
+ {
+ year = (gint)strtol(text, &mptr, 10);
+ if (mptr == text) return FALSE;
+ }
+
+ if (*mptr != '\0' && valid_date_separator(*mptr))
+ {
+ gchar *dptr;
+
+ mptr++;
+ month = strtol(mptr, &dptr, 10);
+ if (dptr == mptr)
+ {
+ if (valid_date_separator(*dptr))
+ {
+ month = lt->tm_mon + 1;
+ dptr++;
+ }
+ else
+ {
+ month = -1;
+ }
+ }
+ if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
+ {
+ gchar *eptr;
+ dptr++;
+ day = strtol(dptr, &eptr, 10);
+ if (dptr == eptr)
+ {
+ day = lt->tm_mday;
+ }
+ }
+ }
+
+ if (year == -1)
+ {
+ year = lt->tm_year + 1900;
+ }
+ else if (year < 100)
+ {
+ if (year > 70)
+ year+= 1900;
+ else
+ year+= 2000;
+ }
+
+ if (year < 1970 ||
+ month < -1 || month == 0 || month > 12 ||
+ day < -1 || day == 0 || day > 31) return FALSE;
+
+ t = pan_date_to_time(year, month, day);
+ if (t < 0) return FALSE;
+
+ if (pw->layout == PAN_LAYOUT_CALENDAR)
+ {
+ list = pan_search_by_date_val(pw, PAN_ITEM_BOX, year, month, day, "day");
+ }
+ else
+ {
+ PanItemType type;
+
+ type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+ list = pan_search_by_date_val(pw, type, year, month, day, NULL);
+ }
+
+ if (list)
+ {
+ found = g_list_find(list, pw->search_pi);
+ if (found && found->next)
+ {
+ found = found->next;
+ pi = found->data;
+ }
+ else
+ {
+ pi = list->data;
+ }
+ }
+
+ pw->search_pi = pi;
+
+ if (pw->layout == PAN_LAYOUT_CALENDAR && pi && pi->type == PAN_ITEM_BOX)
+ {
+ pan_info_update(pw, NULL);
+ pan_calendar_update(pw, pi);
+ image_scroll_to_point(pw->imd,
+ pi->x + pi->width / 2,
+ pi->y + pi->height / 2, 0.5, 0.5);
+ }
+ else if (pi)
+ {
+ pan_info_update(pw, pi);
+ image_scroll_to_point(pw->imd,
+ pi->x - PAN_BOX_BORDER * 5 / 2,
+ pi->y, 0.0, 0.5);
+ }
+
+ if (month > 0)
+ {
+ buf = pan_date_value_string(t, PAN_DATE_LENGTH_MONTH);
+ if (day > 0)
+ {
+ gchar *tmp;
+ tmp = buf;
+ buf = g_strdup_printf("%d %s", day, tmp);
+ g_free(tmp);
+ }
+ }
+ else
+ {
+ buf = pan_date_value_string(t, PAN_DATE_LENGTH_YEAR);
+ }
+
+ if (pi)
+ {
+ buf_count = g_strdup_printf("( %d / %d )",
+ g_list_index(list, pi) + 1,
+ g_list_length(list));
+ }
+ else
+ {
+ buf_count = g_strdup_printf("(%s)", _("no match"));
+ }
+
+ message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
+ g_free(buf);
+ g_free(buf_count);
+ pan_search_status(pw, message);
+ g_free(message);
+
+ g_list_free(list);
+
+ return TRUE;
+}
+
+void pan_search_activate_cb(const gchar *text, gpointer data)
+{
+ PanWindow *pw = data;
+
+ if (!text) return;
+
+ tab_completion_append_to_history(pw->search_ui->search_entry, text);
+
+ if (pan_search_by_path(pw, text)) return;
+
+ if ((pw->layout == PAN_LAYOUT_TIMELINE ||
+ pw->layout == PAN_LAYOUT_CALENDAR) &&
+ pan_search_by_date(pw, text))
+ {
+ return;
+ }
+
+ if (pan_search_by_partial(pw, text)) return;
+
+ pan_search_status(pw, _("no match"));
+}
+
+void pan_search_activate(PanWindow *pw)
+{
+ gchar *text;
+
+ text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_ui->search_entry)));
+ pan_search_activate_cb(text, pw);
+ g_free(text);
+}
+
+void pan_search_toggle_cb(GtkWidget *button, gpointer data)
+{
+ PanWindow *pw = data;
+ PanViewSearchUi *ui = pw->search_ui;
+ gboolean visible;
+
+ visible = gtk_widget_get_visible(ui->search_box);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
+
+ if (visible)
+ {
+ gtk_widget_hide(ui->search_box);
+ gtk_arrow_set(GTK_ARROW(ui->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
+ }
+ else
+ {
+ gtk_widget_show(ui->search_box);
+ gtk_arrow_set(GTK_ARROW(ui->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_widget_grab_focus(ui->search_entry);
+ }
+}
+
+void pan_search_toggle_visible(PanWindow *pw, gboolean enable)
+{
+ PanViewSearchUi *ui = pw->search_ui;
+ if (pw->fs) return;
+
+ if (enable)
+ {
+ if (gtk_widget_get_visible(ui->search_box))
+ {
+ gtk_widget_grab_focus(ui->search_entry);
+ }
+ else
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->search_button), TRUE);
+ }
+ }
+ else
+ {
+ if (gtk_widget_get_visible(ui->search_entry))
+ {
+ if (gtk_widget_has_focus(ui->search_entry))
+ {
+ gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
+ }
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->search_button), FALSE);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_VIEW_SEARCH_H
+#define PAN_VIEW_PAN_VIEW_SEARCH_H
+
+#include "main.h"
+#include "pan-types.h"
+
+void pan_search_toggle_visible(PanWindow *pw, gboolean enable);
+void pan_search_activate(PanWindow *pw);
+void pan_search_activate_cb(const gchar *text, gpointer data);
+void pan_search_toggle_cb(GtkWidget *button, gpointer data);
+
+// Creates a new PanViewSearchUi instance and returns it.
+PanViewSearchUi *pan_search_ui_new(PanWindow *pw);
+
+// Destroys the specified PanViewSearchUi and sets the pointer to NULL.
+void pan_search_ui_destroy(PanViewSearchUi **ui);
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
#include "pan-item.h"
#include "pan-timeline.h"
#include "pan-util.h"
+#include "pan-view-filter.h"
+#include "pan-view-search.h"
#include "pixbuf-renderer.h"
#include "pixbuf_util.h"
#include "thumb.h"
static void pan_fullscreen_toggle(PanWindow *pw, gboolean force_off);
-static void pan_search_toggle_visible(PanWindow *pw, gboolean enable);
-static void pan_search_activate(PanWindow *pw);
-
static void pan_window_close(PanWindow *pw);
static GtkWidget *pan_popup_menu(PanWindow *pw);
}
}
-static void pan_layout_update(PanWindow *pw)
+void pan_layout_update(PanWindow *pw)
{
pan_window_message(pw, _("Sorting images..."));
pan_layout_update_idle(pw);
imd_widget = gtk_container_get_focus_child(GTK_CONTAINER(pw->imd->widget));
focused = (pw->fs || (imd_widget && gtk_widget_has_focus(imd_widget)));
on_entry = (gtk_widget_has_focus(pw->path_entry) ||
- gtk_widget_has_focus(pw->search_entry));
+ gtk_widget_has_focus(pw->search_ui->search_entry) ||
+ gtk_widget_has_focus(pw->filter_ui->filter_entry));
if (focused)
{
if (stop_signal) return stop_signal;
+ // Don't steal characters from entry boxes.
if (!on_entry)
{
stop_signal = TRUE;
}
-static void pan_info_update(PanWindow *pw, PanItem *pi)
+void pan_info_update(PanWindow *pw, PanItem *pi)
{
PanTextAlignment *ta;
PanItem *pbox;
}
-/*
- *-----------------------------------------------------------------------------
- * search
- *-----------------------------------------------------------------------------
- */
-
-static void pan_search_status(PanWindow *pw, const gchar *text)
-{
- gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
-}
-
-static gint pan_search_by_path(PanWindow *pw, const gchar *path)
-{
- PanItem *pi;
- GList *list;
- GList *found;
- PanItemType type;
- gchar *buf;
-
- type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
-
- list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
- if (!list) return FALSE;
-
- found = g_list_find(list, pw->click_pi);
- if (found && found->next)
- {
- found = found->next;
- pi = found->data;
- }
- else
- {
- pi = list->data;
- }
-
- pan_info_update(pw, pi);
- image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
-
- buf = g_strdup_printf("%s ( %d / %d )",
- (path[0] == G_DIR_SEPARATOR) ? _("path found") : _("filename found"),
- g_list_index(list, pi) + 1,
- g_list_length(list));
- pan_search_status(pw, buf);
- g_free(buf);
-
- g_list_free(list);
-
- return TRUE;
-}
-
-static gboolean pan_search_by_partial(PanWindow *pw, const gchar *text)
-{
- PanItem *pi;
- GList *list;
- GList *found;
- PanItemType type;
- gchar *buf;
-
- type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
-
- list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
- if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
- if (!list)
- {
- gchar *needle;
-
- needle = g_utf8_strdown(text, -1);
- list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
- g_free(needle);
- }
- if (!list) return FALSE;
-
- found = g_list_find(list, pw->click_pi);
- if (found && found->next)
- {
- found = found->next;
- pi = found->data;
- }
- else
- {
- pi = list->data;
- }
-
- pan_info_update(pw, pi);
- image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
-
- buf = g_strdup_printf("%s ( %d / %d )",
- _("partial match"),
- g_list_index(list, pi) + 1,
- g_list_length(list));
- pan_search_status(pw, buf);
- g_free(buf);
-
- g_list_free(list);
-
- return TRUE;
-}
-
-static gboolean valid_date_separator(gchar c)
-{
- return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
-}
-
-static GList *pan_search_by_date_val(PanWindow *pw, PanItemType type,
- gint year, gint month, gint day,
- const gchar *key)
-{
- GList *list = NULL;
- GList *work;
-
- work = g_list_last(pw->list_static);
- while (work)
- {
- PanItem *pi;
-
- pi = work->data;
- work = work->prev;
-
- if (pi->fd && (pi->type == type || type == PAN_ITEM_NONE) &&
- ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
- {
- struct tm *tl;
-
- tl = localtime(&pi->fd->date);
- if (tl)
- {
- gint match;
-
- match = (tl->tm_year == year - 1900);
- if (match && month >= 0) match = (tl->tm_mon == month - 1);
- if (match && day > 0) match = (tl->tm_mday == day);
-
- if (match) list = g_list_prepend(list, pi);
- }
- }
- }
-
- return g_list_reverse(list);
-}
-
-static gboolean pan_search_by_date(PanWindow *pw, const gchar *text)
-{
- PanItem *pi = NULL;
- GList *list = NULL;
- GList *found;
- gint year;
- gint month = -1;
- gint day = -1;
- gchar *ptr;
- gchar *mptr;
- struct tm *lt;
- time_t t;
- gchar *message;
- gchar *buf;
- gchar *buf_count;
-
- if (!text) return FALSE;
-
- ptr = (gchar *)text;
- while (*ptr != '\0')
- {
- if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
- ptr++;
- }
-
- t = time(NULL);
- if (t == -1) return FALSE;
- lt = localtime(&t);
- if (!lt) return FALSE;
-
- if (valid_date_separator(*text))
- {
- year = -1;
- mptr = (gchar *)text;
- }
- else
- {
- year = (gint)strtol(text, &mptr, 10);
- if (mptr == text) return FALSE;
- }
-
- if (*mptr != '\0' && valid_date_separator(*mptr))
- {
- gchar *dptr;
-
- mptr++;
- month = strtol(mptr, &dptr, 10);
- if (dptr == mptr)
- {
- if (valid_date_separator(*dptr))
- {
- month = lt->tm_mon + 1;
- dptr++;
- }
- else
- {
- month = -1;
- }
- }
- if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
- {
- gchar *eptr;
- dptr++;
- day = strtol(dptr, &eptr, 10);
- if (dptr == eptr)
- {
- day = lt->tm_mday;
- }
- }
- }
-
- if (year == -1)
- {
- year = lt->tm_year + 1900;
- }
- else if (year < 100)
- {
- if (year > 70)
- year+= 1900;
- else
- year+= 2000;
- }
-
- if (year < 1970 ||
- month < -1 || month == 0 || month > 12 ||
- day < -1 || day == 0 || day > 31) return FALSE;
-
- t = pan_date_to_time(year, month, day);
- if (t < 0) return FALSE;
-
- if (pw->layout == PAN_LAYOUT_CALENDAR)
- {
- list = pan_search_by_date_val(pw, PAN_ITEM_BOX, year, month, day, "day");
- }
- else
- {
- PanItemType type;
-
- type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
- list = pan_search_by_date_val(pw, type, year, month, day, NULL);
- }
-
- if (list)
- {
- found = g_list_find(list, pw->search_pi);
- if (found && found->next)
- {
- found = found->next;
- pi = found->data;
- }
- else
- {
- pi = list->data;
- }
- }
-
- pw->search_pi = pi;
-
- if (pw->layout == PAN_LAYOUT_CALENDAR && pi && pi->type == PAN_ITEM_BOX)
- {
- pan_info_update(pw, NULL);
- pan_calendar_update(pw, pi);
- image_scroll_to_point(pw->imd,
- pi->x + pi->width / 2,
- pi->y + pi->height / 2, 0.5, 0.5);
- }
- else if (pi)
- {
- pan_info_update(pw, pi);
- image_scroll_to_point(pw->imd,
- pi->x - PAN_BOX_BORDER * 5 / 2,
- pi->y, 0.0, 0.5);
- }
-
- if (month > 0)
- {
- buf = pan_date_value_string(t, PAN_DATE_LENGTH_MONTH);
- if (day > 0)
- {
- gchar *tmp;
- tmp = buf;
- buf = g_strdup_printf("%d %s", day, tmp);
- g_free(tmp);
- }
- }
- else
- {
- buf = pan_date_value_string(t, PAN_DATE_LENGTH_YEAR);
- }
-
- if (pi)
- {
- buf_count = g_strdup_printf("( %d / %d )",
- g_list_index(list, pi) + 1,
- g_list_length(list));
- }
- else
- {
- buf_count = g_strdup_printf("(%s)", _("no match"));
- }
-
- message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
- g_free(buf);
- g_free(buf_count);
- pan_search_status(pw, message);
- g_free(message);
-
- g_list_free(list);
-
- return TRUE;
-}
-
-static void pan_search_activate_cb(const gchar *text, gpointer data)
-{
- PanWindow *pw = data;
-
- if (!text) return;
-
- tab_completion_append_to_history(pw->search_entry, text);
-
- if (pan_search_by_path(pw, text)) return;
-
- if ((pw->layout == PAN_LAYOUT_TIMELINE ||
- pw->layout == PAN_LAYOUT_CALENDAR) &&
- pan_search_by_date(pw, text))
- {
- return;
- }
-
- if (pan_search_by_partial(pw, text)) return;
-
- pan_search_status(pw, _("no match"));
-}
-
-static void pan_search_activate(PanWindow *pw)
-{
- gchar *text;
-
- text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_entry)));
- pan_search_activate_cb(text, pw);
- g_free(text);
-}
-
-static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
-{
- PanWindow *pw = data;
- gboolean visible;
-
- visible = gtk_widget_get_visible(pw->search_box);
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
-
- if (visible)
- {
- gtk_widget_hide(pw->search_box);
- gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
- }
- else
- {
- gtk_widget_show(pw->search_box);
- gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
- gtk_widget_grab_focus(pw->search_entry);
- }
-}
-
-static void pan_search_toggle_visible(PanWindow *pw, gboolean enable)
-{
- if (pw->fs) return;
-
- if (enable)
- {
- if (gtk_widget_get_visible(pw->search_box))
- {
- gtk_widget_grab_focus(pw->search_entry);
- }
- else
- {
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
- }
- }
- else
- {
- if (gtk_widget_get_visible(pw->search_entry))
- {
- if (gtk_widget_has_focus(pw->search_entry))
- {
- gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
- }
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
- }
- }
-}
-
-
/*
*-----------------------------------------------------------------------------
* main window
}
pan_fullscreen_toggle(pw, TRUE);
+ pan_search_ui_destroy(&pw->search_ui);
+ pan_filter_ui_destroy(&pw->filter_ui);
gtk_widget_destroy(pw->window);
pan_window_items_free(pw);
/* find bar */
- pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
+ pw->search_ui = pan_search_ui_new(pw);
+ gtk_box_pack_start(GTK_BOX(vbox), pw->search_ui->search_box, FALSE, FALSE, 2);
- pref_spacer(pw->search_box, 0);
- pref_label_new(pw->search_box, _("Find:"));
-
- hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
- gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
- gtk_widget_show(hbox);
-
- combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
- pan_search_activate_cb, pw);
- gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
- gtk_widget_show(combo);
-
- pw->search_label = gtk_label_new("");
- gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
- gtk_widget_show(pw->search_label);
+ /* filter bar */
+ pw->filter_ui = pan_filter_ui_new(pw);
+ gtk_box_pack_start(GTK_BOX(vbox), pw->filter_ui->filter_box, FALSE, FALSE, 2);
/* status bar */
gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
gtk_widget_show(pw->label_zoom);
- pw->search_button = gtk_toggle_button_new();
- gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
- gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
- hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
- gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
- gtk_widget_show(hbox);
- pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
- gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
- gtk_widget_show(pw->search_button_arrow);
- pref_label_new(hbox, _("Find"));
-
- gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
- gtk_widget_show(pw->search_button);
- g_signal_connect(G_OBJECT(pw->search_button), "clicked",
- G_CALLBACK(pan_search_toggle_cb), pw);
+ // Add the "Find" button to the status bar area.
+ gtk_box_pack_end(GTK_BOX(box), pw->search_ui->search_button, FALSE, FALSE, 0);
+ gtk_widget_show(pw->search_ui->search_button);
+
+ // Add the "Filter" button to the status bar area.
+ gtk_box_pack_end(GTK_BOX(box), pw->filter_ui->filter_button, FALSE, FALSE, 0);
+ gtk_widget_show(pw->filter_ui->filter_button);
g_signal_connect(G_OBJECT(pw->window), "delete_event",
G_CALLBACK(pan_window_delete_cb), pw);
#include "main.h"
#include "pan-types.h"
+void pan_layout_update(PanWindow *pw);
GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
void pan_layout_resize(PanWindow *pw);
GList *pan_cache_sort(GList *list, SortType method, gboolean ascend);
+void pan_info_update(PanWindow *pw, PanItem *pi);
#endif
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */