Create/Update file similarity cache
[geeqie.git] / src / preferences.c
index e319c62..2df5c2f 100644 (file)
 #include "preferences.h"
 
 #include "bar_exif.h"
+#include "bar_keywords.h"
 #include "cache.h"
 #include "cache_maint.h"
+#include "dnd.h"
 #include "editors.h"
 #include "exif.h"
 #include "filedata.h"
@@ -36,6 +38,8 @@
 #include "img-view.h"
 #include "layout_config.h"
 #include "layout_util.h"
+#include "metadata.h"
+#include "osd.h"
 #include "pixbuf_util.h"
 #include "slideshow.h"
 #include "toolbar.h"
 #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"
 #include "zonedetect.h"
 
-#include <math.h>
-
 #ifdef HAVE_LCMS
 #ifdef HAVE_LCMS2
 #include <lcms2.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
 {
@@ -109,7 +115,9 @@ gchar *format_class_list[] = {
        N_("Image"),
        N_("RAW Image"),
        N_("Metadata"),
-       N_("Video")
+       N_("Video"),
+       N_("Collection"),
+       N_("Document")
        };
 
 /* config memory values */
@@ -252,7 +260,9 @@ static void config_window_apply(void)
 
        options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
        options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
-       options->file_ops.safe_delete_enable = c_options->file_ops.safe_delete_enable;
+       options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
+       options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
+       options->file_ops.no_trash = c_options->file_ops.no_trash;
        options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
        options->tools_restore_state = c_options->tools_restore_state;
        options->save_window_positions = c_options->save_window_positions;
@@ -285,6 +295,7 @@ static void config_window_apply(void)
        options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
        options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
        options->thumbnails.use_exif = c_options->thumbnails.use_exif;
+       options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
        options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
 //     options->thumbnails.use_ft_metadata_small = c_options->thumbnails.use_ft_metadata_small;
        options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
@@ -367,6 +378,7 @@ static void config_window_apply(void)
        options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
        options->dnd_icon_size = c_options->dnd_icon_size;
        options->clipboard_selection = c_options->clipboard_selection;
+       options->dnd_default_action = c_options->dnd_default_action;
 
        options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
        options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
@@ -408,8 +420,11 @@ static void config_window_apply(void)
        options->info_comment.height = c_options->info_comment.height;
        options->info_rating.height = c_options->info_rating.height;
 
+       options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
+
        options->marks_save = c_options->marks_save;
        options->with_rename = c_options->with_rename;
+       options->collections_on_top = c_options->collections_on_top;
        config_entry_to_option(help_search_engine_entry, &options->help_search_engine, NULL);
 
        options->read_metadata_in_idle = c_options->read_metadata_in_idle;
@@ -435,6 +450,11 @@ static void config_window_apply(void)
                }
 #endif
 
+       options->mouse_button_8 = c_options->mouse_button_8;
+       options->mouse_button_9 = c_options->mouse_button_9;
+
+       config_tab_keywords_save();
+
        image_options_sync();
 
        if (refresh)
@@ -445,7 +465,8 @@ static void config_window_apply(void)
 
        if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, NULL);
 
-       toolbar_apply();
+       toolbar_apply(TOOLBAR_MAIN);
+       toolbar_apply(TOOLBAR_STATUS);
 }
 
 /*
@@ -470,13 +491,16 @@ static void config_window_help_cb(GtkWidget *widget, gpointer data)
        {
        "GuideOptionsGeneral.html",
        "GuideOptionsImage.html",
+       "GuideOptionsOSD.html",
        "GuideOptionsWindow.html",
        "GuideOptionsKeyboard.html",
        "GuideOptionsFiltering.html",
        "GuideOptionsMetadata.html",
+       "GuideOptionsKeywords.html",
        "GuideOptionsColor.html",
        "GuideOptionsStereo.html",
        "GuideOptionsBehavior.html",
+       "GuideOptionsToolbar.html",
        "GuideOptionsToolbar.html"
        };
 
@@ -491,24 +515,14 @@ static gboolean config_window_delete(GtkWidget *widget, GdkEventAny *event, gpoi
 }
 
 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
-{
-       config_window_apply();
-       config_window_close_cb(NULL, NULL);
-}
-
-static void config_window_apply_cb(GtkWidget *widget, gpointer data)
 {
        LayoutWindow *lw;
        lw = layout_window_list->data;
 
        config_window_apply();
        layout_util_sync(lw);
-}
-
-static void config_window_save_cb(GtkWidget *widget, gpointer data)
-{
-       config_window_apply();
        save_options(options);
+       config_window_close_cb(NULL, NULL);
 }
 
 /*
@@ -539,6 +553,24 @@ static void quality_menu_cb(GtkWidget *combo, gpointer data)
                }
 }
 
+static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
+{
+       gint *option = data;
+
+       switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
+               {
+               case 0:
+               default:
+                       *option = DND_ACTION_ASK;
+                       break;
+               case 1:
+                       *option = DND_ACTION_COPY;
+                       break;
+               case 2:
+                       *option = DND_ACTION_MOVE;
+                       break;
+               }
+}
 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
 {
        gint *option = data;
@@ -581,8 +613,34 @@ static void add_quality_menu(GtkWidget *table, gint column, gint row, const gcha
        g_signal_connect(G_OBJECT(combo), "changed",
                         G_CALLBACK(quality_menu_cb), option_c);
 
-       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
+       gtk_widget_show(combo);
+}
+
+static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
+{
+       GtkWidget *combo;
+       gint current = 0;
+
+       *option_c = option;
+
+       pref_table_label(table, column, row, text, 0.0);
+
+       combo = gtk_combo_box_text_new();
+
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
+       if (option == DND_ACTION_ASK) current = 0;
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
+       if (option == DND_ACTION_COPY) current = 1;
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
+       if (option == DND_ACTION_MOVE) current = 2;
+
+       gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
+
+       g_signal_connect(G_OBJECT(combo), "changed",
+                        G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
+
+       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
        gtk_widget_show(combo);
 }
 
@@ -608,8 +666,147 @@ static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row
        g_signal_connect(G_OBJECT(combo), "changed",
                         G_CALLBACK(clipboard_selection_menu_cb), option_c);
 
-       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
+       gtk_widget_show(combo);
+}
+
+typedef struct _UseableMouseItems UseableMouseItems;
+struct _UseableMouseItems
+{
+       gchar *name; /* GtkActionEntry terminology */
+       gchar *label;
+       gchar *stock_id;
+};
+
+static const UseableMouseItems useable_mouse_items[] = {
+       {"", "", NULL},
+       {"FirstImage",  N_("First Image"), GTK_STOCK_GOTO_TOP},
+       {"PrevImage",   N_("Previous Image"), GTK_STOCK_GO_UP},
+       {"NextImage",   N_("Next Image"), GTK_STOCK_GO_DOWN},
+       {"LastImage",   N_("Last Image"), GTK_STOCK_GOTO_BOTTOM},
+       {"Back",        N_("Back"), GTK_STOCK_GO_BACK},
+       {"Forward",     N_("Forward"), GTK_STOCK_GO_FORWARD},
+       {"Home",        N_("Home"), GTK_STOCK_HOME},
+       {"Up",  N_("Up"), GTK_STOCK_GO_UP},
+       {"FirstPage",   N_("First page"), GTK_STOCK_MEDIA_PREVIOUS},
+       {"LastPage",    N_("Last Page"), GTK_STOCK_MEDIA_NEXT},
+       {"NextPage",    N_("Next page"), GTK_STOCK_MEDIA_FORWARD},
+       {"PrevPage",    N_("Previous Page"), GTK_STOCK_MEDIA_REWIND},
+       {"NewWindow",   N_("New _window"), GTK_STOCK_NEW},
+       {"NewCollection",       N_("New collection"), GTK_STOCK_INDEX},
+       {"OpenCollection",      N_("Open collection"), GTK_STOCK_OPEN},
+       {"Search",      N_("Search"), GTK_STOCK_FIND},
+       {"FindDupes",   N_("Find duplicates"), GTK_STOCK_FIND},
+       {"NewFolder",   N_("New folder"),GTK_STOCK_DIRECTORY},
+       {"Copy",        N_("Copy"), GTK_STOCK_COPY},
+       {"Move",        N_("Move"), PIXBUF_INLINE_ICON_MOVE},
+       {"Rename",      N_("Rename"), PIXBUF_INLINE_ICON_RENAME},
+       {"Delete",      N_("Delete"), GTK_STOCK_DELETE},
+       {"CloseWindow", N_("Close Window"), GTK_STOCK_CLOSE},
+       {"PanView",     N_("Pan view"), PIXBUF_INLINE_ICON_PANORAMA},
+       {"SelectAll",   N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL},
+       {"SelectNone",  N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE},
+       {"SelectInvert",        N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT},
+       {"ShowFileFilter",      N_("Show file filter"), PIXBUF_INLINE_ICON_FILE_FILTER},
+       {"RectangularSelection",        N_("Select rectangle"), PIXBUF_INLINE_ICON_SELECT_RECTANGLE},
+       {"Print",       N_("Print"), GTK_STOCK_PRINT},
+       {"Preferences", N_("Preferences"), GTK_STOCK_PREFERENCES},
+       {"LayoutConfig",        N_("Configure this window"), GTK_STOCK_PREFERENCES},
+       {"Maintenance", N_("Cache maintenance"), PIXBUF_INLINE_ICON_MAINTENANCE},
+       {"RotateCW",    N_("Rotate clockwise 90°"), PIXBUF_INLINE_ICON_CW},
+       {"RotateCCW",   N_("Rotate counterclockwise 90°"), PIXBUF_INLINE_ICON_CCW},
+       {"Rotate180",   N_("Rotate 180°"), PIXBUF_INLINE_ICON_180},
+       {"Mirror",      N_("Mirror"), PIXBUF_INLINE_ICON_MIRROR},
+       {"Flip",        N_("Flip"), PIXBUF_INLINE_ICON_FLIP},
+       {"AlterNone",   N_("Original state"), PIXBUF_INLINE_ICON_ORIGINAL},
+       {"ZoomIn",      N_("Zoom in"), GTK_STOCK_ZOOM_IN},
+       {"ZoomOut",     N_("Zoom out"), GTK_STOCK_ZOOM_OUT},
+       {"Zoom100",     N_("Zoom 1:1"), GTK_STOCK_ZOOM_100},
+       {"ZoomFit",     N_("Zoom to fit"), GTK_STOCK_ZOOM_FIT},
+       {"ZoomFillHor", N_("Fit Horizontaly"), PIXBUF_INLINE_ICON_ZOOMFILLHOR},
+       {"ZoomFillVert",        N_("Fit vertically"), PIXBUF_INLINE_ICON_ZOOMFILLVERT},
+       {"Zoom200",     N_("Zoom 2:1"), GTK_STOCK_FILE},
+       {"Zoom300",     N_("Zoom 3:1"), GTK_STOCK_FILE},
+       {"Zoom400",     N_("Zoom 4:1"), GTK_STOCK_FILE},
+       {"Zoom50",      N_("Zoom 1:2"), GTK_STOCK_FILE},
+       {"Zoom33",      N_("Zoom1:3"), GTK_STOCK_FILE},
+       {"Zoom25",      N_("Zoom 1:4"), GTK_STOCK_FILE},
+       {"ConnectZoomIn",       N_("Connected Zoom in"), GTK_STOCK_ZOOM_IN},
+       {"Grayscale",   N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
+       {"OverUnderExposed",    N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
+       {"HideTools",   N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
+       {"SlideShowPause",      N_("Pause slideshow"), GTK_STOCK_MEDIA_PAUSE},
+       {"SlideShowFaster",     N_("Slideshow Faster"), GTK_STOCK_FILE},
+       {"SlideShowSlower",     N_("Slideshow Slower"), GTK_STOCK_FILE},
+       {"Refresh",     N_("Refresh"), GTK_STOCK_REFRESH},
+       {"HelpContents",        N_("Help"), GTK_STOCK_HELP},
+       {"ExifWin",     N_("Exif window"), PIXBUF_INLINE_ICON_EXIF},
+       {"Thumbnails",  N_("Show thumbnails"), PIXBUF_INLINE_ICON_THUMB},
+       {"ShowMarks",   N_("Show marks"), PIXBUF_INLINE_ICON_MARKS},
+       {"ImageGuidelines",     N_("Show guidelines"), PIXBUF_INLINE_ICON_GUIDELINES},
+       {"DrawRectangle",       N_("Draw Rectangle"), PIXBUF_INLINE_ICON_DRAW_RECTANGLE},
+       {"FloatTools",  N_("Float file list"), PIXBUF_INLINE_ICON_FLOAT},
+       {"SBar",        N_("Info sidebar"), PIXBUF_INLINE_ICON_INFO},
+       {"SBarSort",    N_("Sort manager"), PIXBUF_INLINE_ICON_SORT},
+       {"Quit",        N_("Quit"), GTK_STOCK_QUIT},
+       {NULL,          NULL, NULL}
+};
+
+static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
+{
+       gchar **option = data;
+       gchar *label;
+
+       label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
+
+       const UseableMouseItems *list = useable_mouse_items;
+
+       while (list->name)
+               {
+               if (g_strcmp0(list->label, label) == 0)
+                       {
+                       break;
+                       }
+               list++;
+               }
+
+       g_free(*option);
+       *option = g_strdup(list->name);
+       g_free(label);
+}
+
+static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
+                            gchar *option, gchar **option_c)
+{
+       GtkWidget *combo;
+       gint current = 0;
+       gint i = 0;
+
+       *option_c = option;
+
+       pref_table_label(table, column, row, text, 0.0);
+
+       combo = gtk_combo_box_text_new();
+
+       const UseableMouseItems *list = useable_mouse_items;
+
+       while (list->name)
+               {
+               gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), list->label);
+               if (g_strcmp0(list->name, option) == 0)
+                       {
+                       current = i;
+                       }
+               i++;
+               list++;
+               }
+
+       gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
+
+       g_signal_connect(G_OBJECT(combo), "changed",
+                        G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
+
+       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
        gtk_widget_show(combo);
 }
 
@@ -676,8 +873,7 @@ static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *
        g_signal_connect(G_OBJECT(combo), "changed",
                         G_CALLBACK(thumb_size_menu_cb), NULL);
 
-       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
        gtk_widget_show(combo);
 }
 
@@ -796,8 +992,7 @@ static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const
        g_signal_connect(G_OBJECT(combo), "changed",
                         G_CALLBACK(stereo_mode_menu_cb), option_c);
 
-       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
        gtk_widget_show(combo);
 }
 
@@ -838,8 +1033,7 @@ static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar
        g_signal_connect(G_OBJECT(combo), "changed",
                         G_CALLBACK(video_menu_cb), option_c);
 
-       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
        gtk_widget_show(combo);
 }
 
@@ -1032,7 +1226,6 @@ static gboolean filter_add_scroll(gpointer data)
        GList *list_cells;
        GtkCellRenderer *cell;
        GtkTreeViewColumn *column;
-       GList *list_columns;
        const gchar *title;
        guint i = 0;
        gint rows;
@@ -1040,12 +1233,7 @@ static gboolean filter_add_scroll(gpointer data)
        rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), NULL);
        path = gtk_tree_path_new_from_indices(rows-1, -1);
 
-       list_columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(data));
-       do {
-               column = g_list_nth(list_columns,i)->data;
-               title = gtk_tree_view_column_get_title(GTK_TREE_VIEW_COLUMN(column));
-               i++;
-               } while (strcmp(title, "Filter") !=0 );
+       column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
 
        list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
        cell = g_list_last(list_cells)->data;
@@ -1057,7 +1245,6 @@ static gboolean filter_add_scroll(gpointer data)
 
        gtk_tree_path_free(path);
        g_list_free(list_cells);
-       g_list_free(list_columns);
 
        return(FALSE);
 }
@@ -1216,7 +1403,7 @@ static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
 
 static void image_overlay_help_cb(GtkWidget *widget, gpointer data)
 {
-       help_window_show("GuideOptionsWindow.html#OverlayScreenDisplay");
+       help_window_show("GuideOptionsOSD.html");
 }
 
 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer data)
@@ -1558,22 +1745,14 @@ static void cache_standard_cb(GtkWidget *widget, gpointer data)
                c_options->thumbnails.spec_standard =TRUE;
                c_options->thumbnails.cache_into_dirs = FALSE;
                }
-       else
-               {
-               c_options->thumbnails.spec_standard =FALSE;
-               }
 }
 
 static void cache_geeqie_cb(GtkWidget *widget, gpointer data)
 {
-       if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
-               {
-               c_options->thumbnails.spec_standard =TRUE;
-               c_options->thumbnails.cache_into_dirs = FALSE;
-               }
-       else
+       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
                {
                c_options->thumbnails.spec_standard =FALSE;
+               c_options->thumbnails.cache_into_dirs = FALSE;
                }
 }
 
@@ -1584,10 +1763,6 @@ static void cache_local_cb(GtkWidget *widget, gpointer data)
                c_options->thumbnails.cache_into_dirs = TRUE;
                c_options->thumbnails.spec_standard =FALSE;
                }
-       else
-               {
-               c_options->thumbnails.cache_into_dirs = FALSE;
-               }
 }
 
 static void help_search_engine_entry_icon_cb(GtkEntry *entry, GtkEntryIconPosition pos,
@@ -1692,6 +1867,19 @@ static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
 }
 
 /* general options tab */
+static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
+typedef struct _TZData TZData;
+struct _TZData
+{
+       GenericDialog *gd;
+       GCancellable *cancellable;
+
+       GtkWidget *progress;
+       GFile *tmp_g_file;
+       GFile *timezone_database_gq;
+       gchar *timezone_database_user;
+};
+
 static void config_tab_general(GtkWidget *notebook)
 {
        GtkWidget *vbox;
@@ -1708,6 +1896,12 @@ static void config_tab_general(GtkWidget *notebook)
        GtkWidget *star_rating_entry;
        GString *str;
        gchar *rating_symbol;
+       gchar *path;
+       gchar *basename;
+       GNetworkMonitor *net_mon;
+       GSocketConnectable *geeqie_org;
+       gboolean internet_available;
+       TZData *tz;
 
        vbox = scrolled_notebook_page(notebook, _("General"));
 
@@ -1717,35 +1911,47 @@ static void config_tab_general(GtkWidget *notebook)
        add_thumb_size_menu(table, 0, 0, _("Size:"));
        add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
 
-       ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
+       hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
+       pref_label_new(hbox, _("Custom size: "));
+       pref_spin_new_int(hbox, _("Width:"), NULL, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
+       pref_spin_new_int(hbox, _("Height:"), NULL, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
+
+       ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
                                          options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
 
        subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
        pref_checkbox_link_sensitivity(ct_button, subgroup);
 
+       c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
+       c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
        group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
                                                                                GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
        button = pref_radiobutton_new(group_frame, NULL,  get_thumbnails_cache_dir(),
-                                                       !options->thumbnails.spec_standard,
+                                                       !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
                                                        G_CALLBACK(cache_geeqie_cb), NULL);
 
        group_frame = pref_frame_new(subgroup, TRUE,
                                                        _("Store thumbnails local to image folder (non-standard)"),
                                                        GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
        pref_radiobutton_new(group_frame, button, "*/.thumbnails",
-                                                       options->thumbnails.cache_into_dirs,
+                                                       !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
                                                        G_CALLBACK(cache_local_cb), NULL);
 
        group_frame = pref_frame_new(subgroup, TRUE,
                                                        _("Use standard thumbnail style and cache, shared with other applications"),
                                                        GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
        pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
-                                                       options->thumbnails.spec_standard,
+                                                       options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
                                                        G_CALLBACK(cache_standard_cb), NULL);
 
        pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
                              options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
 
+       spin = pref_spin_new_int(group, _("Collection preview:"), NULL,
+                                1, 999, 1,
+                                options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
+       gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
+
 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
        pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
                              options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
@@ -1754,6 +1960,8 @@ static void config_tab_general(GtkWidget *notebook)
 //                           options->thumbnails.use_ft_metadata_small, &c_options->thumbnails.use_ft_metadata_small);
 #endif
 
+       pref_spacer(group, PREF_PAD_GROUP);
+
        group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
 
        c_options->star_rating.star = options->star_rating.star;
@@ -1823,6 +2031,8 @@ static void config_tab_general(GtkWidget *notebook)
        g_string_free(str, TRUE);
        g_free(rating_symbol);
 
+       pref_spacer(group, PREF_PAD_GROUP);
+
        group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
 
        c_options->slideshow.delay = options->slideshow.delay;
@@ -1853,6 +2063,8 @@ static void config_tab_general(GtkWidget *notebook)
        pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
        pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
 
+       pref_spacer(group, PREF_PAD_GROUP);
+
        group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
 
        pref_spin_new_int(group, _("Decoded image cache size (Mb):"), NULL,
@@ -1863,6 +2075,8 @@ static void config_tab_general(GtkWidget *notebook)
        pref_checkbox_new_int(group, _("Refresh on file change"),
                              options->update_on_time_change, &c_options->update_on_time_change);
 
+       pref_spacer(group, PREF_PAD_GROUP);
+
        group = pref_group_new(vbox, FALSE, _("Info sidebar heights"), GTK_ORIENTATION_VERTICAL);
        pref_label_new(group, _("NOTE! Geeqie must be restarted for changes to take effect"));
        hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
@@ -1879,6 +2093,57 @@ static void config_tab_general(GtkWidget *notebook)
                                 1, 9999, 1,
                                 options->info_rating.height, &c_options->info_rating.height);
 
+       pref_spacer(group, PREF_PAD_GROUP);
+
+       group = pref_group_new(vbox, FALSE, _("Show predefined keyword tree"), GTK_ORIENTATION_VERTICAL);
+
+       pref_checkbox_new_int(group, _("Show predefined keyword tree (NOTE! Geeqie must be restarted for change to take effect)"),
+                               options->show_predefined_keyword_tree, &c_options->show_predefined_keyword_tree);
+
+       pref_spacer(group, PREF_PAD_GROUP);
+
+       net_mon = g_network_monitor_get_default();
+       geeqie_org = g_network_address_parse_uri(GQ_WEBSITE, 80, NULL);
+       internet_available = g_network_monitor_can_reach(net_mon, geeqie_org, NULL, NULL);
+       g_object_unref(geeqie_org);
+
+       group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
+       hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
+
+       if (!internet_available)
+               {
+               gtk_widget_set_sensitive(group, FALSE);
+               }
+
+       tz = g_new0(TZData, 1);
+
+       path = path_from_utf8(TIMEZONE_DATABASE);
+       basename = g_path_get_basename(path);
+       tz->timezone_database_user = g_build_filename(get_rc_dir(), basename, NULL);
+       g_free(path);
+       g_free(basename);
+
+       if (isfile(tz->timezone_database_user))
+               {
+               button = pref_button_new(GTK_WIDGET(hbox), NULL, _("Update"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
+               }
+       else
+               {
+               button = pref_button_new(GTK_WIDGET(hbox), NULL, _("Install"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
+               }
+
+       if (!internet_available)
+               {
+               gtk_widget_set_tooltip_text(button, _("No Internet connection!\nThe timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
+               }
+       else
+               {
+               gtk_widget_set_tooltip_text(button, _("The timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
+               }
+       gtk_widget_show(button);
+
+       pref_spacer(group, PREF_PAD_GROUP);
+
        group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
 
        help_search_engine_entry = gtk_entry_new();
@@ -1990,9 +2255,6 @@ static void config_tab_windows(GtkWidget *notebook)
        GtkWidget *button;
        GtkWidget *ct_button;
        GtkWidget *spin;
-       GtkWidget *image_overlay_template_view;
-       GtkWidget *scrolled;
-       GtkTextBuffer *buffer;
 
        vbox = scrolled_notebook_page(notebook, _("Windows"));
 
@@ -2039,35 +2301,55 @@ static void config_tab_windows(GtkWidget *notebook)
                              options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
        pref_checkbox_new_int(group, _("Disable screen saver"),
                              options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
+}
 
+#define PRE_FORMATTED_COLUMNS 5
+static void config_tab_osd(GtkWidget *notebook)
+{
+       GtkWidget *hbox;
+       GtkWidget *vbox;
+       GtkWidget *vbox_buttons;
+       GtkWidget *group;
+       GtkWidget *button;
+       GtkWidget *image_overlay_template_view;
+       GtkWidget *scrolled;
+       GtkWidget *scrolled_pre_formatted;
+       GtkTextBuffer *buffer;
+       GtkWidget *label;
+       GtkWidget *     subgroup;
+       gint i = 0;
+       gint rows = 0;
+       gint cols = 0;
+
+       vbox = scrolled_notebook_page(notebook, _("OSD"));
+
+       image_overlay_template_view = gtk_text_view_new();
 
        group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
 
+       subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
+
+       scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
+       gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
+       gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
+       gtk_widget_show(scrolled_pre_formatted);
+       gtk_widget_show(subgroup);
+
+       pref_line(group, PREF_PAD_GAP);
+
        pref_label_new(group, _("Image overlay template"));
 
        scrolled = gtk_scrolled_window_new(NULL, NULL);
        gtk_widget_set_size_request(scrolled, 200, 150);
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
-                                      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+                                                                       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
        gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
        gtk_widget_show(scrolled);
 
-       image_overlay_template_view = gtk_text_view_new();
-
        gtk_widget_set_tooltip_markup(image_overlay_template_view,
-       _("<i>%name%</i> results in the filename of the picture.\n"
-         "Also available: <i>%collection%</i>, <i>%number%</i>, <i>%total%</i>, <i>%date%</i>,\n"
-         "<i>%size%</i> (filesize), <i>%width%</i>, <i>%height%</i>, <i>%res%</i> (resolution),\n"
-         "<i>%rating%</i>, <i>%keywords%</i>, <i>%comment%</i> (XMP), <i>%imagecomment%</i> (JPEG)\n"
-         "To access exif data use the exif name, e. g. <i>%formatted.Camera%</i> is the formatted camera name,\n"
-         "<i>%Exif.Photo.DateTimeOriginal%</i> the date of the original shot.\n"
-         "<i>%formatted.Camera:20</i> notation will truncate the displayed data to 20 characters and will add 3 dots at the end to denote the truncation.\n"
-         "If two or more variables are connected with the |-sign, it prints available variables with a separator.\n"
-         "<i>%formatted.ShutterSpeed%</i>|<i>%formatted.ISOSpeedRating%</i>|<i>%formatted.FocalLength%</i> could show \"1/20s - 400 - 80 mm\" or \"1/200 - 80 mm\",\n"
-         "if there's no ISO information in the Exif data.\n"
-         "If a line is empty, it is removed. This allows one to add lines that totally disappear when no data is available.\n"
-       ));
+                                       _("Extensive formatting options are shown in the Help file"));
+
        gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
        gtk_widget_show(image_overlay_template_view);
 
@@ -2112,7 +2394,51 @@ static void config_tab_windows(GtkWidget *notebook)
        g_signal_connect(G_OBJECT(buffer), "changed",
                         G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
 
+       pref_line(group, PREF_PAD_GAP);
 
+       group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
+       hbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
+       gtk_widget_show(hbox);
+       label = gtk_label_new(_("%Exif.Image.Orientation%"));
+       gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
+       gtk_widget_show(label);
+       pref_spacer(group,TRUE);
+
+       group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
+       hbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
+       gtk_widget_show(hbox);
+       label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
+       gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
+       gtk_widget_show(label);
+       pref_spacer(group,TRUE);
+
+       group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
+       hbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
+       gtk_widget_show(hbox);
+       label = gtk_label_new(_("%path:39%"));
+       gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
+       gtk_widget_show(label);
+       pref_spacer(group,TRUE);
+
+       group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
+       hbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
+       gtk_widget_show(hbox);
+       label = gtk_label_new(_("Text shown only if the field is non-null:\n%formatted.Aperture:F no. * setting%\n %formatted.Aperture:10:F no. * setting%"));
+       gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
+       gtk_widget_show(label);
+       pref_spacer(group,TRUE);
+
+       group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
+       hbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
+       gtk_widget_show(hbox);
+       label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
+       gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
+       gtk_widget_show(label);
 }
 
 static GtkTreeModel *create_class_model(void)
@@ -2392,6 +2718,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)
@@ -2444,8 +3113,7 @@ static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar
        g_signal_connect(G_OBJECT(combo), "changed",
                         G_CALLBACK(intent_menu_cb), option_c);
 
-       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
        gtk_widget_show(combo);
 }
 #endif
@@ -2532,6 +3200,32 @@ static void config_tab_color(GtkWidget *notebook)
 }
 
 /* advanced entry tab */
+static void use_geeqie_trash_cb(GtkWidget *widget, gpointer data)
+{
+       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+               {
+               c_options->file_ops.use_system_trash = FALSE;
+               c_options->file_ops.no_trash = FALSE;
+               }
+}
+
+static void use_system_trash_cb(GtkWidget *widget, gpointer data)
+{
+       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+               {
+               c_options->file_ops.use_system_trash = TRUE;
+               c_options->file_ops.no_trash = FALSE;
+               }
+}
+
+static void use_no_cache_cb(GtkWidget *widget, gpointer data)
+{
+       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+               {
+               c_options->file_ops.no_trash = TRUE;
+               }
+}
+
 static void config_tab_behavior(GtkWidget *notebook)
 {
        GtkWidget *hbox;
@@ -2544,18 +3238,21 @@ static void config_tab_behavior(GtkWidget *notebook)
        GtkWidget *table;
        GtkWidget *marks;
        GtkWidget *with_rename;
+       GtkWidget *collections_on_top;
 
        vbox = scrolled_notebook_page(notebook, _("Behavior"));
 
        group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
 
-       pref_checkbox_new_int(group, _("Confirm file delete"),
+       pref_checkbox_new_int(group, _("Confirm permanent file delete"),
                              options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
+       pref_checkbox_new_int(group, _("Confirm move file to Trash"),
+                             options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
        pref_checkbox_new_int(group, _("Enable Delete key"),
                              options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
 
-       ct_button = pref_checkbox_new_int(group, _("Safe delete"),
-                                         options->file_ops.safe_delete_enable, &c_options->file_ops.safe_delete_enable);
+       ct_button = pref_radiobutton_new(group, NULL, _("Use Geeqie trash location"),
+                                       !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),NULL);
 
        hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
        pref_checkbox_link_sensitivity(ct_button, hbox);
@@ -2583,8 +3280,16 @@ static void config_tab_behavior(GtkWidget *notebook)
        button = pref_button_new(NULL, GTK_STOCK_CLEAR, NULL, FALSE,
                                 G_CALLBACK(safe_delete_clear_cb), NULL);
        gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+       pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
+                                       options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), NULL);
+
+       pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
+                       options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), NULL);
+
        gtk_widget_show(button);
 
+       pref_spacer(group, PREF_PAD_GROUP);
+
 
        group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
 
@@ -2605,15 +3310,24 @@ static void config_tab_behavior(GtkWidget *notebook)
                                options->with_rename, &c_options->with_rename);
        gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
 
+       collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
+                               options->collections_on_top, &c_options->collections_on_top);
+       gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
+
        pref_spin_new_int(group, _("Recent folder list maximum size"), NULL,
                          1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
 
        pref_spin_new_int(group, _("Drag'n drop icon size"), NULL,
                          16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
 
+       table = pref_table_new(group, 2, 1, FALSE, FALSE);
+       add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
+
        table = pref_table_new(group, 2, 1, FALSE, FALSE);
        add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
 
+       pref_spacer(group, PREF_PAD_GROUP);
+
        group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
 
        pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
@@ -2629,8 +3343,15 @@ static void config_tab_behavior(GtkWidget *notebook)
        table = pref_table_new(group, 2, 1, FALSE, FALSE);
        add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
 
+       table = pref_table_new(group, 2, 1, FALSE, FALSE);
+       table = pref_table_new(group, 2, 1, FALSE, FALSE);
+       add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
+       table = pref_table_new(group, 2, 1, FALSE, FALSE);
+       add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
 
 #ifdef DEBUG
+       pref_spacer(group, PREF_PAD_GROUP);
+
        group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
 
        pref_spin_new_int(group, _("Debug level:"), NULL,
@@ -2748,8 +3469,8 @@ static void config_tab_accelerators(GtkWidget *notebook)
        gtk_widget_show(button);
 }
 
-/* toolbar tab */
-static void config_tab_toolbar(GtkWidget *notebook)
+/* toolbar main tab */
+static void config_tab_toolbar_main(GtkWidget *notebook)
 {
        GtkWidget *vbox;
        GtkWidget *toolbardata;
@@ -2757,9 +3478,25 @@ static void config_tab_toolbar(GtkWidget *notebook)
 
        lw = layout_window_list->data;
 
-       vbox = scrolled_notebook_page(notebook, _("Toolbar"));
+       vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
 
-       toolbardata = toolbar_select_new(lw);
+       toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
+       gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
+       gtk_widget_show(vbox);
+}
+
+/* toolbar status tab */
+static void config_tab_toolbar_status(GtkWidget *notebook)
+{
+       GtkWidget *vbox;
+       GtkWidget *toolbardata;
+       LayoutWindow *lw;
+
+       lw = layout_window_list->data;
+
+       vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
+
+       toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
        gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
        gtk_widget_show(vbox);
 }
@@ -2853,6 +3590,7 @@ static void config_window_create(void)
        if (!c_options) c_options = init_options(NULL);
 
        configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, NULL, _("Preferences"));
+       DEBUG_NAME(configwindow);
        gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
        g_signal_connect(G_OBJECT(configwindow), "delete_event",
                         G_CALLBACK(config_window_delete), NULL);
@@ -2865,19 +3603,23 @@ static void config_window_create(void)
        gtk_widget_show(win_vbox);
 
        notebook = gtk_notebook_new();
-       gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
+       gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
+       gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
        gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
 
        config_tab_general(notebook);
        config_tab_image(notebook);
+       config_tab_osd(notebook);
        config_tab_windows(notebook);
        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);
-       config_tab_toolbar(notebook);
+       config_tab_toolbar_main(notebook);
+       config_tab_toolbar_status(notebook);
 
        hbox = gtk_hbutton_box_new();
        gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
@@ -2900,18 +3642,6 @@ static void config_window_create(void)
 
        ct_button = button;
 
-       button = pref_button_new(NULL, GTK_STOCK_SAVE, NULL, FALSE,
-                                G_CALLBACK(config_window_save_cb), NULL);
-       gtk_container_add(GTK_CONTAINER(hbox), button);
-       gtk_widget_set_can_default(button, TRUE);
-       gtk_widget_show(button);
-
-       button = pref_button_new(NULL, GTK_STOCK_APPLY, NULL, FALSE,
-                                G_CALLBACK(config_window_apply_cb), NULL);
-       gtk_container_add(GTK_CONTAINER(hbox), button);
-       gtk_widget_set_can_default(button, TRUE);
-       gtk_widget_show(button);
-
        button = pref_button_new(NULL, GTK_STOCK_CANCEL, NULL, FALSE,
                                 G_CALLBACK(config_window_close_cb), NULL);
        gtk_container_add(GTK_CONTAINER(hbox), button);
@@ -2960,7 +3690,8 @@ void show_about_window(LayoutWindow *lw)
        gint i_authors = 0;
        gchar *path;
        GString *copyright;
-       gchar *zd_path;
+       gchar *timezone_path;
+       gchar *basename;
        ZoneDetect *cd;
        FILE *fp = NULL;
 #define LINE_LENGTH 1000
@@ -2969,14 +3700,21 @@ void show_about_window(LayoutWindow *lw)
        copyright = g_string_new(NULL);
        copyright = g_string_append(copyright, "This program comes with absolutely no warranty.\nGNU General Public License, version 2 or later.\nSee https://www.gnu.org/licenses/old-licenses/gpl-2.0.html\n\n");
 
-       zd_path = g_build_filename(GQ_BIN_DIR, TIMEZONE_DATABASE, NULL);
-       cd = ZDOpenDatabase(zd_path);
-       if (cd)
+       path = path_from_utf8(TIMEZONE_DATABASE);
+       basename = g_path_get_basename(path);
+       timezone_path = g_build_filename(get_rc_dir(), basename, NULL);
+       if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
                {
-               copyright = g_string_append(copyright, ZDGetNotice(cd));
+               cd = ZDOpenDatabase(timezone_path);
+               if (cd)
+                       {
+                       copyright = g_string_append(copyright, ZDGetNotice(cd));
+                       ZDCloseDatabase(cd);
+                       }
                }
-       ZDCloseDatabase(cd);
-       g_free(zd_path);
+       g_free(path);
+       g_free(timezone_path);
+       g_free(basename);
 
        authors[0] = NULL;
        path = g_build_filename(GQ_HELPDIR, "AUTHORS", NULL);
@@ -3040,4 +3778,98 @@ static void image_overlay_set_text_colours()
        c_options->image_overlay.background_blue = options->image_overlay.background_blue;
        c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
 }
+
+/*
+ *-----------------------------------------------------------------------------
+ * timezone database routines
+ *-----------------------------------------------------------------------------
+ */
+
+static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
+{
+       GError *error = NULL;
+       TZData *tz = data;
+       gchar *tmp_filename;
+
+       if (!g_cancellable_is_cancelled(tz->cancellable))
+               {
+               generic_dialog_close(tz->gd);
+               }
+
+
+       if (g_file_copy_finish(G_FILE(source_object), res, &error))
+               {
+               tmp_filename = g_file_get_parse_name(tz->tmp_g_file);
+               move_file(tmp_filename, tz->timezone_database_user);
+               g_free(tmp_filename);
+               }
+       else
+               {
+               file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, NULL);
+               }
+
+       g_file_delete(tz->tmp_g_file, NULL, &error);
+       g_object_unref(tz->tmp_g_file);
+       tz->tmp_g_file = NULL;
+       g_object_unref(tz->cancellable);
+       g_object_unref(tz->timezone_database_gq);
+}
+
+static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
+{
+       TZData *tz = data;
+
+       if (!g_cancellable_is_cancelled(tz->cancellable))
+               {
+               gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), (gdouble)current_num_bytes / total_num_bytes);
+               }
+}
+
+static void timezone_cancel_button_cb(GenericDialog *gd, gpointer data)
+{
+       TZData *tz = data;
+       GError *error = NULL;
+
+       g_cancellable_cancel(tz->cancellable);
+}
+
+static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
+{
+       TZData *tz = data;
+       GError *error = NULL;
+       GFileIOStream *io_stream;
+
+       if (tz->tmp_g_file)
+               {
+               return;
+               }
+
+       tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
+
+       if (error)
+               {
+               file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, NULL);
+               log_printf("Error: Download timezone database failed:\n%s", error->message);
+               g_error_free(error);
+               g_object_unref(tz->tmp_g_file);
+               }
+       else
+               {
+               tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE);
+
+               tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", NULL, TRUE, timezone_cancel_button_cb, tz);
+
+               generic_dialog_add_message(tz->gd, GTK_STOCK_DIALOG_INFO, _("Downloading timezone database"), NULL, FALSE);
+
+               tz->progress = gtk_progress_bar_new();
+               gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
+               gtk_widget_show(tz->progress);
+
+               gtk_widget_show(tz->gd->dialog);
+               tz->cancellable = g_cancellable_new();
+               g_file_copy_async(tz->timezone_database_gq, tz->tmp_g_file, G_FILE_COPY_OVERWRITE, G_PRIORITY_LOW, tz->cancellable, timezone_progress_cb, tz, timezone_async_ready_cb, tz);
+
+               gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
+               }
+}
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */