Fix #305: Faster Tagging (Keywords)
authorColin Clark <colin.clark@cclark.uk>
Sun, 26 Aug 2018 12:32:42 +0000 (13:32 +0100)
committerColin Clark <colin.clark@cclark.uk>
Sun, 26 Aug 2018 12:32:42 +0000 (13:32 +0100)
https://github.com/BestImageViewer/geeqie/issues/305

Keyword autocompletion - see the Info Sidebar section in the Help file
for details

20 files changed:
doc/docbook/GuideOptionsKeywords.xml [new file with mode: 0644]
doc/docbook/GuideOptionsMain.xml
doc/docbook/GuideSidebarsInfo.xml
src/bar_keywords.c
src/bar_keywords.h
src/layout_util.c
src/preferences.c
web/help/GuideIndex.html
web/help/GuideOptionsBehavior.html
web/help/GuideOptionsColor.html
web/help/GuideOptionsFiltering.html
web/help/GuideOptionsGeneral.html
web/help/GuideOptionsImage.html
web/help/GuideOptionsKeyboard.html
web/help/GuideOptionsMain.html
web/help/GuideOptionsMetadata.html
web/help/GuideOptionsStereo.html
web/help/GuideOptionsToolbar.html
web/help/GuideOptionsWindow.html
web/help/GuideSidebarsInfo.html

diff --git a/doc/docbook/GuideOptionsKeywords.xml b/doc/docbook/GuideOptionsKeywords.xml
new file mode 100644 (file)
index 0000000..6b919f3
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<section id="GuideOptionsKeywords">\r
+  <title id="titleGuideOptionsKeywords">Keywords</title>\r
+  <para>This section describes the keywords list used for autocompletion.</para>\r
+  <para />\r
+  <section id="KeywordSearch">\r
+    <title>Keyword Search</title>\r
+    <para />\r
+    <para>Pressing the Search button will open a dialog which permits a recursive search to be made for keywords already attached to images. The result of the search is automatically appended to the existing list.</para>\r
+  </section>\r
+  <section id="KeywordList">\r
+    <title>Keyword List</title>\r
+    <para />\r
+    <para>\r
+      The list shows all keywords currently used for autocompletion. Text may be copy-pasted into the list or deleted from the list.\r
+      <para />\r
+      When the list is saved, duplicates will autmatically be removed and the list sorted into alphabetical order.\r
+    </para>\r
+  </section>\r
+</section>\r
index 6ed8850..16ac361 100644 (file)
@@ -27,6 +27,7 @@
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsKeyboard.xml" />\r
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsFiltering.xml" />\r
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsMetadata.xml" />\r
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsKeywords.xml" />\r
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsColor.xml" />\r
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsStereo.xml" />\r
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsBehavior.xml" />\r
index 79d59ba..498d155 100644 (file)
     </note>\r
     <para />\r
   </section>\r
+  <section id="KeywordAutocompletion">\r
+    <title id="titleKeywordAutocompletion">Keyword Autocompletion</title>\r
+    <para>\r
+      The text box at the bottom of the keywords pane is used for autocompletion. Any keywords typed into the standard keyword box or the autocompletion box will be remembered as candidates for future autocompletion.\r
+      <para />\r
+      Frequently used sets of keywords can be entered as comma-delimited lists.\r
+      <para />\r
+      The list of keywords used for autocompletion can be edited on the\r
+      <link linkend="GuideOptionsKeywords">Keywords</link>\r
+      tab of the Preferences dialog.\r
+      <para />\r
+      The menu action "Keyword autocomplete", set to\r
+      <code>Alt + K</code>\r
+      by default, will shift the keyboard focus to the autocomplete box. Pressing\r
+      <code>Alt + K</code>\r
+      a second time will shift the keyboard focus back to the previous object.\r
+    </para>\r
+    <para />\r
+  </section>\r
   <section id="Listpanes-ExifFileinfoCopyrightLocationandGPS">\r
     <title id="titleExif">List panes</title>\r
     <para>\r
index 3509bc5..601481a 100644 (file)
 #include "rcfile.h"
 #include "layout.h"
 #include "dnd.h"
+#include "secure_save.h"
 
 
 //static void bar_pane_keywords_keyword_update_all(void);
 static void bar_pane_keywords_changed(GtkTextBuffer *buffer, gpointer data);
 
+static void autocomplete_keywords_list_load(const gchar *path);
+static GtkListStore *keyword_store = NULL;
+static gboolean autocomplete_keywords_list_save(gchar *path);
+static gboolean autocomplete_activate_cb(GtkWidget *widget, gpointer data);
+
 /*
  *-------------------------------------------------------------------
  * keyword / comment utils
@@ -138,6 +144,8 @@ struct _PaneKeywordsData
        gint height;
 
        GList *expanded_rows;
+
+       GtkWidget *autocomplete;
 };
 
 typedef struct _ConfDialogData ConfDialogData;
@@ -336,6 +344,10 @@ gint bar_pane_keywords_event(GtkWidget *bar, GdkEvent *event)
 
        if (gtk_widget_has_focus(pkd->keyword_view)) return gtk_widget_event(pkd->keyword_view, event);
 
+       if (gtk_widget_has_focus(pkd->autocomplete))
+               {
+               return gtk_widget_event(pkd->autocomplete, event);
+               }
        return FALSE;
 }
 
@@ -1430,6 +1442,10 @@ void bar_pane_keywords_close(GtkWidget *bar)
 static void bar_pane_keywords_destroy(GtkWidget *widget, gpointer data)
 {
        PaneKeywordsData *pkd = data;
+       gchar *path;
+
+       path = g_build_filename(get_rc_dir(), "keywords", NULL);
+       autocomplete_keywords_list_save(path);
 
        string_list_free(pkd->expanded_rows);
        if (pkd->click_tpath) gtk_tree_path_free(pkd->click_tpath);
@@ -1446,13 +1462,15 @@ static void bar_pane_keywords_destroy(GtkWidget *widget, gpointer data)
 static GtkWidget *bar_pane_keywords_new(const gchar *id, const gchar *title, const gchar *key, gboolean expanded, gint height)
 {
        PaneKeywordsData *pkd;
-       GtkWidget *hbox;
+       GtkWidget *hbox, *vbox;
        GtkWidget *scrolled;
        GtkTextBuffer *buffer;
        GtkTreeModel *store;
        GtkTreeViewColumn *column;
        GtkCellRenderer *renderer;
        GtkTreeIter iter;
+       GtkEntryCompletion *completion;
+       gchar *path;
 
        pkd = g_new0(PaneKeywordsData, 1);
 
@@ -1471,9 +1489,11 @@ static GtkWidget *bar_pane_keywords_new(const gchar *id, const gchar *title, con
        pkd->expand_checked = TRUE;
        pkd->expanded_rows = NULL;
 
+       vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP);
        hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
+       gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
 
-       pkd->widget = hbox;
+       pkd->widget = vbox;
        g_object_set_data(G_OBJECT(pkd->widget), "pane_data", pkd);
        g_signal_connect(G_OBJECT(pkd->widget), "destroy",
                         G_CALLBACK(bar_pane_keywords_destroy), pkd);
@@ -1504,6 +1524,26 @@ static GtkWidget *bar_pane_keywords_new(const gchar *id, const gchar *title, con
        gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
        gtk_widget_show(scrolled);
 
+       pkd->autocomplete = gtk_entry_new();
+       gtk_box_pack_end(GTK_BOX(vbox), pkd->autocomplete, FALSE, FALSE, 0);
+       gtk_widget_show(pkd->autocomplete);
+       gtk_widget_show(vbox);
+       gtk_widget_set_tooltip_text(pkd->autocomplete, "Keyword autocomplete");
+
+       path = g_build_filename(get_rc_dir(), "keywords", NULL);
+       autocomplete_keywords_list_load(path);
+
+       completion = gtk_entry_completion_new();
+       gtk_entry_set_completion(GTK_ENTRY(pkd->autocomplete), completion);
+       gtk_entry_completion_set_inline_completion(completion, TRUE);
+       gtk_entry_completion_set_inline_selection(completion, TRUE);
+       g_object_unref(completion);
+
+       gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(keyword_store));
+       gtk_entry_completion_set_text_column(completion, 0);
+
+       g_signal_connect(G_OBJECT(pkd->autocomplete), "activate",
+                        G_CALLBACK(autocomplete_activate_cb), pkd);
 
        if (!keyword_tree || !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_tree), &iter))
                {
@@ -1687,4 +1727,258 @@ void bar_pane_keywords_entry_add_from_config(GtkWidget *pane, const gchar **attr
                log_printf("unknown attribute %s = %s\n", option, value);
                }
 }
+
+/*
+ *-----------------------------------------------------------------------------
+ * Autocomplete keywords
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean autocomplete_activate_cb(GtkWidget *widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       gchar *entry_text;
+       GtkTextBuffer *buffer;
+       GtkTextIter iter;
+       GtkTreeIter iter_t;
+       gchar *kw_cr;
+       gchar *kw_split;
+       gboolean valid;
+       gboolean found = FALSE;
+       gchar *string;
+
+       entry_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pkd->autocomplete)));
+       buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pkd->keyword_view));
+
+       kw_split = strtok(entry_text, ",");
+       while (kw_split != NULL)
+               {
+               kw_cr = g_strconcat(kw_split, "\n", NULL);
+               g_strchug(kw_cr);
+               gtk_text_buffer_get_end_iter(buffer, &iter);
+               gtk_text_buffer_insert(buffer, &iter, kw_cr, -1);
+
+               kw_split = strtok(NULL, ",");
+               g_free(kw_cr);
+               }
+
+       g_free(entry_text);
+       entry_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pkd->autocomplete)));
+
+       gtk_entry_set_text(GTK_ENTRY(pkd->autocomplete), "");
+
+       valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_store), &iter_t);
+       while (valid)
+               {
+               gtk_tree_model_get (GTK_TREE_MODEL(keyword_store), &iter_t, 0, &string, -1);
+               if (g_strcmp0(entry_text, string) == 0)
+                       {
+                       found = TRUE;
+                       break;
+                       }
+               valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(keyword_store), &iter_t);
+               }
+
+       if (!found)
+               {
+               gtk_list_store_append (keyword_store, &iter_t);
+               gtk_list_store_set(keyword_store, &iter_t, 0, entry_text, -1);
+               }
+
+       g_free(entry_text);
+       return FALSE;
+}
+
+gint autocomplete_sort_iter_compare_func (GtkTreeModel *model,
+                                                                       GtkTreeIter *a,
+                                                                       GtkTreeIter *b,
+                                                                       gpointer userdata)
+{
+       gint ret = 0;
+       gchar *name1, *name2;
+
+       gtk_tree_model_get(model, a, 0, &name1, -1);
+       gtk_tree_model_get(model, b, 0, &name2, -1);
+
+       if (name1 == NULL || name2 == NULL)
+               {
+               if (name1 == NULL && name2 == NULL)
+                       {
+                       ret = 0;
+                       }
+               else
+                       {
+                       ret = (name1 == NULL) ? -1 : 1;
+                       }
+               }
+       else
+               {
+               ret = g_utf8_collate(name1,name2);
+               }
+
+       g_free(name1);
+       g_free(name2);
+
+       return ret;
+}
+
+static void autocomplete_keywords_list_load(const gchar *path)
+{
+       FILE *f;
+       gchar s_buf[1024];
+       gchar *pathl;
+       gint len;
+       GtkTreeIter iter;
+       GtkTreeSortable *sortable;
+
+       if (keyword_store) return;
+       keyword_store = gtk_list_store_new(1, G_TYPE_STRING);
+
+       sortable = GTK_TREE_SORTABLE(keyword_store);
+       gtk_tree_sortable_set_sort_func(sortable, 0, autocomplete_sort_iter_compare_func,
+                                                                                               GINT_TO_POINTER(0), NULL);
+
+       gtk_tree_sortable_set_sort_column_id(sortable, 0, GTK_SORT_ASCENDING);
+
+       pathl = path_from_utf8(path);
+       f = fopen(pathl, "r");
+       g_free(pathl);
+
+       if (!f)
+               {
+               log_printf("Warning: keywords file %s not loaded", pathl);
+               return;
+               }
+
+       /* first line must start with Keywords comment */
+       if (!fgets(s_buf, sizeof(s_buf), f) ||
+                                       strncmp(s_buf, "#Keywords", 9) != 0)
+               {
+               fclose(f);
+               log_printf("Warning: keywords file %s not loaded", pathl);
+               return;
+               }
+
+       while (fgets(s_buf, sizeof(s_buf), f))
+               {
+               if (s_buf[0]=='#') continue;
+
+               len = strlen(s_buf);
+               if( s_buf[len-1] == '\n' )
+                       {
+                       s_buf[len-1] = 0;
+                       }
+               gtk_list_store_append (keyword_store, &iter);
+               gtk_list_store_set(keyword_store, &iter, 0, g_strdup(s_buf), -1);
+               }
+
+       fclose(f);
+}
+
+static gboolean autocomplete_keywords_list_save(gchar *path)
+{
+       SecureSaveInfo *ssi;
+       gchar *pathl;
+       gchar *string;
+       gchar *string_nl;
+       GtkTreeIter  iter;
+       gboolean     valid;
+
+       pathl = path_from_utf8(path);
+       ssi = secure_open(pathl);
+       g_free(pathl);
+
+       if (!ssi)
+               {
+               log_printf(_("Error: Unable to write keywords list to: %s\n"), path);
+               return FALSE;
+               }
+
+       secure_fprintf(ssi, "#Keywords list\n");
+
+       valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_store), &iter);
+
+       while (valid)
+               {
+               gtk_tree_model_get (GTK_TREE_MODEL(keyword_store), &iter, 0, &string, -1);
+               string_nl = g_strconcat(string, "\n", NULL);
+               secure_fprintf(ssi, "%s", string_nl);
+
+               valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(keyword_store), &iter);
+
+               g_free(string_nl);
+               }
+
+       secure_fprintf(ssi, "#end\n");
+       return (secure_close(ssi) == 0);
+}
+
+GList *keyword_list_get()
+{
+       GList *ret_list = NULL;
+       gchar *string;
+       gchar *string_nl;
+       GtkTreeIter iter;
+       gboolean valid;
+
+       valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_store), &iter);
+
+       while (valid)
+               {
+               gtk_tree_model_get (GTK_TREE_MODEL(keyword_store), &iter, 0, &string, -1);
+               string_nl = g_strconcat(string, "\n", NULL);
+               ret_list = g_list_append(ret_list, string);
+               valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(keyword_store), &iter);
+
+               g_free(string_nl);
+               }
+
+       return ret_list;
+}
+
+void keyword_list_set(GList *keyword_list)
+{
+       GList *ret = NULL;
+       GtkTreeIter  iter;
+
+       if (!keyword_list) return;
+
+       gtk_list_store_clear(keyword_store);
+
+       while (keyword_list)
+               {
+               gtk_list_store_append (keyword_store, &iter);
+               gtk_list_store_set(keyword_store, &iter, 0, keyword_list->data, -1);
+
+               keyword_list = keyword_list->next;
+               }
+}
+
+gboolean bar_keywords_autocomplete_focus(LayoutWindow *lw)
+{
+       GtkWidget *pane;
+       GtkWidget *current_focus;
+       GList *children;
+       gboolean ret;
+
+       current_focus = gtk_window_get_focus(GTK_WINDOW(lw->window));
+       pane = bar_find_pane_by_id(lw->bar, PANE_KEYWORDS, "keywords");
+
+       children = gtk_container_get_children(GTK_CONTAINER(pane));
+       children = g_list_last(children);
+
+       if (current_focus == children->data)
+               {
+               ret = TRUE;
+               }
+       else
+               {
+               gtk_widget_grab_focus(children->data);
+               ret = FALSE;
+               }
+
+       g_list_free(children);
+
+       return ret;
+}
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index 1c2583f..1fe87a0 100644 (file)
@@ -28,5 +28,8 @@ void bar_pane_keywords_entry_add_from_config(GtkWidget *pane, const gchar **attr
 /* used in search.c */
 GList *keyword_list_pull(GtkWidget *text_widget);
 
+GList *keyword_list_get();
+void keyword_list_set(GList *keyword_list);
+gboolean bar_keywords_autocomplete_focus(LayoutWindow *lw);
 #endif
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index a618f7a..06edd5f 100644 (file)
@@ -25,6 +25,7 @@
 #include "advanced_exif.h"
 #include "bar_sort.h"
 #include "bar.h"
+#include "bar_keywords.h"
 #include "cache_maint.h"
 #include "collect.h"
 #include "collect-dlg.h"
@@ -1656,6 +1657,25 @@ static void layout_menu_metadata_write_cb(GtkAction *action, gpointer data)
        metadata_write_queue_confirm(TRUE, NULL, NULL);
 }
 
+static GtkWidget *last_focussed = NULL;
+static void layout_menu_keyword_autocomplete_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+       GtkWidget *tmp;
+       gboolean auto_has_focus;
+
+       tmp = gtk_window_get_focus(GTK_WINDOW(lw->window));
+       auto_has_focus = bar_keywords_autocomplete_focus(lw);
+
+       if (auto_has_focus)
+               {
+               gtk_widget_grab_focus(last_focussed);
+               }
+       else
+               {
+               last_focussed = tmp;
+               }
+}
 
 /*
  *-----------------------------------------------------------------------------
@@ -1888,6 +1908,7 @@ static GtkActionEntry menu_entries[] = {
   { "Maintenance",     PIXBUF_INLINE_ICON_MAINTENANCE, N_("_Cache maintenance..."),    NULL,                   N_("Cache maintenance..."),             CB(layout_menu_remove_thumb_cb) },
   { "Wallpaper",       NULL,                   N_("Set as _wallpaper"),                NULL,                   N_("Set as wallpaper"),                 CB(layout_menu_wallpaper_cb) },
   { "SaveMetadata",    GTK_STOCK_SAVE,         N_("_Save metadata"),                   "<control>S",           N_("Save metadata"),                    CB(layout_menu_metadata_write_cb) },
+  { "KeywordAutocomplete",     NULL,   N_("Keyword autocomplete"),             "<alt>K",               N_("Keyword Autocomplete"),                     CB(layout_menu_keyword_autocomplete_cb) },
   { "ZoomIn",          GTK_STOCK_ZOOM_IN,      N_("Zoom _in"),                         "equal",                N_("Zoom in"),                          CB(layout_menu_zoom_in_cb) },
   { "ZoomInAlt1",      GTK_STOCK_ZOOM_IN,      N_("Zoom _in"),                         "KP_Add",               N_("Zoom in"),                          CB(layout_menu_zoom_in_cb) },
   { "ZoomOut",         GTK_STOCK_ZOOM_OUT,     N_("Zoom _out"),                        "minus",                N_("Zoom out"),                         CB(layout_menu_zoom_out_cb) },
@@ -2110,6 +2131,7 @@ static const gchar *menu_ui_description =
 "        <separator/>"
 "      </menu>"
 "      <menuitem action='SaveMetadata'/>"
+"      <menuitem action='KeywordAutocomplete'/>"
 "      <placeholder name='PropertiesSection'/>"
 "      <separator/>"
 "      <menuitem action='DrawRectangle'/>"
index 88d315d..3a784c8 100644 (file)
@@ -23,6 +23,7 @@
 #include "preferences.h"
 
 #include "bar_exif.h"
+#include "bar_keywords.h"
 #include "cache.h"
 #include "cache_maint.h"
 #include "editors.h"
@@ -36,6 +37,7 @@
 #include "img-view.h"
 #include "layout_config.h"
 #include "layout_util.h"
+#include "metadata.h"
 #include "pixbuf_util.h"
 #include "slideshow.h"
 #include "toolbar.h"
@@ -43,6 +45,7 @@
 #include "utilops.h"
 #include "ui_fileops.h"
 #include "ui_misc.h"
+#include "ui_spinner.h"
 #include "ui_tabcomp.h"
 #include "ui_utildlg.h"
 #include "window.h"
@@ -63,6 +66,9 @@
 
 static void image_overlay_set_text_colours();
 
+GtkWidget *keyword_text;
+static void config_tab_keywords_save();
+
 typedef struct _ThumbSize ThumbSize;
 struct _ThumbSize
 {
@@ -439,6 +445,8 @@ static void config_window_apply(void)
                }
 #endif
 
+       config_tab_keywords_save();
+
        image_options_sync();
 
        if (refresh)
@@ -2391,6 +2399,349 @@ static void config_tab_metadata(GtkWidget *notebook)
        gtk_widget_set_tooltip_text(ct_button,"On folder change, read DateTimeOriginal, DateTimeDigitized and Star Rating in the idle loop.\nIf this is not selected, initial loading of the folder will be faster but sorting on these items will be slower");
 }
 
+/* keywords tab */
+
+typedef struct _KeywordFindData KeywordFindData;
+struct _KeywordFindData
+{
+       GenericDialog *gd;
+
+       GList *list;
+       GList *list_dir;
+
+       GtkWidget *button_close;
+       GtkWidget *button_stop;
+       GtkWidget *button_start;
+       GtkWidget *progress;
+       GtkWidget *spinner;
+
+       GtkWidget *group;
+       GtkWidget *entry;
+
+       gboolean recurse;
+
+       guint idle_id; /* event source id */
+};
+
+#define KEYWORD_DIALOG_WIDTH 400
+
+static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
+{
+       GList *list_d = NULL;
+       GList *list_f = NULL;
+
+       if (kfd->recurse)
+               {
+               filelist_read(dir_fd, &list_f, &list_d);
+               }
+       else
+               {
+               filelist_read(dir_fd, &list_f, NULL);
+               }
+
+       list_f = filelist_filter(list_f, FALSE);
+       list_d = filelist_filter(list_d, TRUE);
+
+       kfd->list = g_list_concat(list_f, kfd->list);
+       kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
+}
+
+static void keywords_find_reset(KeywordFindData *kfd)
+{
+       filelist_free(kfd->list);
+       kfd->list = NULL;
+
+       filelist_free(kfd->list_dir);
+       kfd->list_dir = NULL;
+}
+
+static void keywords_find_close_cb(GenericDialog *fd, gpointer data)
+{
+       KeywordFindData *kfd = data;
+
+       if (!gtk_widget_get_sensitive(kfd->button_close)) return;
+
+       keywords_find_reset(kfd);
+       generic_dialog_close(kfd->gd);
+       g_free(kfd);
+}
+
+static void keywords_find_finish(KeywordFindData *kfd)
+{
+       keywords_find_reset(kfd);
+
+       gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
+       spinner_set_interval(kfd->spinner, -1);
+
+       gtk_widget_set_sensitive(kfd->group, TRUE);
+       gtk_widget_set_sensitive(kfd->button_start, TRUE);
+       gtk_widget_set_sensitive(kfd->button_stop, FALSE);
+       gtk_widget_set_sensitive(kfd->button_close, TRUE);
+}
+
+static void keywords_find_stop_cb(GenericDialog *fd, gpointer data)
+{
+       KeywordFindData *kfd = data;
+
+       g_idle_remove_by_data(kfd);
+
+       keywords_find_finish(kfd);
+}
+
+static gboolean keywords_find_file(gpointer data)
+{
+       KeywordFindData *kfd = data;
+       GtkTextIter iter;
+       GtkTextBuffer *buffer;
+       gchar *tmp;
+       GList *keywords;
+
+       if (kfd->list)
+               {
+               FileData *fd;
+
+               fd = kfd->list->data;
+               kfd->list = g_list_remove(kfd->list, fd);
+
+               keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
+               buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
+
+               while (keywords)
+                       {
+                       gtk_text_buffer_get_end_iter(buffer, &iter);
+                       tmp = g_strconcat(keywords->data, "\n", NULL);
+                       gtk_text_buffer_insert(buffer, &iter, tmp, -1);
+                       g_free(tmp);
+                       keywords = keywords->next;
+                       }
+
+               gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
+               file_data_unref(fd);
+               string_list_free(keywords);
+
+               return (TRUE);
+               }
+       else if (kfd->list_dir)
+               {
+               FileData *fd;
+
+               fd = kfd->list_dir->data;
+               kfd->list_dir = g_list_remove(kfd->list_dir, fd);
+
+               keywords_find_folder(kfd, fd);
+
+               file_data_unref(fd);
+
+               return TRUE;
+               }
+
+       keywords_find_finish(kfd);
+
+       return FALSE;
+}
+
+static void keywords_find_start_cb(GenericDialog *fd, gpointer data)
+{
+       KeywordFindData *kfd = data;
+       gchar *path;
+
+       if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
+
+       path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
+       parse_out_relatives(path);
+
+       if (!isdir(path))
+               {
+               warning_dialog(_("Invalid folder"),
+                               _("The specified folder can not be found."),
+                               GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
+               }
+       else
+               {
+               FileData *dir_fd;
+
+               gtk_widget_set_sensitive(kfd->group, FALSE);
+               gtk_widget_set_sensitive(kfd->button_start, FALSE);
+               gtk_widget_set_sensitive(kfd->button_stop, TRUE);
+               gtk_widget_set_sensitive(kfd->button_close, FALSE);
+               spinner_set_interval(kfd->spinner, SPINNER_SPEED);
+
+               dir_fd = file_data_new_dir(path);
+               keywords_find_folder(kfd, dir_fd);
+               file_data_unref(dir_fd);
+               kfd->idle_id = g_idle_add(keywords_find_file, kfd);
+               }
+
+       g_free(path);
+}
+
+static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
+{
+       KeywordFindData *kfd;
+       GtkWidget *hbox;
+       GtkWidget *label;
+
+       kfd = g_new0(KeywordFindData, 1);
+
+       kfd->gd = generic_dialog_new(_("Search for keywords"),
+                                                                       "search_for_keywords",
+                                                                       widget, FALSE,
+                                                                       NULL, kfd);
+       gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
+       kfd->gd->cancel_cb = keywords_find_close_cb;
+       kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, NULL,
+                                                    keywords_find_close_cb, FALSE);
+       kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"),
+                                                    keywords_find_start_cb, FALSE);
+       kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, NULL,
+                                                   keywords_find_stop_cb, FALSE);
+       gtk_widget_set_sensitive(kfd->button_stop, FALSE);
+
+       generic_dialog_add_message(kfd->gd, NULL, _("Search for keywords"), NULL, FALSE);
+
+       hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
+       pref_spacer(hbox, PREF_PAD_INDENT);
+       kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
+
+       hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
+       pref_label_new(hbox, _("Folder:"));
+
+       label = tab_completion_new(&kfd->entry, path, NULL, NULL, NULL, NULL);
+       tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
+       gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+       gtk_widget_show(label);
+
+       pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
+
+       pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
+       hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
+
+       kfd->progress = gtk_entry_new();
+       gtk_widget_set_can_focus(kfd->progress, FALSE);
+       gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
+       gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
+       gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
+       gtk_widget_show(kfd->progress);
+
+       kfd->spinner = spinner_new(NULL, -1);
+       gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
+       gtk_widget_show(kfd->spinner);
+
+       kfd->list = NULL;
+
+       gtk_widget_show(kfd->gd->dialog);
+}
+
+static void keywords_find_cb(GtkWidget *widget, gpointer data)
+{
+       const gchar *path = layout_get_path(NULL);
+
+       if (!path || !*path) path = homedir();
+       keywords_find_dialog(widget, path);
+}
+
+static void config_tab_keywords_save()
+{
+       GtkTextIter start, end;
+       GtkTextBuffer *buffer;
+       GList *kw_list = NULL;
+       GList *work;
+       gchar *buffer_text;
+       gchar *kw_split;
+       gboolean found;
+
+       buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
+       gtk_text_buffer_get_bounds(buffer, &start, &end);
+
+       buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
+
+       kw_split = strtok(buffer_text, "\n");
+       while (kw_split != NULL)
+               {
+               work = kw_list;
+               found = FALSE;
+               while (work)
+                       {
+                       if (g_strcmp0(work->data, kw_split) == 0)
+                               {
+                               found = TRUE;
+                               break;
+                               }
+                       work = work->next;
+                       }
+               if (!found)
+                       {
+                       kw_list = g_list_append(kw_list, g_strdup(kw_split));
+                       }
+               kw_split = strtok(NULL, "\n");
+               }
+
+       keyword_list_set(kw_list);
+
+       string_list_free(kw_list);
+       g_free(buffer_text);
+}
+
+static void config_tab_keywords(GtkWidget *notebook)
+{
+       GtkWidget *hbox;
+       GtkWidget *vbox;
+       GtkWidget *group;
+       GtkWidget *button;
+       GtkWidget *scrolled;
+       GtkTextIter iter;
+       GtkTextBuffer *buffer;
+       gchar *tmp;
+
+       vbox = scrolled_notebook_page(notebook, _("Keywords"));
+
+       group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
+
+       hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
+
+       button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE,
+                                  G_CALLBACK(keywords_find_cb), keyword_text);
+       gtk_widget_set_tooltip_text(button, "Search for existing keywords");
+
+
+       keyword_text = gtk_text_view_new();
+       gtk_widget_set_size_request(keyword_text, 20, 20);
+       scrolled = gtk_scrolled_window_new(NULL, NULL);
+       gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
+       gtk_widget_show(scrolled);
+
+       gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
+       gtk_widget_show(keyword_text);
+
+       gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
+
+       buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
+       gtk_text_buffer_create_tag(buffer, "monospace",
+                               "family", "monospace", NULL);
+
+       gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
+       gtk_text_buffer_get_start_iter(buffer, &iter);
+       gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
+       gchar *path;
+
+       path = g_build_filename(get_rc_dir(), "keywords", NULL);
+
+       GList *kwl = keyword_list_get();
+       kwl = g_list_first(kwl);
+       while (kwl)
+       {
+               gtk_text_buffer_get_end_iter (buffer, &iter);
+           tmp = g_strconcat(kwl->data, "\n", NULL);
+               gtk_text_buffer_insert(buffer, &iter, tmp, -1);
+               kwl = kwl->next;
+               g_free(tmp);
+       }
+
+       gtk_text_buffer_set_modified(buffer, FALSE);
+
+       g_free(path);
+}
+
 /* metadata tab */
 #ifdef HAVE_LCMS
 static void intent_menu_cb(GtkWidget *combo, gpointer data)
@@ -2878,6 +3229,7 @@ static void config_window_create(void)
        config_tab_accelerators(notebook);
        config_tab_files(notebook);
        config_tab_metadata(notebook);
+       config_tab_keywords(notebook);
        config_tab_color(notebook);
        config_tab_stereo(notebook);
        config_tab_behavior(notebook);
index a6ba724..2e8e0c3 100644 (file)
@@ -592,16 +592,19 @@ dd.answer div.label { float: left; }
 <span class="label">11.6. </span><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a>
 </li>
 <li>
-<span class="label">11.7. </span><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a>
+<span class="label">11.7. </span><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a>
 </li>
 <li>
-<span class="label">11.8. </span><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a>
+<span class="label">11.8. </span><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a>
 </li>
 <li>
-<span class="label">11.9. </span><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a>
+<span class="label">11.9. </span><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a>
 </li>
 <li>
-<span class="label">11.10. </span><a class="xref" href="GuideOptionsToolbar.html" title="Toolbar">Toolbar</a>
+<span class="label">11.10. </span><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a>
+</li>
+<li>
+<span class="label">11.11. </span><a class="xref" href="GuideOptionsToolbar.html" title="Toolbar">Toolbar</a>
 </li>
 </ul></div>
 </li>
index 84ce4ce..4d52490 100644 (file)
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li>Behavior Options</li>
@@ -459,20 +460,20 @@ dd.answer div.label { float: left; }
 <p class="para block block-first">This section describes the options presented under the Behavior Tab of the preferences dialog.</p>
 <div class="autotoc"><ul>
 <li>
-<span class="label">11.9.1. </span><a class="xref" href="GuideOptionsBehavior.html#Delete" title="Delete">Delete</a>
+<span class="label">11.10.1. </span><a class="xref" href="GuideOptionsBehavior.html#Delete" title="Delete">Delete</a>
 </li>
 <li>
-<span class="label">11.9.2. </span><a class="xref" href="GuideOptionsBehavior.html#Behaviour" title="Behavior">Behavior</a>
+<span class="label">11.10.2. </span><a class="xref" href="GuideOptionsBehavior.html#Behaviour" title="Behavior">Behavior</a>
 </li>
 <li>
-<span class="label">11.9.3. </span><a class="xref" href="GuideOptionsBehavior.html#Navigation" title="Navigation">Navigation</a>
+<span class="label">11.10.3. </span><a class="xref" href="GuideOptionsBehavior.html#Navigation" title="Navigation">Navigation</a>
 </li>
 <li>
-<span class="label">11.9.4. </span><a class="xref" href="GuideOptionsBehavior.html#Debugging" title="Debugging">Debugging</a>
+<span class="label">11.10.4. </span><a class="xref" href="GuideOptionsBehavior.html#Debugging" title="Debugging">Debugging</a>
 </li>
 </ul></div>
 <div class="division section">
-<a name="Delete"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">11.9.1. </span>Delete</span></h2></div>
+<a name="Delete"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">11.10.1. </span>Delete</span></h2></div>
 <div class="block list variablelist"><dl class="variablelist">
 <dt class="term dt-first">
           <span class="guilabel">Confirm file delete</span>
@@ -550,7 +551,7 @@ dd.answer div.label { float: left; }
 </dl></div>
 </div>
 <div class="division section">
-<a name="Behaviour"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">11.9.2. </span>Behavior</span></h2></div>
+<a name="Behaviour"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">11.10.2. </span>Behavior</span></h2></div>
 <div class="block list variablelist"><dl class="variablelist">
 <dt class="term dt-first">
           <span class="guilabel">Descend folders in tree view</span>
@@ -628,7 +629,7 @@ dd.answer div.label { float: left; }
 </dl></div>
 </div>
 <div class="division section">
-<a name="Navigation"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">11.9.3. </span>Navigation</span></h2></div>
+<a name="Navigation"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">11.10.3. </span>Navigation</span></h2></div>
 <div class="block list variablelist"><dl class="variablelist">
 <dt class="term dt-first">
           <span class="guilabel">Progressive keyboard scrolling</span>
@@ -669,7 +670,7 @@ dd.answer div.label { float: left; }
 </dl></div>
 </div>
 <div class="division section">
-<a name="Debugging"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">11.9.4. </span>Debugging</span></h2></div>
+<a name="Debugging"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">11.10.4. </span>Debugging</span></h2></div>
 <div class="block list variablelist"><dl class="variablelist">
 <dt class="term dt-first">
           <span class="guilabel">Debug level</span>
index 3c6cf8a..c2fe1ab 100644 (file)
@@ -3,7 +3,7 @@
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>Color management options</title>
-<link rel="previous" href="GuideOptionsMetadata.html" title="Metadata">
+<link rel="previous" href="GuideOptionsKeywords.html" title="Keywords">
 <link rel="next" href="GuideOptionsStereo.html" title="Stereo image management">
 <link rel="top" href="GuideIndex.html" title="The Geeqie User Manual">
 <style type="text/css">
@@ -410,7 +410,7 @@ dd.answer div.label { float: left; }
 </head>
 <body>
 <div class="navbar navbar-top"><table class="navbar"><tr>
-<td class="navbar-prev"><a class="navbar-prev" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></td>
+<td class="navbar-prev"><a class="navbar-prev" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></td>
 <td class="navbar-next"><a class="navbar-next" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></td>
 </tr></table></div>
 <div class="sidebar"><div class="sidenav"><div class="autotoc"><ul>
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li>Color management options</li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
@@ -493,7 +494,7 @@ dd.answer div.label { float: left; }
 </dl></div>
 </div></div>
 <div class="navbar navbar-bottom"><table class="navbar"><tr>
-<td class="navbar-prev"><a class="navbar-prev" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></td>
+<td class="navbar-prev"><a class="navbar-prev" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></td>
 <td class="navbar-next"><a class="navbar-next" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></td>
 </tr></table></div>
 </body>
index 6fdc9f3..5d7939c 100644 (file)
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li>Files Options</li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
index 669bd7d..da994b7 100644 (file)
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
index 13ab9d3..5124697 100644 (file)
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
index 355c2a2..47e2457 100644 (file)
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li>Keyboard Options</li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
index c3cae56..8c02d80 100644 (file)
@@ -434,6 +434,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
@@ -485,16 +486,19 @@ dd.answer div.label { float: left; }
 <span class="label">11.6. </span><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a>
 </li>
 <li>
-<span class="label">11.7. </span><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a>
+<span class="label">11.7. </span><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a>
 </li>
 <li>
-<span class="label">11.8. </span><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a>
+<span class="label">11.8. </span><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a>
 </li>
 <li>
-<span class="label">11.9. </span><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a>
+<span class="label">11.9. </span><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a>
 </li>
 <li>
-<span class="label">11.10. </span><a class="xref" href="GuideOptionsToolbar.html" title="Toolbar">Toolbar</a>
+<span class="label">11.10. </span><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a>
+</li>
+<li>
+<span class="label">11.11. </span><a class="xref" href="GuideOptionsToolbar.html" title="Toolbar">Toolbar</a>
 </li>
 </ul></div>
 </div></div>
index 66c83a1..af633f6 100644 (file)
@@ -4,7 +4,7 @@
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>Metadata</title>
 <link rel="previous" href="GuideOptionsFiltering.html" title="Files Options">
-<link rel="next" href="GuideOptionsColor.html" title="Color management options">
+<link rel="next" href="GuideOptionsKeywords.html" title="Keywords">
 <link rel="top" href="GuideIndex.html" title="The Geeqie User Manual">
 <style type="text/css">
 
@@ -411,7 +411,7 @@ dd.answer div.label { float: left; }
 <body>
 <div class="navbar navbar-top"><table class="navbar"><tr>
 <td class="navbar-prev"><a class="navbar-prev" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></td>
-<td class="navbar-next"><a class="navbar-next" href="GuideOptionsColor.html" title="Color management options">Color management options</a></td>
+<td class="navbar-next"><a class="navbar-next" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></td>
 </tr></table></div>
 <div class="sidebar"><div class="sidenav"><div class="autotoc"><ul>
 <li><a href="GuideIndex-info.html" title="About This Document">About This Document</a></li>
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li>Metadata</li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
@@ -657,7 +658,7 @@ dd.answer div.label { float: left; }
 </div></div>
 <div class="navbar navbar-bottom"><table class="navbar"><tr>
 <td class="navbar-prev"><a class="navbar-prev" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></td>
-<td class="navbar-next"><a class="navbar-next" href="GuideOptionsColor.html" title="Color management options">Color management options</a></td>
+<td class="navbar-next"><a class="navbar-next" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></td>
 </tr></table></div>
 </body>
 </html>
index 7c7b834..fd4876a 100644 (file)
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li>Stereo image management</li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
index 7c43459..b9b167c 100644 (file)
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
index e988776..ee5984e 100644 (file)
@@ -435,6 +435,7 @@ dd.answer div.label { float: left; }
 <li><a class="xref" href="GuideOptionsKeyboard.html" title="Keyboard Options">Keyboard Options</a></li>
 <li><a class="xref" href="GuideOptionsFiltering.html" title="Files Options">Files Options</a></li>
 <li><a class="xref" href="GuideOptionsMetadata.html" title="Metadata">Metadata</a></li>
+<li><a class="xref" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a></li>
 <li><a class="xref" href="GuideOptionsColor.html" title="Color management options">Color management options</a></li>
 <li><a class="xref" href="GuideOptionsStereo.html" title="Stereo image management">Stereo image management</a></li>
 <li><a class="xref" href="GuideOptionsBehavior.html" title="Behavior Options">Behavior Options</a></li>
index 66e562b..5a9ea87 100644 (file)
@@ -472,10 +472,13 @@ dd.answer div.label { float: left; }
 <span class="label">3.1.4. </span><a class="xref" href="GuideSidebarsInfo.html#Keywords" title="Keywords">Keywords</a>
 </li>
 <li>
-<span class="label">3.1.5. </span><a class="xref" href="GuideSidebarsInfo.html#Listpanes-ExifFileinfoCopyrightLocationandGPS" title="List panes">List panes</a>
+<span class="label">3.1.5. </span><a class="xref" href="GuideSidebarsInfo.html#KeywordAutocompletion" title="Keyword Autocompletion">Keyword Autocompletion</a>
 </li>
 <li>
-<span class="label">3.1.6. </span><a class="xref" href="GuideSidebarsInfo.html#GPSMap" title="Map Display">Map Display</a>
+<span class="label">3.1.6. </span><a class="xref" href="GuideSidebarsInfo.html#Listpanes-ExifFileinfoCopyrightLocationandGPS" title="List panes">List panes</a>
+</li>
+<li>
+<span class="label">3.1.7. </span><a class="xref" href="GuideSidebarsInfo.html#GPSMap" title="Map Display">Map Display</a>
 </li>
 </ul></div>
 <div class="division section">
@@ -624,7 +627,26 @@ dd.answer div.label { float: left; }
 <p class="para block"></p>
 </div>
 <div class="division section">
-<a name="Listpanes-ExifFileinfoCopyrightLocationandGPS"></a><div class="header"><h2 class="section title"><span class="title"><a name="titleExif"></a><span class="label">3.1.5. </span>List panes</span></h2></div>
+<a name="KeywordAutocompletion"></a><div class="header"><h2 class="section title"><span class="title"><a name="titleKeywordAutocompletion"></a><span class="label">3.1.5. </span>Keyword Autocompletion</span></h2></div>
+<p class="para block block-first">
+      The text box at the bottom of the keywords pane is used for autocompletion. Any keywords typed into the standard keyword box or the autocompletion box will be remembered as candidates for future autocompletion.
+      <p class="para block block-first"></p>
+      Frequently used sets of keywords can be entered as comma-delimited lists.
+      <p class="para block"></p>
+      The list of keywords used for autocompletion can be edited on the
+      <a class="link" href="GuideOptionsKeywords.html" title="Keywords">Keywords</a>
+      tab of the Preferences dialog.
+      <p class="para block"></p>
+      The menu action "Keyword autocomplete", set to
+      <span class="code" dir="ltr">Alt + K</span>
+      by default, will shift the keyboard focus to the autocomplete box. Pressing
+      <span class="code" dir="ltr">Alt + K</span>
+      a second time will shift the keyboard focus back to the previous object.
+    </p>
+<p class="para block"></p>
+</div>
+<div class="division section">
+<a name="Listpanes-ExifFileinfoCopyrightLocationandGPS"></a><div class="header"><h2 class="section title"><span class="title"><a name="titleExif"></a><span class="label">3.1.6. </span>List panes</span></h2></div>
 <p class="para block block-first">
       There are five list panes, whose contents can be configured to display Exif, XMP or IPTC metadata tags. The five panes are:
       <div class="block list itemizedlist"><ul class="itemizedlist">
@@ -821,7 +843,7 @@ dd.answer div.label { float: left; }
 <p class="para block"></p>
 </div>
 <div class="division section">
-<a name="GPSMap"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">3.1.6. </span>Map Display</span></h2></div>
+<a name="GPSMap"></a><div class="header"><h2 class="section title"><span class="title"><span class="label">3.1.7. </span>Map Display</span></h2></div>
 <p class="para block block-first">
       Geeqie can display maps created by the
       <a class="ulink" href="https://www.openstreetmap.org" title="https://www.openstreetmap.org">openstreetmap.org</a>