/* $Id: e2p_config.c 706 2007-11-06 09:13:06Z tpgww $

Copyright (C) 2006-2007 tooar <tooar@gmx.net>

This file is part of emelFM2.
emelFM2 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 3, or (at your option)
any later version.

emelFM2 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 emelFM2; see the file GPL. If not, contact the Free Software
Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

/**
@file plugins/e2p_config.c
@brief emelfm2 config-data export/import plugin
*/

#include "emelfm2.h"
#include <string.h>
#include "e2_plugins.h"
#include "e2_dialog.h"
#include "e2_task.h"
#include "e2_filetype.h"

#define ANAME "config"
#ifndef UPGRADE_PNAME
#define UPGRADE_PNAME "e2p_upgrade.so"
#endif
//NOTE this needs to be the minimum actionable version in the
//upgrade plugin or else that will just install defaults
#ifndef OLDEST_UPGRADE
#define OLDEST_UPGRADE "0.2.0"
#endif

typedef struct _E2P_ConfigData
{
	GtkWidget *dialog;
	GtkWidget *save_entry;
	GtkWidget *open_entry;
	GtkWidget *expander;
	GtkWidget *icondir_entry;
	GtkWidget *iconsavedir_entry;
} E2P_ConfigData;

typedef enum
{
	ALL_P = 0,
	NONTREE_P, ALLTREE_P, CUSTOM_P,
	MARKS_P, FILETYPES_P,
	BINDINGS_P, ALIASES_P,
	PLUGINS_P, MENU_P, CUSTMENU_P,
	PANEBAR1_P, PANEBAR2_P, TASKBAR_P, CMDBAR_P,
	MAX_FLAGS	//no. of entries in the array
} flag_t;

typedef enum
{
	E2PC_ALL       = 1 << ALL_P,
	E2PC_NONTREE   = 1 << NONTREE_P,
	E2PC_ALLTREE   = 1 << ALLTREE_P,
	E2PC_CUSTOM    = 1 << CUSTOM_P,
	E2PC_MARKS     = 1 << MARKS_P,
	E2PC_FILETYPES = 1 << FILETYPES_P,
	E2PC_BINDINGS  = 1 << BINDINGS_P,
	E2PC_ALIASES   = 1 << ALIASES_P,
	E2PC_PLUGINS   = 1 << PLUGINS_P,
	E2PC_MENU      = 1 << MENU_P,
	E2PC_CUSTMENU  = 1 << CUSTMENU_P,
	E2PC_PANEBAR1  = 1 << PANEBAR1_P,
	E2PC_PANEBAR2  = 1 << PANEBAR2_P,
	E2PC_TASKBAR   = 1 << TASKBAR_P,
	E2PC_CMDBAR    = 1 << CMDBAR_P,
} bitflag_t;

#define E2PC_ALLTREEMASK E2PC_FILETYPES | E2PC_BINDINGS | E2PC_ALIASES \
	| E2PC_PLUGINS | E2PC_MENU | E2PC_CUSTMENU | E2PC_PANEBAR1 \
	| E2PC_PANEBAR2 | E2PC_TASKBAR | E2PC_CMDBAR
//for determining index of some specific tree
#define FIRSTTREE_P MARKS_P

//these treeset "internal" names are in same order as the flag_t enumerator
static gchar *set_private_names [] =
{
	"bookmarks",
	"filetypes",
	"keybindings",
	"command-aliases",
	"plugins",
	"context-menu",
	"custom-menus",
	"panebar1",
	"panebar2",
	"taskbar",
	"commandbar",
};

//cache for toggle values, static for the session
static gboolean flags[MAX_FLAGS];
GPtrArray *treeset_names = NULL;	//names to use when checking importable tree options
gboolean rebuild_needed;
E2P_ConfigData *srt;	//copy of runtime ptr for some funcs that don't get rt directly

  /*********************/
 /***** utilities *****/
/*********************/

/**
@brief get mnemonic'ed label for set corresponding to @a f

@param f enumerated value of option to be processed

@return newly-allocated utf8 label
*/
static gchar *_e2pc_get_setlabel (flag_t f)
{
	//no label-mnemonic that competes with the close button
	//nicer to do this lookup just once !
	gunichar close_mnemonic[2] = {0};
	close_mnemonic[0] = e2_utils_get_mnemonic_char (E2_BUTTON_CLOSE.label);

	gchar *private_name = set_private_names [f - FIRSTTREE_P];
	E2_OptionSet *set = e2_option_get (private_name);
	//quick, flawed approach to underscoring for keyboard speedup
	gchar *label;
	if (close_mnemonic[0] != (gunichar)'\0' &&
		g_str_has_prefix (set->desc, (gchar *) close_mnemonic))
		label = g_strdup (set->desc);
	else
		label = g_strconcat ("_", set->desc, NULL);
	return label;
}
/**
@brief set specified flag to T/F

The relevant array value is set

@param f enumerated value of flag to be set
@param value new value for the flag

@return
*/
static void _e2pc_set_flag (flag_t f, gboolean value)
{
	if (f < MAX_FLAGS)
		flags[ (gint) f] = value;
}
/**
@brief return the value of a specified flag

@param f enumerated value of flag to be interrogated
@param rt UNUSED pointer to dialog data struct

@return flag value, T/F, or FALSE if the value is not recognised
*/
static gboolean _e2pc_get_flag (flag_t f)
{
	if (f < MAX_FLAGS)
		return (flags[(gint) f]);
	else
		return (FALSE);
}
/**
@brief toggle specified option flag

@param widget clicked button, UNUSED
@param flagnum pointerized number of the flag to be toggled

@return
*/
static void _e2pc_toggle_cb (GtkWidget *widget, gpointer flagnum)
{
	flag_t flg = (flag_t) flagnum;
	gboolean newflag = ! _e2pc_get_flag (flg);
	_e2pc_set_flag (flg, newflag);
	//handle here all the 'special cases', if any
/*	if (newflag)
	{
	  switch (GPOINTER_TO_INT(flagnum))
	  {
		  default:
			break;
	  }
	} */
	if (flg == CUSTOM_P)
		gtk_expander_set_expanded (GTK_EXPANDER (srt->expander), newflag);
}
/**
@brief create and show a button in a specified container

@param container the widget into which the button is to be placed
@param f enumerated value of flag to be associated with the button
@param state T/F initial state of the toggle
@param label  translated string for the button label
@param callback callback function to be connected

@return the button widget
*/
/*static GtkWidget *_e2pc_create_option_button (GtkWidget *container,
	flag_t f, gboolean state, gchar *label, gpointer callback_fn)
{
	GtkWidget *btn = gtk_check_button_new_with_mnemonic (label);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (btn), state);
	g_signal_connect (G_OBJECT (btn), "toggled", G_CALLBACK (callback_fn),
		(gpointer) f);  //CHECKME = correct value?
	gtk_container_add (GTK_CONTAINER (container), btn);
	gtk_widget_show (btn);
	return (btn);
} */
/**
@brief create and show a check button in a specified container

@param container the widget into which the button is to be placed
@param f enumerated value of flag to be associated with the button
@param state T/F initial state of the toggle
@param label  translated string for the button label

@return the button widget (UNUSED, now)
*/
static GtkWidget *_e2pc_create_check_button (GtkWidget *container,
	flag_t f, gboolean state, gchar *label)
{
//	GtkWidget *btn = _e2pc_create_option_button
//		(container, f, state, label, _e2pc_toggle_cb);
	GtkWidget *btn = gtk_check_button_new_with_mnemonic (label);
	g_signal_connect (G_OBJECT (btn), "toggled", G_CALLBACK (_e2pc_toggle_cb),
		(gpointer) f);  //CHECKME = correct value?
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (btn), state);
	gtk_container_add (GTK_CONTAINER (container), btn);
	gtk_widget_show (btn);
	return (btn);
}
/**
@brief create and show a radio btn in a specified container
The leader of a group is initialized to TRUE, other group members
may cause that to be changed
@param container the widget into which the button is to be placed
@param f enumerated value of flag to be associated with the button
@param label  translated string for the button label
@param rt

@return the button widget
*/
static GtkWidget *_e2pc_create_radio_button (GtkWidget *container,
	flag_t f, gchar *label)
{
	_e2pc_set_flag (f, TRUE);
	//group leader needs to be TRUE at start, may be toggled by other members
	GtkWidget *btn = gtk_radio_button_new_with_mnemonic (NULL, label);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (btn), TRUE);
	g_signal_connect (G_OBJECT (btn), "toggled", G_CALLBACK (_e2pc_toggle_cb),
		(gpointer) f);
	gtk_container_add (GTK_CONTAINER (container), btn);
	gtk_widget_show (btn);
	return (btn);
}
/**
@brief create and show a radio btn in a specified container

@param container the widget into which the button is to be placed
@param group the radio button widget that 'leads' the group
@param f enumerated value of flag to be associated with the button
@param state T/F initial state of the toggle
@param label  translated string for the button label
@param rt

@return the button widget
*/
static GtkWidget *_e2pc_create_radio_grouped_button (GtkWidget *container, GtkWidget *group,
	flag_t f, gboolean state, gchar *label)
{
  GSList *list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (group));
  GtkWidget *btn = gtk_radio_button_new_with_mnemonic (list, label);
  g_signal_connect (G_OBJECT (btn), "toggled", G_CALLBACK (_e2pc_toggle_cb),
		(gpointer) f);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (btn), state);
  gtk_container_add (GTK_CONTAINER (container), btn);
  gtk_widget_show (btn);
  return (btn);
}
/**
@brief check whether a tree option is to be imported

@param setname internal name of set read from config file
@return TRUE if this set is one we want
*/
static gboolean _e2pc_match_tree (gchar *setname)
{
	guint i;
	gchar **iterator;
	for (i = 0, iterator = (gchar **)treeset_names->pdata;
			i < treeset_names->len;
			i++, iterator++)
	{
		if (g_str_equal (*iterator, setname))
		{
			g_ptr_array_remove_index_fast (treeset_names, i);
			return TRUE;
		}
	}
	return FALSE;
}
/**
@brief apply requested config data
No backup of existing data, so mid-read failure might be ornery!
@param contents ptr to config file after loading into memory
@param flags bitflags indicating which options to import
@return
*/
static void _e2pc_filter_options (gchar *contents, bitflag_t flags)
{
	gint i = -1;	//array index
	gchar *line; //pointer to the current line
	gchar **lsplit;
	//everything goes into an array
	//as for e2_option_read_array(), contents are assumed to be parsable as ascii
	gchar **split = g_strsplit (contents, "\n", -1);

	while ((line = split[++i]) != NULL)
	{
		g_strchomp (line);
		//ignore empty lines and comments
		if (*line == '\0' || line[0] == '#')
			continue;

		lsplit = g_strsplit (line, "=", 2);
		if (lsplit[1] != NULL)
		{
			if (!g_str_equal (lsplit[1], "<")) //not a tree set
			{
				if (flags & (E2PC_NONTREE | E2PC_ALL))
				{

					if (e2_option_set_from_string (lsplit[0], lsplit[1]))
						rebuild_needed = TRUE;
					else
						printd (WARN, "could not set option '%s'", lsplit[0]);
				}
			}
			else //a tree set
			{
				if ((flags & (E2PC_ALLTREE | E2PC_ALL)) || _e2pc_match_tree (lsplit[0]))
				{
					E2_OptionSet *set = e2_option_tree_get (lsplit[0]);
					if (set != NULL)
					{
						e2_option_tree_backup (set);
						gtk_tree_store_clear (GTK_TREE_STORE (set->ex.tree.model));
						if (e2_option_tree_set_from_array (lsplit[0], split, &i, NULL))
						{
							rebuild_needed = TRUE;
							e2_option_tree_unbackup (set, FALSE);
						}
						else
						{
							//reinstate backup
							e2_option_tree_unbackup (set, TRUE);
							set = NULL;	//trigger message
						}
					}
					if (set == NULL)
					{
						gchar *msg = g_strdup_printf (
						_("Bad configuration data for %s, not installed"), lsplit[0]);
						e2_output_print_error (msg, TRUE);
					}
				}
				else	//skip the set
					while ((line = split[++i]) != NULL)
				{
					g_strchomp (line);
					if (line[0] == '>')
						break;
				}
			}
		}
		g_strfreev (lsplit);
	}
	g_strfreev (split);
}
  /*********************/
 /***** callbacks *****/
/*********************/

/**
@brief get config data and apply them

@param button UNUSED the clicked widget
@param rt dialog runtime struct

@return
*/
static void _e2pc_import_cb (GtkWidget *button, E2P_ConfigData *rt)
{
	//convert import flags to bitflags that we can play with
	bitflag_t import_flags = 0;
	gint i;
	for (i = 0; i < MAX_FLAGS; i++)
	{
		if (flags[i])
			import_flags |= 1 << i;
	}
	import_flags &= ~E2PC_CUSTOM;	//this one is just an indicator for specific tree(s)
	if (!import_flags)
		return;	//nothing chosen
	//don't want to separately test the "all" flags
	if (import_flags & E2PC_ALL)
		import_flags |= E2PC_NONTREE;
	if (import_flags & (E2PC_ALL | E2PC_ALLTREE))
		import_flags |= E2PC_ALLTREEMASK;
	//setup tree-option private (untranslated) names, for comparison when parsing
	treeset_names = g_ptr_array_sized_new (MAX_FLAGS);	//plenty of space, even with trailing NULL
	//FIXME make this dynamic
	if (import_flags & E2PC_MARKS)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[MARKS_P - FIRSTTREE_P]);
	if (import_flags & E2PC_FILETYPES)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[FILETYPES_P - FIRSTTREE_P]);
	if (import_flags & E2PC_BINDINGS)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[BINDINGS_P - FIRSTTREE_P]);
	if (import_flags & E2PC_ALIASES)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[ALIASES_P - FIRSTTREE_P]);
	if (import_flags & E2PC_PLUGINS)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[PLUGINS_P - FIRSTTREE_P]);
	if (import_flags & E2PC_MENU)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[MENU_P - FIRSTTREE_P]);
	if (import_flags & E2PC_CUSTMENU)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[CUSTMENU_P - FIRSTTREE_P]);
	if (import_flags & E2PC_PANEBAR1)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[PANEBAR1_P - FIRSTTREE_P]);
	if (import_flags & E2PC_PANEBAR2)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[PANEBAR2_P - FIRSTTREE_P]);
	if (import_flags & E2PC_TASKBAR)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[TASKBAR_P - FIRSTTREE_P]);
	if (import_flags & E2PC_CMDBAR)
		g_ptr_array_add (treeset_names, (gpointer) set_private_names[CMDBAR_P - FIRSTTREE_P]);

	gboolean upgrade = FALSE;	//TRUE when importing an old config file that probably needs upgrade
	const gchar *filepath = gtk_entry_get_text (GTK_ENTRY (rt->open_entry));
	gchar *realpath = (*filepath != '\0') ? (gchar *) filepath :
		g_build_filename (e2_cl_options.config_dir, default_config_file, NULL);
	gchar *localpath = F_FILENAME_TO_LOCALE (realpath);
#ifdef E2_VFS
	VPATH ddata = { localpath, NULL };	//only allow local config data
#endif
	gpointer contents;
	//get file
read_file:
#ifdef E2_VFS
	if (e2_fs_get_file_contents (&ddata, &contents, NULL, TRUE E2_ERR_NONE()))
#else
	if (e2_fs_get_file_contents (localpath, &contents, NULL, TRUE E2_ERR_NONE()))
#endif
	{
		//check for conforming version, hence format for tree options
		gchar *sp, *sq, *st;
		sp = strchr ((gchar *)contents, '\n');
		if (sp != NULL)
		{
			*sp = '\0';
			sq = strstr ((gchar *)contents, "(v");
			if (sq != NULL)
			{
				st = g_strrstr (sq, ")");
				if (st != NULL)
				{
					*st = '\0';
					sq = g_strdup (sq + 2);
					g_strstrip (sq);
					upgrade = (strcmp (sq, VERSION RELEASE) < 0);
					if (upgrade)
					{
						gchar *command;
						gchar *sed = g_find_program_in_path ("sed");
						if (sed != NULL)
						{
							if (strcmp (sq,"0.1.6.3") < 0 && (import_flags & (E2PC_ALLTREEMASK)))
							{
								//re-configure all tree-option name lines
								//this format change is no longer supported in the upgrade plugin
								command = g_strconcat (
								"cp -f ", realpath, " ", realpath, ".save;",
								sed, " -e 's/^<\\(.*\\)/\\1=</'",
								 " -e 's/^\\(\t*\\)[\\]</\\1</' ",
								realpath, ".save >", realpath, NULL);
								system (command);
								g_free (command);
								g_free (sed);
								g_free (sq);
								if (realpath != filepath)
									g_free (realpath);
								g_free (contents);	//need free() if file buffer allocated by malloc()
								goto read_file;
							}
						}
						else
						{
							gchar *msg = g_strdup_printf (_("Incompatible format - %s"), realpath);
							e2_output_print_error (msg, TRUE);
							g_free (sq);
							if (realpath != filepath)
								g_free (realpath);
							g_free (contents);	//need free() if file buffer allocated by malloc()
							return;
						}
					}
					*st = ')';
				}
			}
			*sp = '\n';
		}
		else
			sq = NULL;

		rebuild_needed = FALSE;
		//process the file
		_e2pc_filter_options ((gchar *)contents, import_flags);
		g_free (contents);	//need free() if file buffer allocated by malloc()
		if (rebuild_needed)
		{
			if (upgrade)
			{
				//backup current config file in case the upgrade is not so good
				gchar *path1 = g_build_filename (e2_cl_options.config_dir,
					default_config_file, NULL);
				gchar *local1 = F_FILENAME_TO_LOCALE (path1);
				gchar *path2 = g_strconcat (default_config_file, "-before-import", NULL);
				gchar *path3 = g_build_filename (e2_cl_options.config_dir, path2, NULL);
				gchar *local2 = F_FILENAME_TO_LOCALE (path3);
				gchar *savepath = e2_utils_get_tempname (local2);
				gdk_threads_leave ();	//downstream errors invoke local mutex locking
#ifdef E2_VFS
				VPATH ddata = { local1, NULL };	//local config files only
				VPATH tdata = { savepath, NULL };	//local config files only
				e2_task_backend_rename (&ddata, &tdata);
#else
				e2_task_backend_rename (local1, savepath);
#endif
				gdk_threads_enter ();
				g_free (path1);
				g_free (path2);
				g_free (path3);
				g_free (savepath);
				F_FREE (local1);
				F_FREE (local2);
				//save updated config file
				e2_option_file_write (NULL);
				//do any upgrades needed
				Plugin *p = e2_plugins_open1 (PLUGINS_DIR G_DIR_SEPARATOR_S UPGRADE_PNAME);	//localised path
				if (p != NULL)
				{
					//fake the config version for the plugin
					//NOTE this needs to be the minimum actionable version in the
					//upgrade plugin or else that will just install defaults
					gchar *sv;
					if (sq != NULL)
						sv = (strcmp (sq, OLDEST_UPGRADE) < 0) ? OLDEST_UPGRADE: sq;
					else
						sv = OLDEST_UPGRADE;
					g_strlcpy (app.cfgfile_version, sv, sizeof (app.cfgfile_version));
					//app.cfgfile_version is reverted in plugin
					if (!p->plugin_init (p))	//do any upgrades
					{
						printd (ERROR, "Can't initialize upgrade plugin: "UPGRADE_PNAME);
						g_strlcpy (app.cfgfile_version, VERSION RELEASE, sizeof (app.cfgfile_version));
					}
					e2_plugins_unload1 (p, FALSE);
				}
				else
					printd (ERROR, "Can't find upgrade plugin "UPGRADE_PNAME", so can't upgrade the imported config data");

				if (sq != NULL)
					g_free (sq);
			}

			//recreate window and runtime data structs as appropriate
			//this is like e2_option_refresh (FALSE, TRUE) but omits some fatal
			//parts of that
			app.rebuild_enabled = FALSE;	//block recursion

			if (import_flags & E2PC_BINDINGS)
				e2_keybinding_clean ();
			if (import_flags & E2PC_FILETYPES)
				g_hash_table_destroy (app.filetypes);

			if (import_flags & E2PC_PLUGINS)
			{
				//this is like e2_plugins_unload_all() but must not unload this plugin
				if (app.plugins != NULL)
				{
					GList *member;
					for (member = app.plugins; member != NULL; member = member->next)
					{
						if (member->data != NULL)
						{

							if (! g_str_equal(((Plugin *)member->data)->signature, ANAME VERSION))
							//FIXME handle plugins that are not allowed to be unloaded
							{
								e2_plugins_unload1 ((Plugin *)member->data, TRUE);
								member->data = NULL;
							}
						}
					}
					app.plugins = g_list_remove_all (app.plugins, NULL);
				}
			}
			//clear relevant current information
//			e2_option_clear_data ();
			//now re-create things, in the same order as at session-start
		//	e2_option_default_register ();

//			if (reload)
//			{
//				printd (INFO, "reloading config due to external change");
//				e2_option_file_read ();
//			}

		//	e2_option_tree_install_defaults ();

			if (import_flags & E2PC_PLUGINS)
				e2_plugins_load_all (); // load plugins (if any) CHECKME what if this plugin is reloaded ?

			//re-initialise things that are not done in normal 'recreate' functions
			e2_pane_create_option_data (&app.pane1);
			e2_pane_create_option_data (&app.pane2);

			e2_toolbar_initialise (E2_BAR_PANE1);
			e2_toolbar_initialise (E2_BAR_PANE2);
			e2_toolbar_initialise (E2_BAR_TASK);
			e2_toolbar_initialise (E2_BAR_COMMAND);

		//	if (recreate)
				e2_window_recreate (&app.window); //this also recreates key bindings
		//	else
		//		e2_keybinding_register_all ();

			if (import_flags & E2PC_FILETYPES)
				e2_filetype_add_all ();

			app.rebuild_enabled = TRUE;	//unblock
		}
	}
	else
	{
		gchar *msg = g_strdup_printf (_("Error reading file %s"), realpath);
		e2_output_print_error (msg, TRUE);
	}
	if (*filepath == '\0')
		g_free (realpath);
	F_FREE (localpath);
	g_ptr_array_free (treeset_names, TRUE);
}
/**
@brief get name of config file to open, via a gtkfilechooser

@param button UNUSED the clicked widget
@param rt dialog runtime struct

@return
*/
static void _e2pc_select_config_cb (GtkWidget *button, E2P_ConfigData *rt)
{
	GtkWidget *dialog = gtk_file_chooser_dialog_new (NULL,
		GTK_WINDOW (rt->dialog), GTK_FILE_CHOOSER_ACTION_OPEN,
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
		GTK_STOCK_OK, GTK_RESPONSE_OK,
		NULL);

	e2_dialog_setup_chooser (dialog,
		_("select configuration data file"),
		gtk_entry_get_text (GTK_ENTRY (rt->open_entry)),
		GTK_FILE_CHOOSER_ACTION_OPEN,
		TRUE,	//show hidden
		FALSE,	//single-selection
		GTK_RESPONSE_OK);	//default response

	gint response;
	while ((response = gtk_dialog_run (GTK_DIALOG (dialog))) == E2_RESPONSE_USER1)
	{}

	if (response == GTK_RESPONSE_OK)
	{
		gchar *local = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
		gchar *openpath = F_FILENAME_FROM_LOCALE (local);
		gtk_entry_set_text (GTK_ENTRY (rt->open_entry), openpath);
		g_free (local);
		F_FREE (openpath);
	}
	gtk_widget_destroy (dialog);
}
/**
@brief save config file with specified name

@param button UNUSED the clicked widget
@param rt dialog runtime struct

@return
*/
static void _e2pc_save_cb (GtkWidget *button, E2P_ConfigData *rt)
{
	const gchar *savepath = gtk_entry_get_text (GTK_ENTRY (rt->save_entry));
	if (*savepath != '\0')
	{
		if (e2_option_bool_get ("confirm-overwrite"))
		{
			gchar *dlocal = D_FILENAME_TO_LOCALE (savepath);
			g_strstrip (dlocal);
#ifdef E2_VFS
			VPATH ddata = { dlocal, NULL };
			if (e2_fs_access2 (&ddata E2_ERR_NONE()) == 0)
#else
			if (e2_fs_access2 (dlocal E2_ERR_NONE()) == 0)
#endif
			{
				DialogButtons choice = e2_dialog_ow_check (NULL, dlocal, NONE);
				if (choice != OK)
				{
					g_free (dlocal);
					return;
				}
			}
			g_free (dlocal);
		}
		e2_option_file_write (savepath);
	}
}
/**
@brief save config file with new name

@param button UNUSED the clicked widget
@param rt dialog runtime struct

@return
*/
static void _e2pc_saveas_cb (GtkWidget *button, E2P_ConfigData *rt)
{
	GtkWidget *dialog = gtk_file_chooser_dialog_new (NULL,
		GTK_WINDOW (rt->dialog), GTK_FILE_CHOOSER_ACTION_SAVE,
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
		GTK_STOCK_OK, GTK_RESPONSE_OK,
		NULL);

	e2_dialog_setup_chooser (dialog,
		_("save configuration data file"),
		gtk_entry_get_text (GTK_ENTRY (rt->save_entry)),
		GTK_FILE_CHOOSER_ACTION_SAVE,
		FALSE,	//hide hidden
		FALSE,	//single selection
		GTK_RESPONSE_OK);	//default response

	gint response;
	while ((response = gtk_dialog_run (GTK_DIALOG (dialog))) == E2_RESPONSE_USER1)
	{}

	if (response == GTK_RESPONSE_OK)
	{
		gchar *local = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
#ifndef USE_GTK2_8
		//check for O/W if not done by the dialog itself
#ifdef E2_VFS
		VPATH ddata = { local, NULL };
#endif
		if (e2_option_bool_get ("confirm-overwrite")
#ifdef E2_VFS
		&& e2_fs_access2 (&ddata E2_ERR_NONE()) == 0)
#else
		&& e2_fs_access2 (local E2_ERR_NONE()) == 0)
#endif
		{
			DialogButtons choice = e2_dialog_ow_check (NULL, local, NONE);
			if (choice != OK)
			{
				gtk_widget_destroy (dialog);
				g_free (local);
				return;
			}
		}
#endif
		gchar *savepath = F_FILENAME_FROM_LOCALE (local);
		gtk_entry_set_text (GTK_ENTRY (rt->save_entry), savepath);
		g_free (local);
		F_FREE (savepath);
	}
	gtk_widget_destroy (dialog);
}
/**
@brief select directory containing custom icons to be used

@param widget clicked button, UNUSED
@param rt dialog runtime struct

@return
*/
static void _e2pc_select_icondir_cb (GtkWidget *widget, E2P_ConfigData *rt)
{
	GtkWidget *dialog = gtk_file_chooser_dialog_new (NULL,
		GTK_WINDOW (rt->dialog), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
		GTK_STOCK_OPEN, GTK_RESPONSE_OK,
		NULL);

	e2_dialog_setup_chooser (dialog,
		_("select icons directory"),
		gtk_entry_get_text (GTK_ENTRY (rt->icondir_entry)),
		GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
		TRUE,	//show hidden
		FALSE,	//single-selection
		GTK_RESPONSE_OK);	//default response

	gint response;
	while ((response = gtk_dialog_run (GTK_DIALOG (dialog))) == E2_RESPONSE_USER1)
	{}

	if (response == GTK_RESPONSE_OK)
	{
		gchar *local = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
//		if (strlen (local) > 0)
//		{
			gchar *opendir = F_FILENAME_FROM_LOCALE (local);
			gtk_entry_set_text (GTK_ENTRY (rt->icondir_entry), opendir);
			F_FREE (opendir);
//		}
		g_free (local);
	}
	gtk_widget_destroy (dialog);
}
/**
@brief apply icon directory

@param widget clicked button, UNUSED
@param rt dialog runtime struct

@return
*/
static void _e2pc_apply_icondir_cb (GtkWidget *widget, E2P_ConfigData *rt)
{
	gchar *utfpath = g_strdup (gtk_entry_get_text (GTK_ENTRY (rt->icondir_entry)));
	if (g_str_has_suffix (utfpath, G_DIR_SEPARATOR_S))
		*(utfpath + strlen (utfpath) - sizeof(gchar)) = '\0';

	gchar *localpath = F_FILENAME_TO_LOCALE (utfpath);
	if (g_str_equal (localpath, ICON_DIR))
		e2_option_bool_set ("use-icon-dir", FALSE);
	else
	{
		e2_option_bool_set ("use-icon-dir", TRUE);
		E2_OptionSet *set = e2_option_get ("icon-dir");
		e2_option_str_set_direct (set, utfpath);
		e2_toolbar_recreate_all ();
	}
	F_FREE (localpath);
	g_free (utfpath);
}
/**
@brief select directory in which to save custom icons now in use

@param widget clicked button, UNUSED
@param rt dialog runtime struct

@return
*/
static void _e2pc_select_iconsavedir_cb (GtkWidget *widget, E2P_ConfigData *rt)
{
	GtkWidget *dialog = gtk_file_chooser_dialog_new (NULL,
		GTK_WINDOW (rt->dialog), GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
		GTK_STOCK_OK, GTK_RESPONSE_OK,
		NULL);

	e2_dialog_setup_chooser (dialog,
		_("select icons directory"),
		gtk_entry_get_text (GTK_ENTRY (rt->iconsavedir_entry)),
		GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
		FALSE,	//hide-hidden
		FALSE,	//single-selection
		GTK_RESPONSE_OK);	//default response

	gint response;
	while ((response = gtk_dialog_run (GTK_DIALOG (dialog))) == E2_RESPONSE_USER1)
	{}

	if (response == GTK_RESPONSE_OK)
	{
		gchar *local = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
#ifndef USE_GTK2_8
		//check for O/W if not done by the dialog itself
#ifdef E2_VFS
		VPATH ddata = { local, NULL };
#endif
		if (e2_option_bool_get ("confirm-overwrite")
#ifdef E2_VFS
			&& e2_fs_access2 (&ddata E2_ERR_NONE()) == 0)
#else
			&& e2_fs_access2 (local E2_ERR_NONE()) == 0)
#endif
		{
			DialogButtons choice = e2_dialog_ow_check (NULL, local, NONE);
			if (choice != OK)
			{
				gtk_widget_destroy (dialog);
				g_free (local);
				return;
			}
		}
#endif
		gchar *openpath = F_FILENAME_FROM_LOCALE (local);
		gtk_entry_set_text (GTK_ENTRY (rt->iconsavedir_entry), openpath);
		g_free (local);
		F_FREE (openpath);
	}
	gtk_widget_destroy (dialog);
}
/**
@brief copy icons to specified directory
If the dir is not absolute, curr_view->dir is prepended
@param widget clicked button, UNUSED
@param rt dialog runtime struct

@return
*/
static void _e2pc_apply_iconsavedir_cb (GtkWidget *widget, E2P_ConfigData *rt)
{
	const gchar *path;
	gchar *slocal, *dest, *dlocal;

	//can't have trailing / for copy func
	slocal = e2_utils_get_icons_path (FALSE);

	path = gtk_entry_get_text (GTK_ENTRY (rt->iconsavedir_entry));
	if (!g_path_is_absolute (path))
//E2_VFSTMPOK
		dest = e2_utils_dircat (curr_view, path, FALSE);
	else
		dest = g_strdup (path);
	if (g_str_has_suffix (dest, G_DIR_SEPARATOR_S))
		*(dest + strlen (dest) - sizeof (gchar)) = '\0';
	dlocal = F_FILENAME_TO_LOCALE (dest);

	DialogButtons result;
#ifdef E2_VFS
	VPATH ddata = { dlocal, NULL };
#endif
	if (e2_option_bool_get ("confirm-overwrite")
#ifdef E2_VFS
		&& e2_fs_access2 (&ddata E2_ERR_NONE()) == 0)
#else
		&& e2_fs_access2 (dlocal E2_ERR_NONE()) == 0)
#endif
		result = e2_dialog_ow_check (slocal, dlocal, NONE);
	else
		result = OK;

	if (result == OK)
	{
#ifdef E2_VFS
		VPATH sdata = { slocal, NULL };
#endif
		gdk_threads_leave ();	//downstream errors invoke local mutex locking
#ifdef E2_VFS
		e2_task_backend_copy (&sdata, &ddata, E2_FTM_NORMAL);
#else
		e2_task_backend_copy (slocal, dlocal, E2_FTM_NORMAL);
#endif
		gdk_threads_enter ();
	}

	g_free (slocal);
	g_free (dest);
	F_FREE (dlocal);
}
/**
@brief cleanup after cancel button is pressed

@param widget clicked button, UNUSED
@param rt dialog runtime struct

@return
*/
static void _e2pc_quit_cb (GtkWidget *widget, E2P_ConfigData *rt)
{
	printd (DEBUG, "Config plugin dialog quit cb");
	gtk_widget_destroy (rt->dialog);
	DEALLOCATE (E2P_ConfigData, rt);
//	gtk_widget_grab_focus (curr_view->treeview);
}

/**
@brief  create and show notbook page with config export widgets

@param dialog the dialog where the response was triggered
@param response the number assigned to the widget that triggered the callback
@param rt dialog runtime struct

@return
*/
static void	_e2pc_response_cb (GtkDialog *dialog, gint response, E2P_ConfigData *rt)
{
	printd (DEBUG, "Config plugin dialog response cb");
	switch (response)
	{
		case GTK_RESPONSE_CLOSE:
			_e2pc_quit_cb (NULL, rt);
			break;
		default:
			break;
	}
}

  /***************************/
 /***** widget creation *****/
/***************************/

/**
@brief  create and show notebook page with config export widgets

@param notebook the notebook widget
@param rt ptr to dialog data struct

@return
*/
static void	_e2pc_make_export_tab (GtkWidget *notebook, E2P_ConfigData *rt)
{
	GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
	gtk_widget_show (vbox);
#ifdef E2_ASSISTED
	GtkWidget *label =
#endif
	e2_widget_add_label (vbox, _("Save configuration data in"),
		0.5, 0.5, FALSE, E2_PADDING);

	const gchar *savepath;
	gchar *local = F_FILENAME_TO_LOCALE (e2_cl_options.config_dir);
	//default export to config dir if it's usable
#ifdef E2_VFS
	VPATH ddata = { local, NULL };
	if (e2_fs_is_dir3 (&ddata E2_ERR_NONE()) && !e2_fs_access (&ddata, R_OK | W_OK | X_OK E2_ERR_NONE()))
#else
	if (e2_fs_is_dir3 (local E2_ERR_NONE()) && !e2_fs_access (local, R_OK | W_OK | X_OK E2_ERR_NONE()))
#endif
		savepath = e2_cl_options.config_dir;
	else
		savepath = g_get_home_dir ();
	F_FREE (local);

	gchar *savename = g_build_filename (savepath, default_config_file, NULL);
	local = F_FILENAME_TO_LOCALE (savename);
	gchar *temppath, *tempext, *local2;
	guint i = 0;
	while (TRUE)
	{
		tempext = g_strdup_printf (".%s~%d", _("backup"), i);
		local2 = F_FILENAME_TO_LOCALE (tempext);
		temppath = e2_utils_strcat (local, local2);
		g_free (tempext);
		F_FREE (local2);
		E2_ERR_DECLARE
#ifdef E2_VFS
		ddata.localpath = temppath;
		if (e2_fs_access2 (&ddata E2_ERR_PTR())	//checkme need for vfs ?
#else
		if (e2_fs_access2 (temppath E2_ERR_PTR())	//checkme need for vfs ?
#endif
				&& E2_ERR_IS (ENOENT))
		{
			E2_ERR_CLEAR
			break;
		}
		E2_ERR_CLEAR
		g_free (temppath);
		i++;
	}
	F_FREE (local);
	g_free (savename);
	savepath = F_FILENAME_FROM_LOCALE (temppath);
	if (savepath != temppath)
		g_free (temppath);

	rt->save_entry = e2_widget_add_entry (vbox, (gchar *) savepath, TRUE, TRUE);
#ifdef E2_ASSISTED
	e2_widget_set_label_relations (GTK_LABEL (label), rt->save_entry);
#endif
	gtk_widget_set_size_request (rt->save_entry, 400, -1);

	GtkWidget *hbox = gtk_hbutton_box_new ();
	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, E2_PADDING);
	gtk_widget_show (hbox);

	gtk_box_set_spacing (GTK_BOX (hbox), E2_PADDING_LARGE);
	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
	GtkWidget *btn = e2_button_get (_("Se_lect"), GTK_STOCK_SAVE_AS,
		_("Select the file in which to store the config data"), _e2pc_saveas_cb, rt);
	gtk_container_add (GTK_CONTAINER (hbox), btn);
	btn = e2_button_get (_("_Save"), GTK_STOCK_SAVE,
		_("Save current config data in the specified file"), _e2pc_save_cb, rt);
	gtk_container_add (GTK_CONTAINER (hbox), btn);

#ifndef E2_ASSISTED
	GtkWidget *
#endif
	label = gtk_label_new (_("export"));
	gtk_widget_show (label);
	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
}
/**
@brief  create and show notebook page with config import widgets

@param notebook the notebook widget
@param rt ptr to dialog data struct

@return
*/
static void	_e2pc_make_import_tab (GtkWidget *notebook, E2P_ConfigData *rt)
{
	GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
	gtk_widget_show (vbox);
#ifdef E2_ASSISTED
	GtkWidget *label =
#endif
	e2_widget_add_label (vbox, _("Get configuration data from"),
		0.5, 0.5, FALSE, E2_PADDING);

	const gchar *openpath;
	gchar *local = F_FILENAME_TO_LOCALE (e2_cl_options.config_dir);
	//default export to config dir if it's usable
#ifdef E2_VFS
	VPATH ddata = { local, NULL };
	if (e2_fs_is_dir3 (&ddata E2_ERR_NONE()) && !e2_fs_access (&ddata, R_OK | X_OK E2_ERR_NONE()))
#else
	if (e2_fs_is_dir3 (local E2_ERR_NONE()) && !e2_fs_access (local, R_OK | X_OK E2_ERR_NONE()))
#endif
		openpath = e2_cl_options.config_dir;
	else
		openpath = g_get_home_dir ();
	F_FREE (local);

	gchar *openname = g_build_filename (openpath, default_config_file, NULL);

	rt->open_entry = e2_widget_add_entry (vbox, (gchar *) openname, TRUE, TRUE);
#ifdef E2_ASSISTED
	e2_widget_set_label_relations (GTK_LABEL (label), rt->open_entry);
#endif
	gtk_widget_set_size_request (rt->open_entry, 400, -1);

	GtkWidget *hbox = gtk_hbutton_box_new ();
	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, E2_PADDING);
	gtk_widget_show (hbox);

	gtk_box_set_spacing (GTK_BOX (hbox), E2_PADDING_LARGE);
	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
	GtkWidget *btn = e2_button_get (_("Se_lect"), GTK_STOCK_OPEN,
		_("Select the config file from which to get the data"),
		_e2pc_select_config_cb, rt);
	gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0);
	btn = e2_button_get (_("_Apply"), GTK_STOCK_APPLY,
		_("Import config data in accord with choices below"), _e2pc_import_cb, rt);
	gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0);

	e2_widget_add_separator (vbox, FALSE, E2_PADDING_SMALL);

	//now the import options
	hbox = e2_widget_add_box (vbox, TRUE, E2_PADDING_SMALL,
		FALSE, TRUE, E2_PADDING);
	GtkWidget *leader =
	_e2pc_create_radio_button (hbox, ALL_P, _("_all options"));
	_e2pc_create_radio_grouped_button (hbox, leader, NONTREE_P,
		FALSE, _("all '_non-group' options"));
	hbox = e2_widget_add_box (vbox, TRUE, E2_PADDING_SMALL,
		FALSE, TRUE, E2_PADDING);
	_e2pc_create_radio_grouped_button (hbox, leader, ALLTREE_P,
		FALSE, _("all 'g_roup' options"));
	_e2pc_create_radio_grouped_button (hbox, leader, CUSTOM_P,
		FALSE, _("_specific group option(s)"));
	rt->expander = gtk_expander_new_with_mnemonic (_("_groups"));
	gtk_box_pack_start (GTK_BOX (vbox), rt->expander, FALSE, FALSE, 0);
	gtk_widget_show (rt->expander);
	GtkWidget *expvbox = gtk_vbox_new (FALSE, 0);
	gtk_container_add (GTK_CONTAINER (rt->expander), expvbox);
	gtk_widget_show (expvbox);
	hbox = e2_widget_add_box (expvbox, TRUE, E2_PADDING_SMALL,
		FALSE, TRUE, E2_PADDING);
	gchar *labeltxt = _e2pc_get_setlabel (PANEBAR1_P);
	_e2pc_create_check_button (hbox, PANEBAR1_P, FALSE, labeltxt);
	g_free (labeltxt);
	labeltxt = _e2pc_get_setlabel (PANEBAR2_P);
	_e2pc_create_check_button (hbox, PANEBAR2_P, FALSE, labeltxt);
	g_free (labeltxt);
	hbox = e2_widget_add_box (expvbox, TRUE, E2_PADDING_SMALL,
		FALSE, TRUE, E2_PADDING);
	labeltxt = _e2pc_get_setlabel (TASKBAR_P);
	_e2pc_create_check_button (hbox, TASKBAR_P, FALSE, labeltxt);
	g_free (labeltxt);
	labeltxt = _e2pc_get_setlabel (CMDBAR_P);
	_e2pc_create_check_button (hbox, CMDBAR_P, FALSE, labeltxt);
	g_free (labeltxt);
	hbox = e2_widget_add_box (expvbox, TRUE, E2_PADDING_SMALL,
		FALSE, TRUE, E2_PADDING);
	labeltxt =  _e2pc_get_setlabel (MARKS_P);
	_e2pc_create_check_button (hbox, MARKS_P, FALSE, labeltxt);
	g_free (labeltxt);
	labeltxt = _e2pc_get_setlabel (FILETYPES_P);
	_e2pc_create_check_button (hbox, FILETYPES_P, FALSE, labeltxt);
	g_free (labeltxt);
	hbox = e2_widget_add_box (expvbox, TRUE, E2_PADDING_SMALL,
		FALSE, TRUE, E2_PADDING);
	labeltxt = _e2pc_get_setlabel (BINDINGS_P);
	_e2pc_create_check_button (hbox, BINDINGS_P, FALSE, labeltxt);
	g_free (labeltxt);
	labeltxt = _e2pc_get_setlabel (ALIASES_P);
	_e2pc_create_check_button (hbox, ALIASES_P, FALSE, labeltxt);
	g_free (labeltxt);
	hbox = e2_widget_add_box (expvbox, TRUE, E2_PADDING_SMALL,
		FALSE, TRUE, E2_PADDING);
	labeltxt = _e2pc_get_setlabel (MENU_P);
	_e2pc_create_check_button (hbox, MENU_P, FALSE, labeltxt);
	g_free (labeltxt);
	labeltxt = _e2pc_get_setlabel (PLUGINS_P);
	_e2pc_create_check_button (hbox, PLUGINS_P, FALSE, labeltxt);
	g_free (labeltxt);
	hbox = e2_widget_add_box (expvbox, TRUE, E2_PADDING_SMALL,
		FALSE, TRUE, E2_PADDING);
	labeltxt = _e2pc_get_setlabel (CUSTMENU_P);
	_e2pc_create_check_button (hbox, CUSTMENU_P, FALSE, labeltxt);
	g_free (labeltxt);

#ifndef E2_ASSISTED
	GtkWidget *
#endif
	label = gtk_label_new (_("import"));
	gtk_widget_show (label);
	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
}
/**
@brief  create and show notebook page with config import widgets

@param notebook the notebook widget
@param rt ptr to dialog data struct

@return
*/
static void	_e2pc_make_icons_tab (GtkWidget *notebook, E2P_ConfigData *rt)
{
	GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
	gtk_widget_show (vbox);
#ifdef E2_ASSISTED
	GtkWidget *label =
#endif
	e2_widget_add_label (vbox, _("Use icons in"),
		0.5, 0.5, FALSE, E2_PADDING);

	gchar *openpath;
	gchar *localpath = e2_utils_get_icons_path (FALSE);
	//default icons in config dir if that's usable
#ifdef E2_VFS
	VPATH ddata = { localpath, NULL };
	if (!e2_fs_is_dir3 (&ddata E2_ERR_NONE()) || e2_fs_access (&ddata, R_OK | X_OK E2_ERR_NONE()))
#else
	if (!e2_fs_is_dir3 (localpath E2_ERR_NONE()) || e2_fs_access (localpath, R_OK | X_OK E2_ERR_NONE()))
#endif
		openpath = g_build_filename (e2_cl_options.config_dir, _("icons"), NULL);
	else
		openpath = D_FILENAME_FROM_LOCALE (localpath);
	g_free (localpath);

	rt->icondir_entry = e2_widget_add_entry (vbox, openpath, TRUE, TRUE);
#ifdef E2_ASSISTED
	e2_widget_set_label_relations (GTK_LABEL (label), rt->icondir_entry);
#endif
	g_free (openpath);
	gtk_widget_set_size_request (rt->icondir_entry, 400, -1);

	GtkWidget *hbox = gtk_hbutton_box_new ();
	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, E2_PADDING);
	gtk_widget_show (hbox);

	gtk_box_set_spacing (GTK_BOX (hbox), E2_PADDING_LARGE);
	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
	GtkWidget *btn = e2_button_get (_("Se_lect"), GTK_STOCK_OPEN,
		_("Select the directory where the icons are"),
		_e2pc_select_icondir_cb, rt);
	gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0);
	btn = e2_button_get (_("_Apply"), GTK_STOCK_APPLY,
		_("Apply the chosen icon directory"), _e2pc_apply_icondir_cb, rt);
	gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0);

	e2_widget_add_separator (vbox, FALSE, E2_PADDING_SMALL);
	//now the exporting
#ifdef E2_ASSISTED
	label =
#endif
	e2_widget_add_label (vbox, _("Copy current icons to"),
		0.5, 0.5, FALSE, E2_PADDING);

	openpath = g_strconcat (e2_cl_options.config_dir,G_DIR_SEPARATOR_S BINNAME"-",
		_("icons"), NULL);
	rt->iconsavedir_entry = e2_widget_add_entry (vbox, openpath, TRUE, TRUE);
#ifdef E2_ASSISTED
	e2_widget_set_label_relations (GTK_LABEL (label), rt->iconsavedir_entry);
#endif
	g_free (openpath);
	gtk_widget_set_size_request (rt->iconsavedir_entry, 400, -1);

	hbox = gtk_hbutton_box_new ();
	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, E2_PADDING);
	gtk_widget_show (hbox);

	gtk_box_set_spacing (GTK_BOX (hbox), E2_PADDING_LARGE);
	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
	btn = e2_button_get (_("Se_lect"), GTK_STOCK_OPEN,
		_("Select the directory where the icons will be saved"),
		_e2pc_select_iconsavedir_cb, rt);
	gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0);
	btn = e2_button_get (_("C_opy"), GTK_STOCK_COPY,
		_("Copy the icons to the chosen directory"), _e2pc_apply_iconsavedir_cb, rt);
	gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0);

	GtkWidget *pagelabel = gtk_label_new (_("icons"));
	gtk_widget_show (pagelabel);
	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, pagelabel);
}
/**
@brief establish and show the dialog

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2p_config_dialog_create (gpointer from, E2_ActionRuntime *art)
{
	E2P_ConfigData *rt = ALLOCATE (E2P_ConfigData);
	CHECKALLOCATEDWARN (rt, return FALSE;)
	srt = rt;	//copy for some funcs that don't get rt as an argument
	//create dialog
	rt->dialog = e2_dialog_create (NULL, NULL, _("manage configuration data"),
	_e2pc_response_cb, rt);
	//populate it with widgets
	GtkWidget *vbox = GTK_DIALOG (rt->dialog)->vbox;
	GtkWidget *notebook = e2_widget_add_notebook
		(vbox, TRUE, 0, NULL, NULL);
	gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);

	_e2pc_make_export_tab (notebook, rt);	//page 0
	_e2pc_make_import_tab (notebook, rt);	//page 1
	_e2pc_make_icons_tab (notebook, rt);	//page 2
	gtk_widget_show (notebook);

	//add action buttons in the order that they will appear
	e2_dialog_add_defined_button (rt->dialog, &E2_BUTTON_CLOSE);

	e2_dialog_set_negative_response (rt->dialog, GTK_RESPONSE_CLOSE);

	e2_dialog_setup (rt->dialog, app.main_window);
	gtk_widget_show (rt->dialog);
	gtk_main ();
	return TRUE;
}

//aname must be confined to this module
static gchar *aname;
/**
@brief plugin initialization function, called by main program

@param p ptr to plugin data struct

@return TRUE if the initialization succeeds, else FALSE
*/
gboolean init_plugin (Plugin *p)
{
  aname = _("manage");
  p->signature = ANAME VERSION;
  p->menu_name = _("_Configure..");
  p->description = _("Export or import configuration data");
  p->icon = "plugin_config_"E2IP".png";

  if (p->action == NULL)
  {
	//no need to free this
	gchar *action_name = g_strconcat (_A(2),".",aname,NULL);
	p->action = e2_plugins_action_register
	  (action_name, E2_ACTION_TYPE_ITEM, _e2p_config_dialog_create, NULL, FALSE, 0, NULL);
	return TRUE;
  }
  return FALSE;
}
/**
@brief cleanup transient things for this plugin

@param p pointer to data struct for the plugin

@return TRUE if all cleanups were completed
*/
gboolean clean_plugin (Plugin *p)
{
  gchar *action_name = g_strconcat (_A(2),".",aname,NULL);
  gboolean ret = e2_plugins_action_unregister (action_name);
  g_free (action_name);
  return ret;
}
