changeset 2623:f5886b8e1968

Fix #444: User-definable toolbar https://github.com/BestImageViewer/geeqie/issues/444 Set via an additional tab on the Preferences dialogue
author Colin Clark <colin.clark@cclark.uk>
date Tue, 10 Oct 2017 10:53:31 +0100
parents cad1ce03590d
children af19f970be03
files doc/docbook/GuideOptionsMain.xml doc/docbook/GuideOptionsToolbar.xml src/Makefile.am src/layout.c src/layout_util.c src/layout_util.h src/preferences.c src/rcfile.c src/toolbar.c src/toolbar.h src/typedefs.h
diffstat 11 files changed, 783 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/doc/docbook/GuideOptionsMain.xml	Sun Oct 08 13:30:05 2017 +0100
+++ b/doc/docbook/GuideOptionsMain.xml	Tue Oct 10 10:53:31 2017 +0100
@@ -30,6 +30,7 @@
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsColor.xml" />
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsStereo.xml" />
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsBehavior.xml" />
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsToolbar.xml" />
     <para />
 
 </chapter>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/docbook/GuideOptionsToolbar.xml	Tue Oct 10 10:53:31 2017 +0100
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<section id="GuideOptionsSteroa">
+  <title>Toolbar</title>
+  <para>
+    This dialogue enables you to change the items displayed on the Toolbar.
+    <para />
+    The initial display shows the current setup. You may re-order these items by right-clicking on any item.
+    <para />
+    Additional items may be included by clicking on the "Plus" symbol at the bottom of the tab. A list of all available options is displayed.
+  </para>
+  <para>Most of the Geeqie menu items are available, plus any desktop (External Editor) files.</para>
+  <note>
+    <para>
+      If you select a desktop file, ensure that it includes an icon - the desktop file format is described in the
+      <link linkend="GuideReferenceStandards">Standards section</link>
+    </para>
+  </note>
+</section>
--- a/src/Makefile.am	Sun Oct 08 13:30:05 2017 +0100
+++ b/src/Makefile.am	Tue Oct 10 10:53:31 2017 +0100
@@ -239,6 +239,8 @@
 	thumb.h		\
 	thumb_standard.c	\
 	thumb_standard.h	\
+	toolbar.c	\
+	toolbar.h	\
 	trash.c		\
 	trash.h		\
 	uri_utils.c	\
--- a/src/layout.c	Sun Oct 08 13:30:05 2017 +0100
+++ b/src/layout.c	Tue Oct 10 10:53:31 2017 +0100
@@ -2489,6 +2489,8 @@
 	bar_sort_write_config(lw->bar_sort, outstr, indent + 1);
 	bar_write_config(lw->bar, outstr, indent + 1);
 
+	layout_toolbar_write_config(lw, TOOLBAR_MAIN, outstr, indent + 1);
+
 	WRITE_NL(); WRITE_STRING("</layout>");
 }
 
--- a/src/layout_util.c	Sun Oct 08 13:30:05 2017 +0100
+++ b/src/layout_util.c	Tue Oct 10 10:53:31 2017 +0100
@@ -44,6 +44,7 @@
 #include "pixbuf_util.h"
 #include "preferences.h"
 #include "print.h"
+#include "rcfile.h"
 #include "search.h"
 #include "slideshow.h"
 #include "ui_fileops.h"
@@ -1741,7 +1742,7 @@
   { "OpenRecent",	NULL,			N_("Open recen_t"),			NULL,			N_("Open recent"),			NULL },
   { "Search",		GTK_STOCK_FIND,		N_("_Search..."),			"F3",			N_("Search..."),			CB(layout_menu_search_cb) },
   { "FindDupes",	GTK_STOCK_FIND,		N_("_Find duplicates..."),		"D",			N_("Find duplicates..."),		CB(layout_menu_dupes_cb) },
-  { "PanView",		NULL,			N_("Pa_n view"),			"<control>J",		N_("Pan view"),				CB(layout_menu_pan_cb) },
+  { "PanView",		GTK_STOCK_FILE,			N_("Pa_n view"),			"<control>J",		N_("Pan view"),				CB(layout_menu_pan_cb) },
   { "Print",		GTK_STOCK_PRINT,	N_("_Print..."),			"<shift>P",		N_("Print..."),				CB(layout_menu_print_cb) },
   { "NewFolder",	GTK_STOCK_DIRECTORY,	N_("N_ew folder..."),			"<control>F",		N_("New folder..."),			CB(layout_menu_dir_cb) },
   { "Copy",		GTK_STOCK_COPY,		N_("_Copy..."),				"<control>C",		N_("Copy..."),				CB(layout_menu_copy_cb) },
@@ -1774,7 +1775,7 @@
   { "Preferences",	GTK_STOCK_PREFERENCES,	N_("P_references..."),			"<control>O",		N_("Preferences..."),			CB(layout_menu_config_cb) },
   { "Editors",		GTK_STOCK_PREFERENCES,	N_("Configure _Editors..."),		NULL,			N_("Configure Editors..."),		CB(layout_menu_editors_cb) },
   { "LayoutConfig",	GTK_STOCK_PREFERENCES,	N_("_Configure this window..."),	NULL,			N_("Configure this window..."),		CB(layout_menu_layout_config_cb) },
-  { "Maintenance",	NULL,			N_("_Thumbnail maintenance..."),	NULL,			N_("Thumbnail maintenance..."),		CB(layout_menu_remove_thumb_cb) },
+  { "Maintenance",	GTK_STOCK_FILE,			N_("_Thumbnail maintenance..."),	NULL,			N_("Thumbnail 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) },
   { "ZoomIn",		GTK_STOCK_ZOOM_IN,	N_("Zoom _in"),				"equal",		N_("Zoom in"),				CB(layout_menu_zoom_in_cb) },
@@ -1785,14 +1786,14 @@
   { "Zoom100Alt1",	GTK_STOCK_ZOOM_100,	N_("Zoom _1:1"),			"KP_Divide",		N_("Zoom 1:1"),				CB(layout_menu_zoom_1_1_cb) },
   { "ZoomFit",		GTK_STOCK_ZOOM_FIT,	N_("_Zoom to fit"),			"X",			N_("Zoom to fit"),			CB(layout_menu_zoom_fit_cb) },
   { "ZoomFitAlt1",	GTK_STOCK_ZOOM_FIT,	N_("_Zoom to fit"),			"KP_Multiply",		N_("Zoom to fit"),			CB(layout_menu_zoom_fit_cb) },
-  { "ZoomFillHor",	NULL,			N_("Fit _Horizontally"),		"H",			N_("Fit Horizontally"),			CB(layout_menu_zoom_fit_hor_cb) },
-  { "ZoomFillVert",	NULL,			N_("Fit _Vertically"),			"W",			N_("Fit Vertically"),			CB(layout_menu_zoom_fit_vert_cb) },
-  { "Zoom200",	        NULL,			N_("Zoom _2:1"),			NULL,			N_("Zoom 2:1"),				CB(layout_menu_zoom_2_1_cb) },
-  { "Zoom300",	        NULL,			N_("Zoom _3:1"),			NULL,			N_("Zoom 3:1"),				CB(layout_menu_zoom_3_1_cb) },
-  { "Zoom400",		NULL,			N_("Zoom _4:1"),			NULL,			N_("Zoom 4:1"),				CB(layout_menu_zoom_4_1_cb) },
-  { "Zoom50",		NULL,			N_("Zoom 1:2"),				NULL,			N_("Zoom 1:2"),				CB(layout_menu_zoom_1_2_cb) },
-  { "Zoom33",		NULL,			N_("Zoom 1:3"),				NULL,			N_("Zoom 1:3"),				CB(layout_menu_zoom_1_3_cb) },
-  { "Zoom25",		NULL,			N_("Zoom 1:4"),				NULL,			N_("Zoom 1:4"),				CB(layout_menu_zoom_1_4_cb) },
+  { "ZoomFillHor",	GTK_STOCK_FILE,			N_("Fit _Horizontally"),		"H",			N_("Fit Horizontally"),			CB(layout_menu_zoom_fit_hor_cb) },
+  { "ZoomFillVert",	GTK_STOCK_FILE,			N_("Fit _Vertically"),			"W",			N_("Fit Vertically"),			CB(layout_menu_zoom_fit_vert_cb) },
+  { "Zoom200",	        GTK_STOCK_FILE,			N_("Zoom _2:1"),			NULL,			N_("Zoom 2:1"),				CB(layout_menu_zoom_2_1_cb) },
+  { "Zoom300",	        GTK_STOCK_FILE,			N_("Zoom _3:1"),			NULL,			N_("Zoom 3:1"),				CB(layout_menu_zoom_3_1_cb) },
+  { "Zoom400",		GTK_STOCK_FILE,			N_("Zoom _4:1"),			NULL,			N_("Zoom 4:1"),				CB(layout_menu_zoom_4_1_cb) },
+  { "Zoom50",		GTK_STOCK_FILE,			N_("Zoom 1:2"),				NULL,			N_("Zoom 1:2"),				CB(layout_menu_zoom_1_2_cb) },
+  { "Zoom33",		GTK_STOCK_FILE,			N_("Zoom 1:3"),				NULL,			N_("Zoom 1:3"),				CB(layout_menu_zoom_1_3_cb) },
+  { "Zoom25",		GTK_STOCK_FILE,			N_("Zoom 1:4"),				NULL,			N_("Zoom 1:4"),				CB(layout_menu_zoom_1_4_cb) },
   { "ConnectZoomIn",	GTK_STOCK_ZOOM_IN,	N_("Zoom _in"),				"plus",			N_("Connected Zoom in"),		CB(layout_menu_connect_zoom_in_cb) },
   { "ConnectZoomInAlt1",GTK_STOCK_ZOOM_IN,	N_("Zoom _in"),				"<shift>KP_Add",	N_("Connected Zoom in"),		CB(layout_menu_connect_zoom_in_cb) },
   { "ConnectZoomOut",	GTK_STOCK_ZOOM_OUT,	N_("Zoom _out"),			"underscore",		N_("Connected Zoom out"),		CB(layout_menu_connect_zoom_out_cb) },
@@ -1818,10 +1819,10 @@
   { "ImageOverlayCycle",NULL,			N_("_Cycle through overlay modes"),	"I",			N_("Cycle through Overlay modes"),	CB(layout_menu_overlay_toggle_cb) },
   { "HistogramChanCycle",NULL,			N_("Cycle through histogram ch_annels"),"K",			N_("Cycle through histogram channels"),	CB(layout_menu_histogram_toggle_channel_cb) },
   { "HistogramModeCycle",NULL,			N_("Cycle through histogram mo_des"),	"J",			N_("Cycle through histogram modes"),	CB(layout_menu_histogram_toggle_mode_cb) },
-  { "HideTools",	NULL,			N_("_Hide file list"),			"<control>H",		N_("Hide file list"),			CB(layout_menu_hide_cb) },
+  { "HideTools",	GTK_STOCK_FILE,			N_("_Hide file list"),			"<control>H",		N_("Hide file list"),			CB(layout_menu_hide_cb) },
   { "SlideShowPause",	GTK_STOCK_MEDIA_PAUSE,	N_("_Pause slideshow"), 		"P",			N_("Pause slideshow"), 			CB(layout_menu_slideshow_pause_cb) },
-  { "SlideShowFaster",	NULL,	N_("Faster"), 		"<control>KP_Add",			N_("Faster"), 			CB(layout_menu_slideshow_faster_cb) },
-  { "SlideShowSlower",	NULL,	N_("Slower"), 		"<control>KP_Subtract",			N_("Slower"), 			CB(layout_menu_slideshow_slower_cb) },
+  { "SlideShowFaster",	GTK_STOCK_FILE,	N_("Faster"), 		"<control>KP_Add",			N_("Faster"), 			CB(layout_menu_slideshow_faster_cb) },
+  { "SlideShowSlower",	GTK_STOCK_FILE,	N_("Slower"), 		"<control>KP_Subtract",			N_("Slower"), 			CB(layout_menu_slideshow_slower_cb) },
   { "Refresh",		GTK_STOCK_REFRESH,	N_("_Refresh"),				"R",			N_("Refresh"),				CB(layout_menu_refresh_cb) },
   { "HelpContents",	GTK_STOCK_HELP,		N_("_Contents"),			"F1",			N_("Contents"),				CB(layout_menu_help_cb) },
   { "HelpShortcuts",	NULL,			N_("_Keyboard shortcuts"),		NULL,			N_("Keyboard shortcuts"),		CB(layout_menu_help_keys_cb) },
@@ -1830,7 +1831,7 @@
   { "HelpChangeLog",	NULL,			N_("_ChangeLog"),			NULL,			N_("ChangeLog notes"),			CB(layout_menu_changelog_cb) },
   { "About",		GTK_STOCK_ABOUT,	N_("_About"),				NULL,			N_("About"),				CB(layout_menu_about_cb) },
   { "LogWindow",	NULL,			N_("_Log Window"),			NULL,			N_("Log Window"),			CB(layout_menu_log_window_cb) },
-  { "ExifWin",		NULL,			N_("_Exif window"),			"<control>E",		N_("Exif window"),			CB(layout_menu_bar_exif_cb) },
+  { "ExifWin",		GTK_STOCK_FILE,			N_("_Exif window"),			"<control>E",		N_("Exif window"),			CB(layout_menu_bar_exif_cb) },
   { "StereoCycle",	NULL,			N_("_Cycle through stereo modes"),	NULL,			N_("Cycle through stereo modes"),	CB(layout_menu_stereo_mode_next_cb) },
   { "SplitNextPane",	NULL,			N_("_Next Pane"),	"<alt>Right",			N_("Next Pane"),	CB(layout_menu_split_pane_next_cb) },
   { "SplitPreviousPane",	NULL,			N_("_Previous Pane"),	"<alt>Left",			N_("Previous Pane"),	CB(layout_menu_split_pane_prev_cb) },
@@ -1843,12 +1844,12 @@
 
 static GtkToggleActionEntry menu_toggle_entries[] = {
   { "Thumbnails",	PIXBUF_INLINE_ICON_THUMB,N_("Show _Thumbnails"),		"T",			N_("Show Thumbnails"),			CB(layout_menu_thumb_cb),	 FALSE },
-  { "ShowMarks",        NULL,			N_("Show _Marks"),			"M",			N_("Show Marks"),			CB(layout_menu_marks_cb),	 FALSE  },
+  { "ShowMarks",        GTK_STOCK_FILE,			N_("Show _Marks"),			"M",			N_("Show Marks"),			CB(layout_menu_marks_cb),	 FALSE  },
   { "ShowInfoPixel",	GTK_STOCK_COLOR_PICKER,	N_("Pi_xel Info"),			NULL,			N_("Show Pixel Info"),			CB(layout_menu_info_pixel_cb),	 FALSE  },
   { "FloatTools",	PIXBUF_INLINE_ICON_FLOAT,N_("_Float file list"),		"L",			N_("Float file list"),			CB(layout_menu_float_cb),	 FALSE  },
   { "HideToolbar",	NULL,			N_("Hide tool_bar"),			NULL,			N_("Hide toolbar"),			CB(layout_menu_toolbar_cb),	 FALSE  },
-  { "SBar",		NULL,			N_("_Info sidebar"),			"<control>K",		N_("Info sidebar"),			CB(layout_menu_bar_cb),		 FALSE  },
-  { "SBarSort",		NULL,			N_("Sort _manager"),			"<shift>S",		N_("Sort manager"),			CB(layout_menu_bar_sort_cb),	 FALSE  },
+  { "SBar",		GTK_STOCK_FILE,			N_("_Info sidebar"),			"<control>K",		N_("Info sidebar"),			CB(layout_menu_bar_cb),		 FALSE  },
+  { "SBarSort",		GTK_STOCK_FILE,			N_("Sort _manager"),			"<shift>S",		N_("Sort manager"),			CB(layout_menu_bar_sort_cb),	 FALSE  },
   { "HideBars",		NULL,			N_("Hide Bars"),			"grave",		N_("Hide Bars"),			CB(layout_menu_hide_bars_cb),	 FALSE  },
   { "SlideShow",	GTK_STOCK_MEDIA_PLAY,	N_("Toggle _slideshow"),		"S",			N_("Toggle slideshow"),			CB(layout_menu_slideshow_cb),	 FALSE  },
   { "UseColorProfiles",	GTK_STOCK_SELECT_COLOR,	N_("Use _color profiles"), 		NULL,			N_("Use color profiles"), 		CB(layout_color_menu_enable_cb), FALSE},
@@ -2138,18 +2139,6 @@
 "    </menu>"
 "  </menubar>"
 "  <toolbar name='ToolBar'>"
-"    <toolitem action='Thumbnails'/>"
-"    <toolitem action='Back'/>"
-"    <toolitem action='Forward'/>"
-"    <toolitem action='Up'/>"
-"    <toolitem action='Home'/>"
-"    <toolitem action='Refresh'/>"
-"    <toolitem action='ZoomIn'/>"
-"    <toolitem action='ZoomOut'/>"
-"    <toolitem action='ZoomFit'/>"
-"    <toolitem action='Zoom100'/>"
-"    <toolitem action='Preferences'/>"
-"    <toolitem action='FloatTools'/>"
 "  </toolbar>"
 "  <toolbar name='StatusBar'>"
 "    <toolitem action='ExifRotate'/>"
@@ -2483,6 +2472,9 @@
 		exit(EXIT_FAILURE);
 		}
 
+	layout_toolbar_clear(lw, TOOLBAR_MAIN);
+	layout_toolbar_add_default(lw, TOOLBAR_MAIN);
+
 	DEBUG_1("%s layout_actions_setup: marks", get_exec_time());
 	layout_actions_setup_marks(lw);
 
@@ -2608,6 +2600,170 @@
 	return lw->toolbar[type];
 }
 
+void layout_toolbar_clear(LayoutWindow *lw, ToolbarType type)
+{
+	if (lw->toolbar_merge_id[type])
+		{
+		gtk_ui_manager_remove_ui(lw->ui_manager, lw->toolbar_merge_id[type]);
+		gtk_ui_manager_ensure_update(lw->ui_manager);
+		}
+	string_list_free(lw->toolbar_actions[type]);
+	lw->toolbar_actions[type] = NULL;
+
+	lw->toolbar_merge_id[type] = gtk_ui_manager_new_merge_id(lw->ui_manager);
+}
+
+void layout_toolbar_add(LayoutWindow *lw, ToolbarType type, const gchar *action)
+{
+	const gchar *path = NULL;
+
+	if (!action || !lw->ui_manager) return;
+
+	if (g_list_find_custom(lw->toolbar_actions[type], action, (GCompareFunc)strcmp)) return;
+
+	switch (type)
+		{
+		case TOOLBAR_MAIN:
+			path = "/ToolBar";
+			break;
+		default:
+			break;
+		}
+
+
+	if (g_str_has_suffix(action, ".desktop"))
+		{
+		/* this may be called before the external editors are read
+		   create a dummy action for now */
+		if (!lw->action_group_editors)
+			{
+			lw->action_group_editors = gtk_action_group_new("MenuActionsExternal");
+			gtk_ui_manager_insert_action_group(lw->ui_manager, lw->action_group_editors, 1);
+			}
+		if (!gtk_action_group_get_action(lw->action_group_editors, action))
+			{
+			GtkActionEntry entry = { action,
+			                         GTK_STOCK_MISSING_IMAGE,
+			                         action,
+			                         NULL,
+			                         NULL,
+			                         NULL };
+			DEBUG_1("Creating temporary action %s", action);
+			gtk_action_group_add_actions(lw->action_group_editors, &entry, 1, lw);
+			}
+		}
+	gtk_ui_manager_add_ui(lw->ui_manager, lw->toolbar_merge_id[type], path, action, action, GTK_UI_MANAGER_TOOLITEM, FALSE);
+	lw->toolbar_actions[type] = g_list_append(lw->toolbar_actions[type], g_strdup(action));
+}
+
+
+void layout_toolbar_add_default(LayoutWindow *lw, ToolbarType type)
+{
+	LayoutWindow *lw_first;
+	GList *work_action;
+
+	switch (type)
+		{
+		case TOOLBAR_MAIN:
+			if (layout_window_list)
+				{
+				lw_first = layout_window_list->data;
+				if (lw_first->toolbar_actions[TOOLBAR_MAIN])
+					{
+					work_action = lw_first->toolbar_actions[type];
+					while (work_action)
+						{
+						gchar *action = work_action->data;
+						work_action = work_action->next;
+						layout_toolbar_add(lw, type, action);
+						}
+					}
+				else
+					{
+					layout_toolbar_add(lw, type, "Thumbnails");
+					layout_toolbar_add(lw, type, "Back");
+					layout_toolbar_add(lw, type, "Forward");
+					layout_toolbar_add(lw, type, "Up");
+					layout_toolbar_add(lw, type, "Home");
+					layout_toolbar_add(lw, type, "Refresh");
+					layout_toolbar_add(lw, type, "ZoomIn");
+					layout_toolbar_add(lw, type, "ZoomOut");
+					layout_toolbar_add(lw, type, "ZoomFit");
+					layout_toolbar_add(lw, type, "Zoom100");
+					layout_toolbar_add(lw, type, "Preferences");
+					layout_toolbar_add(lw, type, "FloatTools");
+					}
+				}
+			else
+				{
+				layout_toolbar_add(lw, type, "Thumbnails");
+				layout_toolbar_add(lw, type, "Back");
+				layout_toolbar_add(lw, type, "Forward");
+				layout_toolbar_add(lw, type, "Up");
+				layout_toolbar_add(lw, type, "Home");
+				layout_toolbar_add(lw, type, "Refresh");
+				layout_toolbar_add(lw, type, "ZoomIn");
+				layout_toolbar_add(lw, type, "ZoomOut");
+				layout_toolbar_add(lw, type, "ZoomFit");
+				layout_toolbar_add(lw, type, "Zoom100");
+				layout_toolbar_add(lw, type, "Preferences");
+				layout_toolbar_add(lw, type, "FloatTools");
+				}
+			break;
+		default:
+			break;
+		}
+}
+
+
+
+void layout_toolbar_write_config(LayoutWindow *lw, ToolbarType type, GString *outstr, gint indent)
+{
+	const gchar *name = NULL;
+	GList *work = lw->toolbar_actions[type];
+
+	switch (type)
+		{
+		case TOOLBAR_MAIN:
+			name = "toolbar";
+			break;
+		default:
+			break;
+		}
+
+	WRITE_NL(); WRITE_STRING("<%s>", name);
+	indent++;
+	WRITE_NL(); WRITE_STRING("<clear/>");
+	while (work)
+		{
+		gchar *action = work->data;
+		work = work->next;
+		WRITE_NL(); WRITE_STRING("<toolitem ");
+		write_char_option(outstr, indent + 1, "action", action);
+		WRITE_STRING("/>");
+		}
+	indent--;
+	WRITE_NL(); WRITE_STRING("</%s>", name);
+}
+
+void layout_toolbar_add_from_config(LayoutWindow *lw, ToolbarType type, const char **attribute_names, const gchar **attribute_values)
+{
+	gchar *action = NULL;
+
+	while (*attribute_names)
+		{
+		const gchar *option = *attribute_names++;
+		const gchar *value = *attribute_values++;
+
+		if (READ_CHAR_FULL("action", action)) continue;
+
+		log_printf("unknown attribute %s = %s\n", option, value);
+		}
+
+	layout_toolbar_add(lw, type, action);
+	g_free(action);
+}
+
 /*
  *-----------------------------------------------------------------------------
  * misc
--- a/src/layout_util.h	Sun Oct 08 13:30:05 2017 +0100
+++ b/src/layout_util.h	Tue Oct 10 10:53:31 2017 +0100
@@ -47,9 +47,14 @@
 void layout_actions_setup(LayoutWindow *lw);
 void layout_actions_add_window(LayoutWindow *lw, GtkWidget *window);
 GtkWidget *layout_actions_menu_bar(LayoutWindow *lw);
+void layout_toolbar_add_from_config(LayoutWindow *lw, ToolbarType type, const gchar **attribute_names, const gchar **attribute_values);
 
 GtkWidget *layout_actions_toolbar(LayoutWindow *lw, ToolbarType type);
 
+void layout_toolbar_write_config(LayoutWindow *lw, ToolbarType type, GString *outstr, gint indent);
+void layout_toolbar_clear(LayoutWindow *lw, ToolbarType type);
+void layout_toolbar_add(LayoutWindow *lw, ToolbarType type, const gchar *action);
+void layout_toolbar_add_default(LayoutWindow *lw, ToolbarType type);
 void layout_keyboard_init(LayoutWindow *lw, GtkWidget *window);
 
 void layout_bar_toggle(LayoutWindow *lw);
--- a/src/preferences.c	Sun Oct 08 13:30:05 2017 +0100
+++ b/src/preferences.c	Tue Oct 10 10:53:31 2017 +0100
@@ -38,6 +38,7 @@
 #include "layout_util.h"
 #include "pixbuf_util.h"
 #include "slideshow.h"
+#include "toolbar.h"
 #include "trash.h"
 #include "utilops.h"
 #include "ui_fileops.h"
@@ -426,6 +427,8 @@
 		}
 
 	if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, NULL);
+
+	toolbar_apply();
 }
 
 /*
@@ -2404,6 +2407,22 @@
 	gtk_widget_show(button);
 }
 
+/* toolbar tab */
+static void config_tab_toolbar(GtkWidget *notebook)
+{
+	GtkWidget *vbox;
+	GtkWidget *toolbardata;
+	LayoutWindow *lw;
+
+	lw = layout_window_list->data;
+
+	vbox = scrolled_notebook_page(notebook, _("Toolbar"));
+
+	toolbardata = toolbar_select_new(lw);
+	gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
+	gtk_widget_show(vbox);
+}
+
 /* stereo tab */
 static void config_tab_stereo(GtkWidget *notebook)
 {
@@ -2517,6 +2536,7 @@
 	config_tab_color(notebook);
 	config_tab_stereo(notebook);
 	config_tab_behavior(notebook);
+	config_tab_toolbar(notebook);
 
 	hbox = gtk_hbutton_box_new();
 	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
--- a/src/rcfile.c	Sun Oct 08 13:30:05 2017 +0100
+++ b/src/rcfile.c	Tue Oct 10 10:53:31 2017 +0100
@@ -1073,13 +1073,24 @@
 		}
 }
 
-/* Just a dummy function to parse out old leftovers
- *
- * This function can be cleaned somedays.
- */
-static void options_parse_toolbar_and_statusbar(GQParserData *parser_data, GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer data, GError **error)
+static void options_parse_toolbar(GQParserData *parser_data, GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer data, GError **error)
 {
-	options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+	LayoutWindow *lw = data;
+	if (g_ascii_strcasecmp(element_name, "toolitem") == 0)
+		{
+		layout_toolbar_add_from_config(lw, TOOLBAR_MAIN, attribute_names, attribute_values);
+		options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+		}
+	else if (g_ascii_strcasecmp(element_name, "clear") == 0)
+		{
+		layout_toolbar_clear(lw, TOOLBAR_MAIN);
+		options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+		}
+	else
+		{
+		log_printf("unexpected in <toolbar>: <%s>\n", element_name);
+		options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+		}
 }
 
 static void options_parse_layout(GQParserData *parser_data, GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer data, GError **error)
@@ -1106,11 +1117,11 @@
 		}
 	else if (g_ascii_strcasecmp(element_name, "toolbar") == 0)
 		{
-		options_parse_func_push(parser_data, options_parse_toolbar_and_statusbar, NULL, NULL);
+		options_parse_func_push(parser_data, options_parse_toolbar, NULL, lw);
 		}
 	else if (g_ascii_strcasecmp(element_name, "statusbar") == 0)
 		{
-		options_parse_func_push(parser_data, options_parse_toolbar_and_statusbar, NULL, NULL);
+		options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
 		}
 	else
 		{
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/toolbar.c	Tue Oct 10 10:53:31 2017 +0100
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2004 John Ellis
+ * Copyright (C) 2008 - 2017 The Geeqie Team
+ *
+ * Author: Colin Clark
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "main.h"
+#include "toolbar.h"
+
+#include "collect.h"
+#include "layout_util.h"
+#include "ui_fileops.h"
+#include "ui_misc.h"
+#include "pixbuf_util.h"
+#include "ui_menu.h"
+#include "editors.h"
+
+/** Implements the user-definable toolbar function
+ * Called from the Preferences/toolbar tab
+ **/
+
+typedef struct _ToolbarData ToolbarData;
+struct _ToolbarData
+{
+	GtkWidget *widget;
+	GtkWidget *vbox;
+	GtkWidget *add_button;
+
+	LayoutWindow *lw;
+};
+
+typedef struct _ToolbarButtonData ToolbarButtonData;
+struct _ToolbarButtonData
+{
+	GtkWidget *button;
+	GtkWidget *button_label;
+	GtkWidget *image;
+
+	gchar *name; /* GtkActionEntry terminology */
+	gchar *stock_id;
+};
+
+static 	ToolbarData *toolbarlist;
+
+typedef struct _UseableToolbarItems UseableToolbarItems;
+struct _UseableToolbarItems
+{
+	gchar *name; /* GtkActionEntry terminology */
+	gchar *label;
+	gchar *stock_id;
+};
+
+/* FIXME Should be created by program from menu_entries[]
+ * in layout_util.c */
+ /** The user is limited to selecting from this list of menu items
+  * plus any desktop files
+  **/
+static const UseableToolbarItems useable_toolbar_items[] = {
+	{"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},
+	{"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},
+	{"PanView",	N_("Pan view"), GTK_STOCK_FILE},
+	{"Print",	N_("Print"), GTK_STOCK_PRINT},
+	{"Preferences",	N_("Preferences"), GTK_STOCK_PREFERENCES},
+	{"LayoutConfig",	N_("Configure this window"), GTK_STOCK_PREFERENCES},
+	{"Maintenance",	N_("Thumbnail maintenance"), GTK_STOCK_FILE},
+	{"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"), GTK_STOCK_FILE},
+	{"ZoomFillVert",	N_("Fit vertically"), GTK_STOCK_FILE},
+	{"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},
+	{"HideTools",	N_("Hide file list"), GTK_STOCK_FILE},
+	{"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"), GTK_STOCK_FILE},
+	{"Thumbnails",	N_("Show thumbnails"), PIXBUF_INLINE_ICON_THUMB},
+	{"ShowMarks",	N_("Show marks"), GTK_STOCK_FILE},
+	{"FloatTools",	N_("Float file list"), PIXBUF_INLINE_ICON_FLOAT},
+	{"SBar",	N_("Info sidebar"), GTK_STOCK_FILE},
+	{"SBarSort",	N_("Sort manager"), GTK_STOCK_FILE},
+	{"Quit",	N_("Quit"), GTK_STOCK_QUIT},
+	{NULL,		NULL}
+};
+
+/**
+ * @brief
+ * @param widget Not used
+ * @param data Pointer to vbox list item
+ * @param up Up/Down movement
+ * @param single_step Move up/down one step, or to top/bottom
+ * 
+ */
+static void toolbar_item_move(GtkWidget *widget, gpointer data,
+									gboolean up, gboolean single_step)
+{
+	GtkWidget *list_item = data;
+	GtkWidget *box;
+	gint pos = 0;
+
+	if (!list_item) return;
+	box = gtk_widget_get_ancestor(list_item, GTK_TYPE_BOX);
+	if (!box) return;
+
+	gtk_container_child_get(GTK_CONTAINER(box), list_item, "position", &pos, NULL);
+
+	if (single_step)
+		{
+		pos = up ? (pos - 1) : (pos + 1);
+		if (pos < 0) pos = 0;
+		}
+	else
+		{
+		pos = up ? 0 : -1;
+		}
+
+	gtk_box_reorder_child(GTK_BOX(box), list_item, pos);
+}
+
+static void toolbar_item_move_up_cb(GtkWidget *widget, gpointer data)
+{
+	toolbar_item_move(widget, data, TRUE, TRUE);
+}
+
+static void toolbar_item_move_down_cb(GtkWidget *widget, gpointer data)
+{
+	toolbar_item_move(widget, data, FALSE, TRUE);
+}
+
+static void toolbar_item_move_top_cb(GtkWidget *widget, gpointer data)
+{
+	toolbar_item_move(widget, data, TRUE, FALSE);
+}
+
+static void toolbar_item_move_bottom_cb(GtkWidget *widget, gpointer data)
+{
+	toolbar_item_move(widget, data, FALSE, FALSE);
+}
+
+static void toolbar_item_delete_cb(GtkWidget *widget, gpointer data)
+{
+	gtk_widget_destroy(data);
+}
+
+static void toolbar_menu_popup(GtkWidget *widget)
+{
+	GtkWidget *menu;
+	GtkWidget *vbox;
+
+	vbox = gtk_widget_get_parent(widget);
+
+	menu = popup_menu_short_lived();
+
+	if (widget)
+		{
+		menu_item_add_stock(menu, _("Move to _top"), GTK_STOCK_GOTO_TOP, G_CALLBACK(toolbar_item_move_top_cb), widget);
+		menu_item_add_stock(menu, _("Move _up"), GTK_STOCK_GO_UP, G_CALLBACK(toolbar_item_move_up_cb), widget);
+		menu_item_add_stock(menu, _("Move _down"), GTK_STOCK_GO_DOWN, G_CALLBACK(toolbar_item_move_down_cb), widget);
+		menu_item_add_stock(menu, _("Move to _bottom"), GTK_STOCK_GOTO_BOTTOM, G_CALLBACK(toolbar_item_move_bottom_cb), widget);
+		menu_item_add_divider(menu);
+		menu_item_add_stock(menu, _("Remove"), GTK_STOCK_DELETE, G_CALLBACK(toolbar_item_delete_cb), widget);
+		menu_item_add_divider(menu);
+		}
+
+	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, vbox, 0, GDK_CURRENT_TIME);
+}
+
+static gboolean toolbar_press_cb(GtkWidget *button, GdkEventButton *event, gpointer data)
+{
+	ToolbarButtonData *button_data = data;
+
+	if (event->button == MOUSE_BUTTON_RIGHT)
+		{
+		toolbar_menu_popup(button_data->button);
+		return TRUE;
+		}
+	return FALSE;
+}
+
+static void get_toolbar_item(const gchar *name, gchar **label, gchar **stock_id)
+{
+	const UseableToolbarItems *list = useable_toolbar_items;
+	*label = NULL;
+	*stock_id = NULL;
+
+	while (list->name)
+		{
+		if (g_strcmp0(list->name, name) == 0)
+			{
+			*label = g_strdup(list->label);
+			*stock_id = g_strdup(list->stock_id);
+			break;
+			}
+		list++;
+		}
+}
+
+
+static void toolbar_item_free(ToolbarButtonData *tbbd)
+{
+	if (!tbbd) return;
+
+	g_free(tbbd->name);
+	g_free(tbbd->stock_id);
+	g_free(tbbd);
+}
+
+static void toolbarlist_add_button(const gchar *name, const gchar *label,
+									const gchar *stock_id, GtkBox *box)
+{
+	ToolbarButtonData *toolbar_entry;
+	GtkWidget *hbox;
+
+	toolbar_entry = g_new(ToolbarButtonData,1);
+	toolbar_entry->button = gtk_button_new();
+	gtk_button_set_relief(GTK_BUTTON(toolbar_entry->button), GTK_RELIEF_NONE);
+	gtk_box_pack_start(GTK_BOX(box), toolbar_entry->button, FALSE, FALSE, 0);
+	gtk_widget_show(toolbar_entry->button);
+
+	g_object_set_data_full(G_OBJECT(toolbar_entry->button), "toolbarbuttondata",
+	toolbar_entry, (GDestroyNotify)toolbar_item_free);
+
+	hbox = gtk_hbox_new(FALSE, PREF_PAD_BUTTON_GAP);
+	gtk_container_add(GTK_CONTAINER(toolbar_entry->button), hbox);
+	gtk_widget_show(hbox);
+
+	toolbar_entry->button_label = gtk_label_new(label);
+	toolbar_entry->name = g_strdup(name);
+	toolbar_entry->stock_id = g_strdup(stock_id);
+	g_signal_connect(G_OBJECT(toolbar_entry->button), "button_release_event",
+									G_CALLBACK(toolbar_press_cb), toolbar_entry);
+
+	if (toolbar_entry->stock_id)
+		{
+		GdkPixbuf *pixbuf;
+		gchar *iconl;
+		iconl = path_from_utf8(toolbar_entry->stock_id);
+		pixbuf = gdk_pixbuf_new_from_file(iconl, NULL);
+		g_free(iconl);
+		if (pixbuf)
+			{
+			GdkPixbuf *scaled;
+			gint w, h;
+
+			w = h = 16;
+			gtk_icon_size_lookup(GTK_ICON_SIZE_BUTTON, &w, &h);
+
+			scaled = gdk_pixbuf_scale_simple(pixbuf, w, h,
+							 GDK_INTERP_BILINEAR);
+			toolbar_entry->image = gtk_image_new_from_pixbuf(scaled);
+
+			g_object_unref(scaled);
+			g_object_unref(pixbuf);
+			}
+		else
+			{
+			toolbar_entry->image = gtk_image_new_from_stock(toolbar_entry->stock_id,
+														GTK_ICON_SIZE_BUTTON);
+			}
+		}
+	else
+		{
+		toolbar_entry->image = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO,
+														GTK_ICON_SIZE_BUTTON);
+		}
+	gtk_box_pack_start(GTK_BOX(hbox), toolbar_entry->image, FALSE, FALSE, 0);
+	gtk_widget_show(toolbar_entry->image);
+	gtk_box_pack_start(GTK_BOX(hbox), toolbar_entry->button_label, FALSE, FALSE, 0);
+	gtk_widget_show(toolbar_entry->button_label);
+}
+
+static void toolbarlist_add_cb(GtkWidget *widget, gpointer data)
+{
+	const gchar *name = g_object_get_data(G_OBJECT(widget), "toolbar_add_name");
+	const gchar *label = g_object_get_data(G_OBJECT(widget), "toolbar_add_label");
+	const gchar *stock_id = g_object_get_data(G_OBJECT(widget), "toolbar_add_stock_id");
+	ToolbarData *tbbd = data;
+
+	toolbarlist_add_button(name, label, stock_id, GTK_BOX(tbbd->vbox));
+}
+
+static void get_desktop_data(const gchar *name, gchar **label, gchar **stock_id)
+{
+	GList *editors_list;
+	GList *work;
+	*label = NULL;
+	*stock_id = NULL;
+
+	editors_list = editor_list_get();
+	work = editors_list;
+	while (work)
+		{
+		const EditorDescription *editor = work->data;
+
+		if (g_strcmp0(name, editor->key) == 0)
+			{
+			*label = g_strdup(editor->name);
+			*stock_id = g_strdup(editor->icon);
+			break;
+			}
+		work = work->next;
+		}
+	g_list_free(editors_list);
+}
+
+static void toolbar_menu_add_popup(GtkWidget *widget, gpointer data)
+{
+	GtkWidget *menu;
+	GList *editors_list;
+	GList *work;
+	ToolbarData *toolbarlist = data;
+	const UseableToolbarItems *list = useable_toolbar_items;
+
+	menu = popup_menu_short_lived();
+
+	/* get standard menu item data */
+	while (list->name)
+		{
+		GtkWidget *item;
+		item = menu_item_add_stock(menu, list->label, GTK_STOCK_ADD,
+										G_CALLBACK(toolbarlist_add_cb), toolbarlist);
+		g_object_set_data(G_OBJECT(item), "toolbar_add_name", g_strdup(list->name));
+		g_object_set_data(G_OBJECT(item), "toolbar_add_label", g_strdup(list->label));
+		g_object_set_data(G_OBJECT(item), "toolbar_add_stock_id", g_strdup(list->stock_id));
+		list++;
+		}
+
+	/* get desktop file data */
+	editors_list = editor_list_get();
+	work = editors_list;
+	while (work)
+		{
+		const EditorDescription *editor = work->data;
+
+		GtkWidget *item;
+		item = menu_item_add_stock(menu, editor->name, GTK_STOCK_ADD,
+										G_CALLBACK(toolbarlist_add_cb), toolbarlist);
+		g_object_set_data(G_OBJECT(item), "toolbar_add_name", g_strdup(editor->key));
+		g_object_set_data(G_OBJECT(item), "toolbar_add_label", g_strdup(editor->name));
+		g_object_set_data(G_OBJECT(item), "toolbar_add_stock_id", g_strdup(editor->icon));
+		work = work->next;
+		}
+	g_list_free(editors_list);
+
+	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, widget, 0, GDK_CURRENT_TIME);
+}
+
+static gboolean toolbar_menu_add_cb(GtkWidget *widget, gpointer data)
+{
+	ToolbarData *toolbarlist = data;
+
+	toolbar_menu_add_popup(widget, toolbarlist);
+	return TRUE;
+}
+
+/**
+ * @brief For each layoutwindow, clear toolbar and reload with current selection
+ * 
+ */
+void toolbar_apply()
+{
+	LayoutWindow *lw;
+	GList *work_windows;
+	GList *work_toolbar;
+
+	work_windows = layout_window_list;
+	while (work_windows)
+		{
+		lw = work_windows->data;
+
+		layout_toolbar_clear(lw, TOOLBAR_MAIN);
+
+		work_toolbar = gtk_container_get_children(GTK_CONTAINER(toolbarlist->vbox));
+		while (work_toolbar)
+			{
+			GtkButton *button = work_toolbar->data;
+			ToolbarButtonData *tbbd;
+
+			tbbd = g_object_get_data(G_OBJECT(button),"toolbarbuttondata");
+			layout_toolbar_add(lw, TOOLBAR_MAIN, tbbd->name);
+
+			work_toolbar = work_toolbar->next;
+			}
+		g_list_free(work_toolbar);
+
+		work_windows = work_windows->next;
+		}
+
+}
+
+/**
+ * @brief Load the current toolbar items into the vbox
+ * @param lw 
+ * @param box The vbox displayed in the preferences Toolbar tab
+ * 
+ * Get the current contents of the toolbar, both menu items
+ * and desktop items, and load them into the vbox
+ */
+static void toolbarlist_populate(LayoutWindow *lw, GtkBox *box)
+{
+	GList *work = g_list_first(lw->toolbar_actions[TOOLBAR_MAIN]);
+
+	while (work)
+		{
+		gchar *name = work->data;
+		gchar *label;
+		gchar *icon;
+		work = work->next;
+
+		if (file_extension_match(name, ".desktop"))
+			{
+			get_desktop_data(name, &label, &icon);
+			}
+		else
+			{
+			get_toolbar_item(name, &label, &icon);
+			}
+		toolbarlist_add_button(name, label, icon, box);
+		}
+}
+
+GtkWidget *toolbar_select_new(LayoutWindow *lw)
+{
+	GtkWidget *scrolled;
+	GtkWidget *tbar;
+	GtkWidget *add_box;
+
+	if (!lw) return NULL;
+
+	if (!toolbarlist)
+		{
+		toolbarlist = g_new0(ToolbarData, 1);
+		}
+	toolbarlist->lw = lw;
+
+	toolbarlist->widget = gtk_vbox_new(FALSE, PREF_PAD_GAP);
+	gtk_widget_show(toolbarlist->widget);
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+							GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
+	gtk_box_pack_start(GTK_BOX(toolbarlist->widget), scrolled, TRUE, TRUE, 0);
+	gtk_widget_show(scrolled);
+
+	toolbarlist->vbox = gtk_vbox_new(FALSE, 0);
+	gtk_widget_show(toolbarlist->vbox);
+	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), toolbarlist->vbox);
+	gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(scrolled))),
+																GTK_SHADOW_NONE);
+
+	add_box = gtk_vbox_new(FALSE, 0);
+	gtk_widget_show(add_box);
+	gtk_box_pack_end(GTK_BOX(toolbarlist->widget), add_box, FALSE, FALSE, 0);
+	tbar = pref_toolbar_new(add_box, GTK_TOOLBAR_ICONS);
+	toolbarlist->add_button = pref_toolbar_button(tbar, GTK_STOCK_ADD, "NULL", FALSE,
+											_("Add Toolbar Item"),
+											G_CALLBACK(toolbar_menu_add_cb), toolbarlist);
+	gtk_widget_show(toolbarlist->add_button);
+
+	toolbarlist_populate(lw,GTK_BOX(toolbarlist->vbox));
+
+	return toolbarlist->widget;
+}
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/toolbar.h	Tue Oct 10 10:53:31 2017 +0100
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2004 John Ellis
+ * Copyright (C) 2008 - 2017 The Geeqie Team
+ *
+ * Author: Colin Clark
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TOOLBAR_H
+#define TOOLBAR_H
+
+GtkWidget *toolbar_select_new(LayoutWindow *lw);
+void toolbar_apply();
+#endif
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/typedefs.h	Sun Oct 08 13:30:05 2017 +0100
+++ b/src/typedefs.h	Tue Oct 10 10:53:31 2017 +0100
@@ -686,6 +686,7 @@
 	GtkActionGroup *action_group_editors;
 	guint ui_editors_id;
 	GtkUIManager *ui_manager;
+	guint toolbar_merge_id[TOOLBAR_COUNT];
 	GList *toolbar_actions[TOOLBAR_COUNT];
 
 	GtkWidget *path_entry;