/* $Id: e2_command_line.c 2101 2010-05-29 07:32:39Z tpgww $

Copyright (C) 2004-2010 tooar <tooar@emelfm2.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, see http://www.gnu.org/licenses.
*/

/**
@file src/utils/e2_command_line.c
@brief command line functions

This file contains functions for e2's command- and dir-lines.
*/

#include <string.h>
#include <pthread.h>
#include "e2_command_line.h"
#include "e2_complete.h"
#include "e2_filelist.h"
#include "e2_task.h"
#ifdef E2_MOUSECUSTOM
# include "e2_mousebinding.h"
#endif

//list of names of command and dir lines, used to ensure that each
//command- or dir-line has a unique name
static GList *line_names = NULL; //CHECKME cleaned only via _e2_command_line_destroy()?
extern GList *children;
extern const gchar *shellcmd;
extern pthread_mutex_t task_mutex;

  /*****************/
 /***** utils *****/
/*****************/

/**
@brief sync command line tree model history with cachable history glist
This is called in the process of destroying a command-line (e.g. when rebuilding
toolbars) and when session is ending.
Expects the combo model to be still available
@param rt  pointer to command line runtime data struct

@return
*/
static void _e2_command_line_sync_history (E2_CommandLineRuntime *rt)
{
#ifdef DEBUG_MESSAGES
	//the model doesn't exist when the widget is being destroyed
	if (GTK_IS_TREE_MODEL(rt->model))
	{
#endif
		//free old history
		if (rt->history != NULL)
			e2_list_free_with_data (&rt->history);
		GtkTreeIter iter;
//		printd (DEBUG, "update history list for %s", rt->name);
		//fill with new state
		if (gtk_tree_model_get_iter_first (rt->model, &iter))
		{
			gint count = 0;
			gint max = (rt->original) ?
				e2_option_int_get ("command-line-history-max"):
				e2_option_int_get ("dir-line-history-max");
			gchar *value;
			do
			{
				gtk_tree_model_get (rt->model, &iter, 0, &value, -1);
				rt->history = g_list_append (rt->history, value);
				if (++count == max)
					break;
			} while (gtk_tree_model_iter_next (rt->model, &iter));
		}
#ifdef DEBUG_MESSAGES
	}
	else
		printd (WARN, "no history update for %s, model is destroyed", rt->name);
#endif
}
/* *
@brief clear and backup runtime data for a command/dir line
This is the 'sync data' function specified when command lines are created.
It must be called before the combo widget is destroyed, as the combo's model is
needed downstream
@param rt runtime data for the commandline being processed

@return
*/
/*ONLY 1 USE, INLINED THERE
static void _e2_command_line_destroy (E2_CommandLineRuntime *rt)
{
	line_names = g_list_remove (line_names, rt->name); //each name is unique
	e2_cache_unregister (rt->name);	//also zaps rt->history
	g_free (rt->name);
	app.command_lines = g_list_remove (app.command_lines, rt);
	DEALLOCATE (E2_CommandLineRuntime, rt);
} */
/**
@brief check if strings @a arg1 and @a arg2 match

@param arg1 string data for member of glist being scanned
@param arg2 string to be found in the glist

@return 0 if strings @a arg1 and @a arg2 are the same
*/
static gint _e2_command_line_match_list_str (const gchar *arg1, const gchar *arg2)
{
	return (strcmp (arg1, arg2));
}
/**
@brief get a unique commandline name beginning with @a private
@param private intended private name of commandline

@return newly-allocated string same as @a private, or @a private with appended digit
*/
static gchar *_e2_command_line_get_name (const gchar *private)
{
	GList *tmp = g_list_find_custom (line_names, private,
		(GCompareFunc)_e2_command_line_match_list_str);
	if (tmp == NULL)
		return (g_strdup (private));
	else
	{
		gint turn = 2;
		gchar *name = NULL;	//assigned to enable initial free()
		while (tmp != NULL)
		{
			g_free (name);
			name = g_strdup_printf ("%s %d", private, turn++);
			tmp = g_list_find_custom (line_names, name,
				(GCompareFunc)_e2_command_line_match_list_str);
		}
		return name;
	}
}
/**
@brief set command-line text to last-used history value, if required, or to ""

@param rt pointer to command line runtime data struct

@return
*/
static void _e2_command_line_update_entry (E2_CommandLineRuntime *rt)
{
	GtkWidget *entry =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->combo));
#else
		GTK_BIN (rt->combo)->child;
#endif
	if (e2_option_bool_get_direct (rt->opt_history_last))
	{
		GtkTreeIter iter;
		if (gtk_tree_model_get_iter_first (rt->model, &iter))
		{
			e2_combobox_set_active (rt->combo, 0);
			gtk_editable_set_position (GTK_EDITABLE (entry), -1);
		}
	}
	else
		gtk_entry_set_text (GTK_ENTRY (entry), "");
}
/**
@brief get pointer to rt data for first-recorded command line (as opposed to dir line)

This does nothing if @a rt does not point to NULL

@param rt pointer to location to store command line runtime data struct pointer

@return
*/
static void _e2_command_line_get_first (E2_CommandLineRuntime **rt)
{
	if (*rt == NULL)
	{
//		if (app.command_lines == NULL)
//			return;	//no command lines registered
//		else
//		{
			//find the first-registered command-line
			E2_CommandLineRuntime *cl_rt;
			GList *member;
			for (member = app.command_lines; member != NULL ; member = g_list_next (member))
			{
				cl_rt = (E2_CommandLineRuntime *) member->data;
				if (cl_rt->original)
				{
					*rt = cl_rt;
					break;
				}
			}
//		}
	}
}
/**
@brief determine command line pointer(s)

If @a rt != NULL, this function tries to find the corresponding entry widget.
Or if @a rt is NULL, tries to find the @a rt corresponding to @a widget.
But then, if @a entry is not actually an entry widget, @a rt is just set
to the one for the first-registered command line (as opposed to dir line) if any

@param widget pointer to widget to check/set (may be NULL)
@param rt pointer to the command line runtime object to check/set, or to NULL

@return  TRUE if @a entry and @a rt are set to valid values
*/
static gboolean _e2_command_line_get (GtkWidget **widget,
	E2_CommandLineRuntime **rt)
{
	//the runtime object has precedence
	if (*rt == NULL)
	{
		//if the entry really is an entry, get the pointer to the
		//runtime object
		if (GTK_IS_ENTRY (*widget))
		{
			*rt = g_object_get_data (G_OBJECT (*widget), "command-line-runtime");
			if (*rt != NULL)
				return TRUE;
			else
			{
				E2_CommandLineRuntime *clrt;
				GList *line;
				for (line = app.command_lines; line != NULL ; line = g_list_next (line))
				{
					clrt = line->data;
					if (*widget ==
#ifdef USE_GTK2_14
						gtk_bin_get_child (GTK_BIN (clrt->combo))
#else
						GTK_BIN (clrt->combo)->child
#endif
					)
						break;
				}
				if (line != NULL)
				{
					*rt = clrt;
					return TRUE;
				}
			}
		}
		else
		//can't find anything from widget, just try to find the first available command line
			_e2_command_line_get_first (rt);
		if (*rt == NULL)	//OOPS can't find anything !!
			return FALSE;
	}
	//set the real entry widget
	*widget =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN ((*rt)->combo));
#else
		GTK_BIN ((*rt)->combo)->child;
#endif
	return TRUE;
}
/**
@brief change the appearance of @a entry to indicate whether or not its content is 'special'
@param entry the widget to alter
@param on TRUE to highlight, FALSE to normalise
@return
*/
void e2_command_line_highlight (GtkWidget *entry, gboolean on)
{
	static GdkColor *default_color = NULL;
	if (on)
	{
		gtk_widget_modify_text (entry, GTK_STATE_NORMAL,
			e2_option_color_get ("color-positive"));
	}
	else
	{
		if (default_color == NULL)
		{
			GtkStyle *style = gtk_rc_get_style (app.main_window); //not of entry itself, that may be altered already
			default_color = &style->text[GTK_STATE_NORMAL];
		}
		gtk_widget_modify_text (entry, GTK_STATE_NORMAL, default_color);
	}
}
/**
@brief change appearance of @a entry according to whether @a newtext is in parent combo's history
Also sets the active iter for the combo, if relevant
@param entry the widget used for finding combo model
@param newtext the string to check
@return
*/
void e2_command_line_update_highlight (GtkWidget *entry, const gchar *newtext)
{
	GtkComboBox *combo = GTK_COMBO_BOX (
#ifdef USE_GTK2_14
		gtk_widget_get_parent (entry)
#else
		entry->parent
#endif
	);
	GtkTreeModel *model = gtk_combo_box_get_model (combo);
	GtkTreeIter iter;
	gboolean badge = (gtk_tree_model_get_iter_first (model, &iter)
		  && e2_tree_find_iter_from_str_simple (model, 0, newtext, &iter, FALSE));
	e2_command_line_highlight (entry, badge);
	if (badge)
	{
//		printd (DEBUG, "Set dirline active iter");
		gtk_combo_box_set_active_iter (combo, &iter);
	}
}
/**
@brief change-dir helper function
This is initiated from within _e2_fileview_change_dir(), with BGL closed/on
@param path cleaned path of the dir that is now current, UTF-8 string
@param rt pointer to command line runtime data struct

@return
*/
static void _e2_command_line_change_dir (const gchar *path,
	E2_CommandLineRuntime *rt)
{
	GtkWidget *entry =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->combo));
#else
		GTK_BIN (rt->combo)->child;
#endif
	if (e2_option_bool_get_direct (rt->opt_history_last))
	{
		gtk_entry_set_text (GTK_ENTRY (entry), path);
		e2_command_line_update_highlight (entry, path);
		if (e2_option_bool_get ("dir-line-pathname-hint"))
			e2_widget_set_safetip (entry, path);
	}
	else
	{
		gtk_entry_set_text (GTK_ENTRY (entry), "");
		e2_command_line_highlight (entry, FALSE);
	}
}

  /*********************/
 /***** callbacks *****/
/*********************/

#ifdef E2_MOUSECUSTOM
/**
@brief mouse button-event (press and release) callback
This is to block default handling of modified events, so that gestures etc are
not interfered with
@param entry the entry widget where the button was pressed
@param event pointer to gtk event data struct
@param user_data UNUSED pointer specified when cb was connected

@return TRUE if modifier key(s) were pressed
*/
static gboolean _e2_command_line_button_filter_cb (GtkWidget *entry,
	GdkEventButton *event, gpointer user_data)
{
	printd (DEBUG, "command line button filter cb");
	GdkModifierType mask = e2_utils_get_modifiers ();
	return ((mask & E2_MODIFIER_MASK) != 0);
}
#else
/**
@brief mouse button-press callback
Double-left-clicks cause the focused entry widget to be activated,
or a directory selection dialog when <Ctrl> key and the middle-button
are pressed while the pointer is on a directory-line
@param entry the entry widget where the button was pressed
@param event pointer to gtk event data struct
@param rt pointer to commandline runtime data struct

@return TRUE if left-double or ctrl-middle button event
*/
static gboolean _e2_command_line_button_press_cb (GtkWidget *entry,
	GdkEventButton *event, E2_CommandLineRuntime *rt)
{
	printd (DEBUG, "command line button press cb");
	GdkModifierType mask;
	if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
	{
		mask = e2_utils_get_modifiers ();
		if (mask == 0)
		{
			g_signal_emit_by_name (G_OBJECT (entry), "activate");
			return TRUE;
		}
	}
	else if (event->button == 2 && event->type == GDK_BUTTON_PRESS && !rt->original)
	{
		mask = e2_utils_get_modifiers ();
		if (mask & GDK_CONTROL_MASK)
			return (e2_pane_choose_new_dir (rt->pane, entry));
	}
	return FALSE;
}
#endif
/**
@brief autocomplete dir-line entries when dir is mounted-local
This is a(nother) callback for dir-line key-press-event signals
@param entry pointer to the widget that received the keypress
@param event event data struct
@param rt pointer to data struct for the dir-line

@return TRUE if the key was non-modified and a textkey, and triggered completion
*/
static gboolean _e2_command_line_key_press_cb (GtkWidget *entry,
	GdkEventKey *event, E2_CommandLineRuntime *rt)
{
	if ((event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK
			| GDK_MOD3_MASK | GDK_MOD4_MASK | GDK_MOD5_MASK	//CHECKME
#ifdef USE_GTK2_10
			| GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK
#endif
		)) == 0
		&& (event->keyval < 0xF000 || event->keyval > 0xFFFF))
	{
		guint pane = (rt->pane == &app.pane1) ? E2PANE1 : E2PANE2;
		if (e2_fs_complete_dir (entry, event->keyval, pane))
			return TRUE;
	}
	return FALSE;
}
/**
@brief Another "activate" signal callback for [each] command line entry widget
It runs the command recorded in the dir line
e2_combobox_activated_cb() also applies, it updates history
@param entry the activated entry widget containing command to run
@param clrt callback user_data, as supplied to e2_combobox_get()

@return
*/
static void _e2_command_line_activate_cb (GtkEntry *entry,
	E2_CommandLineRuntime *clrt)
{
	printd (DEBUG, "_e2_command_line_activate_cb (entry:,user_data:)");
	const gchar *command_raw = gtk_entry_get_text (entry);
	if (command_raw != NULL && *command_raw != '\0')
	{
		e2_command_run ((gchar *)command_raw, E2_COMMAND_RANGE_DEFAULT, entry
#ifdef E2_COMMANDQ
		, FALSE
#endif
		);
		_e2_command_line_update_entry (clrt);
	}
}
/**
@brief The "activate" signal callback for each dirline entry widget
Opens the directory recorded in the dir line and updates history, using
a cleaned path if possible
@param entry activated entry widget containing path to open
@param clrt callback user_data, as supplied to e2_combobox_get()

@return
*/
static void _e2_command_line_diractivate_cb (GtkEntry *entry,
	E2_CommandLineRuntime *clrt)
{
	printd (DEBUG, "_e2_command_line_diractivate_cb (entry:,user_data:)");
	const gchar *path_raw = gtk_entry_get_text (entry);
	if (path_raw != NULL && *path_raw != '\0')
	{
		gchar *path = e2_utf8_unescape (path_raw, ' ');
		E2_PaneRuntime *prt = clrt->pane;
		if (prt->path != NULL)
		{
			gchar *trailer = path + strlen (path) - 1; //facilitate path-matching
			if (trailer > path && *trailer == G_DIR_SEPARATOR)
				*trailer = '\0';
			trailer = prt->path + strlen (prt->path) - 1;
			if (trailer > prt->path)
				*trailer = '\0';
			gboolean same = (strcmp (path, prt->path) == 0);
			*trailer = G_DIR_SEPARATOR;
			if (same)
			{
				e2_command_line_highlight (GTK_WIDGET (entry), TRUE);
				//update dirline history
				e2_combobox_activated_cb (GTK_WIDGET (entry),
					GINT_TO_POINTER (e2_option_bool_get ("dir-line-history-double")));
				goto focus;
			}
		}
//E2_VFSTMPOK no need to handle any URI here
		//signal need for dirline history update, if/when cd succeeds
		//i.e. when the path string is clean and valid
//		clrt->update_history = TRUE;
		e2_pane_change_dir (prt, path);
focus:
		g_free (path);
		if (e2_option_bool_get ("dir-line-focus-after-activate"))
		{
			if (prt == other_pane)
				e2_pane_activate_other ();
			gtk_widget_grab_focus (curr_view->treeview);
		}
	}
}
/**
@brief backup and cleanup during the destruction of a command line combobox widget
@param object UNUSED the object being processed
@param rt pointer to rt data struct for the command line
@return
*/
static void _e2_command_line_destroy_cb (GtkObject *object,
	E2_CommandLineRuntime *rt)
{
//	_e2_command_line_destroy (rt);
	line_names = g_list_remove (line_names, rt->name); //each name is unique
	e2_cache_unregister (rt->name);	//also zaps rt->history
	g_free (rt->name);
	app.command_lines = g_list_remove (app.command_lines, rt);
	DEALLOCATE (E2_CommandLineRuntime, rt);
}

  /*******************/
 /***** actions *****/
/*******************/

/**
@brief toggle focus between a dir line and the corresponding file pane

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

@return TRUE if the focus was changed
*/
static gboolean _e2_command_line_focus_dirline_action (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *pane = curr_pane;	//default
	//string with "1" (or anything else) which specifies the dir line to focus
	gchar *arg = (gchar *) art->data;
	if (arg != NULL)
	{
		g_strstrip (arg);
		if (*arg != '\0')
			pane = (!strcmp (arg, "2")) ? &app.pane2 : &app.pane1;	//no arg translation
	}
	//find the first-registered dir-line for the pane
	GList *list;
	for (list = app.command_lines; list != NULL ; list = g_list_next (list))
	{
		E2_CommandLineRuntime *clrt = (E2_CommandLineRuntime *) list->data;
		if (!clrt->original && clrt->pane == pane)
		{
			GtkWidget *child =
#ifdef USE_GTK2_14
				gtk_bin_get_child (GTK_BIN (clrt->combo));
#else
			GTK_BIN (clrt->combo)->child;
#endif
#ifdef USE_GTK2_18
			if (gtk_widget_has_focus (child))
#else
			if (GTK_WIDGET_HAS_FOCUS (child))
#endif
				gtk_widget_grab_focus (curr_view->treeview);
			else
			{
				gtk_editable_set_position (GTK_EDITABLE (child), -1);
				gtk_widget_grab_focus (child);
			}
			return TRUE;
		}
	}
	return FALSE;	//no dirline found for the pane
}
#ifdef E2_MOUSECUSTOM
/**
@brief 'activate' a command line or dirline via a pointer-device operation

@param from the entry widget which was the focus of the pointer operation
@param art UNUSED action runtime data

@return TRUE if @a from is the right type ofd widget
*/
static gboolean _e2_command_line_activate_action (gpointer from, E2_ActionRuntime *art)
{
	if (GTK_IS_ENTRY (from))
	{
		GtkWidget *parent =
#ifdef USE_GTK2_14
			gtk_widget_get_parent (GTK_WIDGET(from));
#else
			GTK_WIDGET(from)->parent;
#endif
		if (GTK_IS_COMBO_BOX_ENTRY (parent))
		{
			printd (DEBUG, "issue activate signal for combobox entry");
			g_signal_emit_by_name (G_OBJECT (from), "activate");
			return TRUE;
		}
	}
	return FALSE;
}
#endif
/**
@brief focus the first-logged command line

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

@return TRUE if the line is found
*/
static gboolean _e2_command_line_focus_action (gpointer from, E2_ActionRuntime *art)
{
	E2_CommandLineRuntime *rt = NULL;
	_e2_command_line_get_first (&rt);	//get the first line
	if (rt == NULL)
		return FALSE;
	GtkWidget *child =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->combo));
#else
		GTK_BIN (rt->combo)->child;
#endif
	gtk_editable_set_position (GTK_EDITABLE (child), -1);
	gtk_widget_grab_focus (child);
	return TRUE;
}
/**
@brief toggle focus between first-logged command line and active pane

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

@return TRUE if the line is found
*/
static gboolean _e2_command_line_focus_toggle_action
	(gpointer from, E2_ActionRuntime *art)
{
	E2_CommandLineRuntime *rt = NULL;
	_e2_command_line_get_first (&rt); //gets the first line
	if (rt == NULL)
		return FALSE;
	GtkWidget *child =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->combo));
#else
		GTK_BIN (rt->combo)->child;
#endif
#ifdef USE_GTK2_18
	if (gtk_widget_has_focus (child))
#else
	if (GTK_WIDGET_HAS_FOCUS (child))
#endif
		gtk_widget_grab_focus (curr_view->treeview);
	else
	{
		gtk_editable_set_position (GTK_EDITABLE (child), -1);
		gtk_widget_grab_focus (child);
	}
	return TRUE;
}
/**
@brief clear contents of an entry, generally @a from

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

@return TRUE
*/
static gboolean _e2_command_line_clear_action
	(gpointer from, E2_ActionRuntime *art)
{
	//from may be entry, button, menuitem etc
	GtkWidget *entry = (from == NULL) ? NULL : GTK_WIDGET (from);
	if (!GTK_IS_ENTRY (from))
	{
		//try to find the corresponding command line
		E2_CommandLineRuntime *rt = NULL;
		_e2_command_line_get (&entry, &rt);
		if (entry == NULL || entry == GTK_WIDGET (from))
		{
#if 0
			//FIXME get the entry from somewhere
			GList *line;
			entry = NULL;
			for (line = app.command_lines; line != NULL ; line = line->next)
			{
				rt = (E2_CommandLineRuntime *)line->data;
				if (rt->combo <shares some ancestor with 'from'>) ?? from = NULL ?
				{
					entry =
#ifdef USE_GTK2_14
						gtk_bin_get_child (GTK_BIN (rt->combo));
#else
						GTK_BIN (rt->combo)->child;
#endif
					break;
				}
			}
			if (entry == NULL)
#else
			printd (WARN, "no suport yet for clear action initiated from random non-entry");
#endif
				return FALSE;
		}
	}
	gtk_entry_set_text (GTK_ENTRY (entry), "");
	if (g_object_get_data (G_OBJECT (entry), "e2-dir-line") != NULL)
		e2_command_line_highlight (entry, FALSE);
	return TRUE;
}
/**
@brief clear command line history

@param from the entry which was activated
@param art action runtime data

@return TRUE if there was a history
*/
static gboolean _e2_command_line_clear_history_action
	(gpointer from, E2_ActionRuntime *art)
{
	GtkWidget *entry = (from == NULL) ? NULL : GTK_WIDGET (from);
	//find the command line for the entry
	E2_CommandLineRuntime *rt = NULL;
	_e2_command_line_get (&entry, &rt);

	if (rt->history != NULL)
	{
		gint num = g_list_length (rt->history);
		for (; num > 0; num--)
			gtk_combo_box_remove_text (GTK_COMBO_BOX (rt->combo), 0);
		e2_list_free_with_data (&rt->history);
		g_object_unref (rt->model);
		return TRUE;
	}
	return FALSE;
}
/**
@brief insert selection in @a from into @a entry
If @a from is a filelist treeview, the name of each selected item is inserted
@param from the widget where the action was initiated
@param art action runtime data

@return TRUE if ...
*/
static gboolean _e2_command_line_insert_action
	(gpointer from, E2_ActionRuntime *art)
{
	gboolean retval;
	gint start, end;

	E2_CommandLineRuntime *clrt = NULL;
	_e2_command_line_get_first (&clrt); //gets the first line
	if (clrt == NULL)
		return FALSE;

	GtkWidget *entry =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (clrt->combo));
#else
		GTK_BIN (clrt->combo)->child;
#endif
	GString *cmd = g_string_sized_new (128);

	if (GTK_IS_TEXT_VIEW (from)) //output
	{
		GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (from));
		GtkTextIter start, end;
		retval = gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
		if (retval)
		{
			gchar *text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
			cmd = g_string_assign (cmd, text);
			g_free (text);
		}
	}
	else if (GTK_IS_ENTRY (from) && GTK_WIDGET (from) != entry)
	{
		retval = gtk_editable_get_selection_bounds (GTK_EDITABLE (from), &start, &end);
		if (retval)
		{
			gchar *text = gtk_editable_get_chars (GTK_EDITABLE (from), start, end);
			cmd = g_string_assign (cmd, text);
			g_free (text);
		}
	}
	else //filelists, buttons, menus
	{
		const gchar *arg = NULL;
		E2_PaneRuntime *rt = e2_pane_get_runtime (from, art->data, &arg);
		if (rt != NULL)
		{
			gboolean escape, quote;
			if (arg != NULL)
			{
				//find out from the action argument if the user wants quotes/escaping
//				gchar *tmp = g_utf8_strdown (arg, -1);
//OR			gchar *tmp = g_strdup (arg);
//FOR == 		check g_strstrip (tmp);
				quote = (strstr (arg, _A(117)) != NULL);	//_("quote"
				escape = (quote) ? FALSE : (strstr (arg, _A(112)) != NULL);	//_("escape"
//				g_free (tmp);
			}
			else
			{
				escape = quote = FALSE;
			}

#ifdef E2_REFRESH_DEBUG
			printd (DEBUG, "disable refresh, insert action");
#endif
			e2_filelist_disable_refresh ();

			GList *base;
			base = e2_fileview_get_selected_local (&rt->view);
			if (base != NULL)
			{
				GList *tmp;
				for (tmp = base; tmp != NULL; tmp = tmp->next)
				{
					gchar *utf;
					//not DISPLAY, always dup, to avoid refresh race
					utf = D_FILENAME_FROM_LOCALE (((FileInfo *) tmp->data)->filename);
					cmd = g_string_append_c (cmd, ' ');
//tag E2_BADQUOTES
					if (escape)
					{
						gchar *qp = e2_utils_quote_string (utf);
						cmd = g_string_append (cmd, qp);
						g_free (qp);
					}
					else if (quote)
						g_string_append_printf (cmd, "\"%s\"", utf);
					else
						cmd = g_string_append (cmd, utf);
					g_free (utf);
				}
				g_list_free (base);
				retval = TRUE;
			}
			else
				retval = FALSE;

#ifdef E2_REFRESH_DEBUG
			printd (DEBUG, "enable refresh, insert action");
#endif
			e2_filelist_enable_refresh();
		}
		else
			retval = FALSE;
	}

	if (retval)
	{
		if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
			gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
		else
			start = gtk_editable_get_position (GTK_EDITABLE (entry));
		gtk_editable_insert_text (GTK_EDITABLE (entry), cmd->str, cmd->len, &start);
		gtk_editable_set_position (GTK_EDITABLE (entry), start);
	}
	g_string_free (cmd, TRUE);

	return retval;
}
/**
@brief complete the string in the command line associated with @a from
This is the mechanism for tab-completion, via a keybinding to the relevant
widget
@param from a pointer to the entry widget whose cntents are to be completed, or NULL
@param art pointer to action rt data

@return
*/
static gboolean _e2_command_line_complete_action
	(gpointer from, E2_ActionRuntime *art)
{
	//find which command line is active
	GtkWidget *entry = (from == NULL) ? NULL : GTK_WIDGET (from);
	E2_CommandLineRuntime *rt = NULL;
	_e2_command_line_get (&entry, &rt);
	if (rt == NULL)
		return FALSE;	//can't find what to work on

	gint pos = gtk_editable_get_position (GTK_EDITABLE (entry));	//characters, not bytes
	gchar *text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));	//could be ""

	//do macros/variables replacement CHECKME worth it ?
	printd (DEBUG, "_e2_command_line_complete_action, macro/var interpretation");
	gchar *tmp = text;
	gchar *freeme = e2_utils_expand_macros (text, NULL); //note %% changes to %
	if (freeme == NULL || freeme == GINT_TO_POINTER (0x1))
		freeme = tmp;
	text = e2_utils_replace_vars (freeme, FALSE);
	if (freeme != tmp)
		g_free (freeme);
	if (strcmp (tmp, text))
		pos = g_utf8_strlen (text, -1);
	g_free (tmp);

	gchar *arg = (gchar *)art->data;
	printd (DEBUG, "complete_action (rt:_,arg:%s,entry:_", arg);
	//find out what type of completion is wanted and relevant
	E2_CompleteFlags flags;
	if (!rt->original)
		flags = E2_COMPLETE_FLAG_DIRS;	//dir-lines just find dirs
	else
	{
		flags = E2_COMPLETE_FLAG_PATH;	//command-lines default to finding executables
		//but whitespace before cursor position implies we are completing an argument
		gchar *s = e2_utils_pass_whitespace (text);
		if (s != NULL)
		{
			gchar *p = e2_utils_bare_strchr (s, ' ');
			if (p == NULL)
				p = e2_utils_bare_strchr (s, '\t');
			if (p != NULL)
			{
				if (p < g_utf8_offset_to_pointer (text, pos))
				{
					flags &= ~E2_COMPLETE_FLAG_PATH;
					g_strstrip (arg);
					if (strstr (arg, _("all")) != NULL)	//_I("all")
						flags |= E2_COMPLETE_FLAG_ALL;
					else
					{
						if (strstr (arg, _A(111)) != NULL)
							flags |= E2_COMPLETE_FLAG_DIRS;
						if (strstr (arg, _A(114)) != NULL)
							flags |= E2_COMPLETE_FLAG_FILES;
						if (strstr (arg, _("mounts")) != NULL	//_I("mounts")
							//even if arg isn't 'mounts', add mountpoints completion
							//in case of entered [u]mount command
							|| g_strstr_len (s, sizeof(E2_MOUNTCOMMAND) + 3, E2_MOUNTCOMMAND) != NULL)
							flags |= E2_COMPLETE_FLAG_MOUNT;
/*NOT WORKING			if (strstr (arg, _("shell")) != NULL)
							flags |= E2_COMPLETE_FLAG_SHELL;
*/
						if (flags == 0)	//empty or unknown arg
							flags = E2_COMPLETE_FLAG_ALL;
					}
				}
			}
			else if (strstr (arg, _("shell")) != NULL)
				flags = E2_COMPLETE_FLAG_SHELL;	//shell completion ok for commmands too
		}
	}

	GList *found = NULL;
	gint num = e2_complete_str (&text, &pos, &found, flags, 0);
//	printd (DEBUG, "found %d - %s - %d", num, text, pos);

	if (num > 0)
	{
		// if it isn't a dir: append a <space>
		if ((e2_option_bool_get ("command-line-complete-append-space")) &&
			(num == 1) && (g_utf8_get_char (g_utf8_offset_to_pointer (text, pos - 1)) != G_DIR_SEPARATOR))
		{
			gchar *part1 = e2_utf8_ndup (text, pos);
			gchar *part2 = g_utf8_offset_to_pointer (text, pos);
			gchar *text_spaced = g_strconcat (part1, " ", *part2 != '\0' ? part2 : "" , NULL);
			g_free (part1);
			gtk_entry_set_text (GTK_ENTRY (entry), text_spaced);
			gtk_editable_set_position (GTK_EDITABLE (entry), pos +1);
			g_free (text_spaced);
		}
		else
		{
			gtk_entry_set_text (GTK_ENTRY (entry), text);
			gtk_editable_set_position (GTK_EDITABLE (entry), pos);
		}
		if (num > 1)
		{
			GList *tmp;
			for (tmp = found; tmp != NULL; tmp = g_list_next (tmp))
			{
				e2_output_print (&app.tab, (gchar *)tmp->data, NULL, TRUE, NULL);
			}
			e2_output_print_end (&app.tab, FALSE);
		}
	}
	e2_list_free_with_data (&found);
	g_free (text);
	return TRUE;
}
/**
@brief children-menu callback, prepends a pid string to the command line

@param item UNUSED the activated menu item
@param rt pointer to data for the command related to @a item, or NULL for "no children" item

@return
*/
static void _e2_commmand_line_child_menu_cb (GtkMenuItem *item, E2_TaskRuntime *rt)
{
	if (rt == NULL)
		return;	//no children
	pthread_mutex_lock (&task_mutex);
	GList *member = g_list_find (app.taskhistory, rt);
	pthread_mutex_unlock (&task_mutex);
	if (member != NULL)	//command data still exists
	{
		E2_CommandLineRuntime *clrt = NULL;
		_e2_command_line_get_first (&clrt); //in principle, there could be > 1 OK ??
		if (clrt != NULL)
		{
			GtkWidget *entry =
#ifdef USE_GTK2_14
				gtk_bin_get_child (GTK_BIN (clrt->combo));
#else
				GTK_BIN (clrt->combo)->child;
#endif
			gint newlen;
			gint oldcursor = gtk_editable_get_position (GTK_EDITABLE (entry));
			GdkModifierType mask = e2_utils_get_modifiers ();
			if (oldcursor == 0 || (mask & GDK_CONTROL_MASK))
			{
				gint newpos = 0;
				gchar *newtext = g_strconcat (rt->pidstr, ":", NULL);
				newlen = strlen (newtext);
				gtk_editable_insert_text (GTK_EDITABLE (entry), newtext,
					newlen, &newpos);
				g_free (newtext);
			}
			else
			{
				newlen = strlen (rt->pidstr);
				gtk_editable_insert_text (GTK_EDITABLE (entry), rt->pidstr,
					newlen, &oldcursor);
			}
			gtk_editable_set_position (GTK_EDITABLE (entry), oldcursor+newlen);

			if (rt->status != E2_TASK_RUNNING)
			{
				gchar *msg = g_strdup_printf (_("Warning - process %s is not active"),
					rt->pidstr);
				e2_output_print (&app.tab, msg, NULL, TRUE, NULL);
				g_free (msg);
			}
		}
	}
}
/**
@brief create and pop up a menu of child processes
This essentially gets data for prepending to a command line
@param action_data ptr to data assigned when action struct created at session start NULL
@param from the thing that was activated to popup the menu
@param rt_data data suppplied to the action

@return TRUE if the action succeeded
*/
static gboolean _e2_command_line_insert_child_pid
	(gpointer from, E2_ActionRuntime *art)
{
	if (GTK_IS_BUTTON (from))
	{
		art->state &= ~GDK_MOD2_MASK;
		if (!ACTION_CLICK(art))
			return FALSE;
	}
	GtkWidget *menu = e2_menu_create_child_menu (E2_CHILD_ACTIVE,
		_e2_commmand_line_child_menu_cb);
	//determine the menu's popup position
	if (GTK_IS_BUTTON (from))
		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
			(GtkMenuPositionFunc) e2_toolbar_set_menu_position, GTK_WIDGET (from), 1, 0);
	else
		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
			(GtkMenuPositionFunc) e2_output_set_menu_position, app.tab.text, 0, 0);

	return TRUE;
}

  /******************/
 /***** public *****/
/******************/

/**
@brief insert @a string into command line at current cursor position
@param newtext UTF-8 text to be inserted

@return TRUE if the commandline is found
*/
gboolean e2_command_line_insert (const gchar *newtext)
{
	E2_CommandLineRuntime *clrt = NULL;
	_e2_command_line_get_first (&clrt); //in principle, there could be > 1 OK ??
	if (clrt != NULL)
	{
		GtkWidget *entry =
#ifdef USE_GTK2_14
			gtk_bin_get_child (GTK_BIN (clrt->combo));
#else
			GTK_BIN (clrt->combo)->child;
#endif
		gint oldcursor = gtk_editable_get_position (GTK_EDITABLE (entry));
		gint newlen = g_utf8_strlen (newtext, -1);
		gtk_editable_insert_text (GTK_EDITABLE (entry), newtext, newlen, &oldcursor);
		gtk_editable_set_position (GTK_EDITABLE (entry), oldcursor + newlen);
		return TRUE;
	}
	return FALSE;
}
/**
@brief [re]register all key-event callbacks for a commandline entry
@param rt pointer to rt data for the commandline

@return
*/
void e2_command_line_register_keybindings (E2_CommandLineRuntime *rt)
{
	GtkWidget *entry =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->combo));
#else
		GTK_BIN (rt->combo)->child;
#endif
	//other "key-press-event" connection(s) have non-NULL data
	//also, if needed, could use bindings cb func
	guint id = g_signal_lookup ("key-press-event", GTK_TYPE_ENTRY);
	g_signal_handlers_disconnect_matched (G_OBJECT (entry),
		G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA, id, 0, NULL, NULL, NULL);

	gchar *category = (rt->original) ?
		g_strconcat (_C(17),".",_C(23),".",_C(5),NULL):	//_(general.main.command-line"
		g_strconcat (_C(17),".",_C(23),".",_C(12),NULL);//_(general.main.dir-line"

	e2_keybinding_enrol (entry, category, (void(*)(E2_OptionSet*))NULL);
	g_free (category);
}
#ifdef E2_MOUSECUSTOM
/**
@brief [re]register all button-event callbacks for a commandline entry
@param rt pointer to rt data for the commandline

@return
*/
void e2_command_line_register_pointerbindings (E2_CommandLineRuntime *rt)
{
	GtkWidget *entry =
# ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->combo));
# else
		GTK_BIN (rt->combo)->child;
# endif
	guint id = g_signal_lookup ("button-press-event", GTK_TYPE_ENTRY);
	g_signal_handlers_disconnect_matched (G_OBJECT (entry),
		 G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA, id, 0, NULL, NULL, NULL);
# ifdef E2_PTRGESTURES
	id = g_signal_lookup ("button-release-event", GTK_TYPE_ENTRY);
	g_signal_handlers_disconnect_matched (G_OBJECT (entry),
		 G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA, id, 0, NULL, NULL, NULL);
# endif

	gchar *category = (rt->original) ?
		g_strconcat (_C(17),".",_C(23),".",_C(5),NULL):	//_(general.main.command-line"
		g_strconcat (_C(17),".",_C(23),".",_C(12),NULL);//_(general.main.dir-line"

	e2_mousebinding_enrol (entry, category, (void(*)(E2_OptionSet*))NULL);
# ifdef E2_PTRGESTURES
	e2_mousegesture_enrol (entry, category, (void(*)(E2_OptionSet*))NULL);
# endif
	g_free (category);
}
#endif
/**
@brief create a data struct and widget for commandline or dirline

@param commands TRUE for a command line, FALSE for dir line
@param pane_rt NULL for a command line, pane data struct for a dir line

@return pointer to the created runtime data struct
*/
E2_CommandLineRuntime *e2_command_line_create (gboolean commands,
	E2_PaneRuntime *pane_rt)
{
	E2_CommandLineRuntime *rt = ALLOCATE (E2_CommandLineRuntime);
	CHECKALLOCATEDFATAL (rt);

	app.command_lines = g_list_append (app.command_lines, rt);

	//unique private name, for cacheing, matching
	rt->name = _e2_command_line_get_name ((commands) ? "command line" : "dir line");
	//append name to local name register to ensure uniqueness of each name
	line_names = g_list_prepend (line_names, rt->name);

//	rt->func = cb;
//	rt->flags = flags;
	rt->original = commands; //TRUE for commandline, FALSE for dirline
	rt->pane = pane_rt;	//pane rt for dirline, NULL for commandline

	rt->history = NULL;
//	rt->update_history = FALSE;
	E2_Cache *cache = e2_cache_list_register (rt->name, &rt->history);
	//this func can't backup model data during destruction - the model is gone already
	cache->sync_func = _e2_command_line_sync_history;
	cache->sync_data = rt;

	void (*activate_cb)(GtkEntry*, E2_CommandLineRuntime*);
	E2_ComboBoxFlags flags = E2_COMBOBOX_HAS_ENTRY;
	//sort out some options for command line or dir line;
	//the options have been registered in the general init function
	if (commands)
	{	//this is a command line
		if (e2_option_bool_get ("command-line-history-double"))
			flags |= E2_COMBOBOX_ALLOW_DOUBLE;
		if (e2_option_bool_get ("command-line-history-cycle"))
			flags |= E2_COMBOBOX_CYCLE_HISTORY;
		if (e2_option_bool_get ("command-line-menu-style"))
			flags |= E2_COMBOBOX_MENU_STYLE;
		flags |= E2_COMBOBOX_FOCUS_ON_CHANGE;
		activate_cb = _e2_command_line_activate_cb;
		//we need to access this often, thus we save the pointer to the option
		//set and will not have to look it up in the hashtable each time
		rt->opt_history_last = e2_option_get ("command-line-history-last");
	}
	else
	{	//this is a dir line
		if (e2_option_bool_get ("dir-line-history-double"))
			flags |= E2_COMBOBOX_ALLOW_DOUBLE;
		if (e2_option_bool_get ("dir-line-history-cycle"))
			flags |= E2_COMBOBOX_CYCLE_HISTORY;
		if (e2_option_bool_get ("dir-line-menu-style"))
			flags |= E2_COMBOBOX_MENU_STYLE;
		activate_cb = _e2_command_line_diractivate_cb;
		rt->opt_history_last = e2_option_get ("dir-line-history-last");
	}
	//actually create the comboboxentry and its history
	rt->combo = e2_combobox_get ((ActivateFunc)activate_cb, rt, &rt->history, flags);
	GtkWidget *child =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->combo));
#else
		GTK_BIN (rt->combo)->child;
#endif
	rt->model = gtk_combo_box_get_model (GTK_COMBO_BOX (rt->combo));	//model is for a liststore

	if (!commands)
	{
		//dirlines manage history updates in a single callback
		//and are initialised later, by a cd or e2_toolbar_rebadge()
		g_signal_handlers_disconnect_matched (G_OBJECT (child),
			G_SIGNAL_MATCH_FUNC,
			0, 0, NULL, e2_combobox_activated_cb, NULL);
	}
	else //command line
		if (e2_option_bool_get ("command-line-history-last"))
	{
		if (e2_combobox_has_history (GTK_COMBO_BOX (rt->combo)))
		{
			//suspend "activate" signals connected when combo was created
			g_signal_handlers_block_matched (G_OBJECT (child),
				G_SIGNAL_MATCH_FUNC,
				0, 0, NULL, e2_combobox_activated_cb, NULL);
			g_signal_handlers_block_matched (G_OBJECT (child),
				G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, activate_cb, rt);

			gtk_combo_box_set_active (GTK_COMBO_BOX (rt->combo), 0);

			g_signal_handlers_unblock_matched (G_OBJECT (child),
				G_SIGNAL_MATCH_FUNC,
				0, 0, NULL, e2_combobox_activated_cb, NULL);
			g_signal_handlers_unblock_matched (G_OBJECT (child),
				G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, activate_cb, rt);
		}
	}
	//arrange for backup/cleanup before complete destruction of combo
	g_signal_connect (GTK_OBJECT (rt->combo), "destroy",
		G_CALLBACK (_e2_command_line_destroy_cb), rt);

	//allow the entry to find its rt data
	g_object_set_data (G_OBJECT (child), "command-line-runtime", rt);
//see also ...register_all()
//	e2_command_line_register_pointerbindings (rt);
#ifdef E2_MOUSECUSTOM
	//non-NULL data here, if only to prevent disconnection in
	// e2_command_line_register_pointerbindings()
	g_signal_connect (G_OBJECT (child), "button-press-event",
		G_CALLBACK (_e2_command_line_button_filter_cb), rt);
# ifdef E2_PTRGESTURES
	g_signal_connect (G_OBJECT (child), "button-release-event",
		G_CALLBACK (_e2_command_line_button_filter_cb), rt);
# endif
#else
	g_signal_connect (G_OBJECT (child), "button-press-event",
		G_CALLBACK (_e2_command_line_button_press_cb), rt);
#endif
	if (commands)
	{	//this is a command line
		g_object_set_data (G_OBJECT (child), "e2-command-line", child); //make it recognisable
		if (e2_option_bool_get ("command-line-show-output-on-focus-in"))
			g_signal_connect (G_OBJECT (child), "focus-in-event",
				G_CALLBACK (e2_window_output_show), NULL);
		if (e2_option_bool_get ("command-line-hide-output-on-focus-out"))
			g_signal_connect (G_OBJECT (child), "focus-out-event",
				G_CALLBACK (e2_window_output_hide), NULL);
	}
	else
	{	//this is a dir line
		g_object_set_data (G_OBJECT (child), "e2-dir-line", child);
//see register_all
//		e2_command_line_register_keybindings (rt);
		g_signal_connect (G_OBJECT (child), "key-press-event",
			G_CALLBACK (_e2_command_line_key_press_cb), rt); //auto-completion
		//register to change_dir hook, if wanted
		//e2_hook_register (&rt->pane->hook_change_dir, func, data);
	}

	return rt;
}
/* *
@brief

@param rt pointer to command line runtime data struct

@return the commandline widget
*/
//static GtkWidget *_e2_command_line_create (E2_CommandLineRuntime *rt)
//{
//	return GTK_WIDGET (rt->combo);
//}
/**
@brief clear runtime data for all command/dir lines, no backup

@return
*/
void e2_command_line_clean_all (void)
{
	GList *member;
	for (member = app.command_lines; member != NULL; member = member->next)
	{
		E2_CommandLineRuntime *rt = (E2_CommandLineRuntime *)member->data;
		g_free (rt->name);
		e2_list_free_with_data (&rt->history); //CHECKME
		DEALLOCATE (E2_CommandLineRuntime, rt);
	}
}
/**
@brief change-dirline contents function
This is initiated with BGL closed/on
@param utfpath utf-8 string, path of the dir that is being opened, with trailer
@param rt pointer to data struct for the pane being changed

@return
*/
void e2_command_line_change_dir (gchar *utfpath, E2_PaneRuntime *rt)
{
	GList *member;
	for (member = app.command_lines; member != NULL; member = member->next)
	{
		E2_CommandLineRuntime *cl = (E2_CommandLineRuntime *)member->data;
		if (cl->pane == rt)
		{
			_e2_command_line_change_dir (utfpath, cl);
			break;
		}
	}
}
/**
@brief initialize command line actions

This function initializes the command line actions.
It is, and should only be, called once at startup.

@return
*/
void e2_command_line_actions_register (void)
{
	E2_Action actions[] =
	{
	{g_strconcat(_A(1),".",_A(26),NULL), (gboolean(*)(gpointer,E2_ActionRuntime*))NULL,
									FALSE,E2_ACTION_TYPE_COMMAND_LINE,
									E2_ACTION_EXCLUDE_MENU | E2_ACTION_EXCLUDE_ACCEL,
		//NULL data signals a command line as distinct from to dirline
									NULL, NULL},
	{g_strconcat(_A(1),".",_A(47),NULL), _e2_command_line_focus_action,        FALSE,E2_ACTION_TYPE_ITEM, 0, NULL, NULL},
	{g_strconcat(_A(1),".",_A(100),NULL),_e2_command_line_focus_toggle_action, FALSE,E2_ACTION_TYPE_ITEM, 0, NULL, NULL},
#ifdef E2_MOUSECUSTOM
	{g_strconcat(_A(1),".",_A(29),NULL), _e2_command_line_activate_action,     FALSE,E2_ACTION_TYPE_ITEM, E2_ACTION_EXCLUDE_MENU | E2_ACTION_EXCLUDE_TOOLBAR | E2_ACTION_EXCLUDE_ACCEL, NULL, NULL},
#endif
	{g_strconcat(_A(1),".",_A(33),NULL), _e2_command_line_clear_action,        FALSE,E2_ACTION_TYPE_ITEM, 0, NULL, NULL},
	{g_strconcat(_A(1),".",_A(34),NULL), _e2_command_line_clear_history_action,FALSE,E2_ACTION_TYPE_ITEM, 0, NULL, NULL},
	{g_strconcat(_A(1),".",_A(35),NULL), _e2_command_line_complete_action,     TRUE, E2_ACTION_TYPE_ITEM, 0, NULL, NULL},
	{g_strconcat(_A(1),".",_A(56),NULL), _e2_command_line_insert_action,       FALSE,E2_ACTION_TYPE_ITEM, 0, NULL, NULL},
	{g_strconcat(_A(2),".",_A(27),NULL), _e2_command_line_insert_child_pid,    FALSE,E2_ACTION_TYPE_ITEM, E2_ACTION_EXCLUDE_MENU, NULL, NULL},
	{g_strconcat(_A(5),".",_A(100),NULL),_e2_command_line_focus_dirline_action,TRUE, E2_ACTION_TYPE_ITEM, E2_ACTION_EXCLUDE_MENU | E2_ACTION_EXCLUDE_TOOLBAR, NULL, NULL},
#ifdef E2_MOUSECUSTOM
	//same as for commandline activation - ok ?
	{g_strconcat(_A(5),".",_A(29),NULL), _e2_command_line_activate_action,     FALSE,E2_ACTION_TYPE_ITEM, E2_ACTION_EXCLUDE_MENU | E2_ACTION_EXCLUDE_TOOLBAR | E2_ACTION_EXCLUDE_ACCEL, NULL, NULL},
#endif
	};
	guint i, count = sizeof (actions)/sizeof (E2_Action);
	for (i = 0; i < count; i++)
		e2_action_register (&actions[i]);
}
/**
@brief initialize command line options

This function initializes the options for the various command lines.
It is, and should only be, called once at startup.

@return
*/
void e2_command_line_options_register (void)
{
	//none of these require a rebuild after change
	//command line
	gchar *group_name = g_strconcat(_C(6),".",_C(5),":",_C(26),NULL);  //_("commands.command line:misc"
	e2_option_bool_register ("command-line-history-last", group_name, _("show last"),
		_("If activated, the last-entered command will be displayed, instead of an empty line"),
		NULL, FALSE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_FREEGROUP);

	group_name = g_strconcat(_C(6),".",_C(5),":",_C(18),NULL);  //_("commands.command line:history"
	e2_option_int_register ("command-line-history-max", group_name, _("maximum number of history entries"),
		_("This is the largest number of command-line history entries that will be recorded"),
		NULL, 10, 0, 9999999,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_FREEGROUP);
	e2_option_bool_register ("command-line-history-double", group_name, _("double entries"),
		_("This allows entries to be recorded more than once in the history list, so the last entry is always close to hand"),
		NULL, FALSE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_BUILDBARS);
	e2_option_bool_register ("command-line-history-cycle", group_name, _("cyclic list"),
		_("When scanning the history list, cycle from either end around to the other end, instead of stopping"),
		NULL, TRUE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_BUILDBARS);
	e2_option_bool_register ("command-line-menu-style",
		group_name, _("show as a menu"),
		_("If activated, the history entries will be presented as a menu. "
		"For most Gtk+2 themes, this will not be as attractive as the list view"),
		NULL, FALSE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_BUILDBARS);

	group_name =g_strconcat(_C(6),".",_C(5),":",_C(40),NULL); //_("commands.command line:tab completion"
	e2_option_bool_register ("command-line-complete-append-space",
		group_name, _("append space after unique items"),
		_("This appends a 'space' character to the end of a unique successful match of a file"),
		NULL, TRUE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_FREEGROUP);

	//dir line
	group_name = g_strconcat(_C(33),".",_C(12),":",_C(26),NULL); //_("panes.directory line:misc"
	e2_option_bool_register ("dir-line-history-last",
		group_name, _("show last entry"),
		_("If activated, the last-entered directory will be displayed, instead of an empty line"),
		NULL, TRUE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_FREEGROUP);
	e2_option_bool_register ("dir-line-pathname-hint",
		group_name, _("show pathname as a tooltip"),
		_("If activated, the full directory pathname will display as a tooltip. "
		"This is useful when the path is too long for the normal display"),
		NULL, TRUE,
		E2_OPTION_FLAG_ADVANCED);
	const gchar *opt_completions[] =
		{_("<Tab> only"), _("inserted"), _("selected"), NULL};
	e2_option_sel_register ("dir-line-completion", group_name, _("directory path completion"),
		_("This determines the mode of completion when keying a directory-path"),
		NULL, 1, opt_completions,
		E2_OPTION_FLAG_ADVANCED);

	group_name = g_strconcat(_C(33),".",_C(12),":",_C(18),NULL); //_("panes.directory line:history"
	e2_option_int_register ("dir-line-history-max", group_name, _("maximum number of history entries"),
		_("This is the largest number of directory-line history entries that will be retained"),
		NULL, 10, 0, 999999,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_FREEGROUP);
	e2_option_bool_register ("dir-line-history-double",
		group_name, _("double entries"),
		_("This allows entries to be recorded more than once in the history list, so the last entry is always close to hand"),
		NULL, FALSE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_BUILDBARS);
	e2_option_bool_register ("dir-line-history-cycle",
		group_name, _("cyclic list"),
		_("When scanning the history list, cycle from either end around to the other end, instead of stopping"),
		NULL, TRUE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_BUILDBARS);
	e2_option_bool_register ("dir-line-menu-style",
		group_name, _("show as a menu"),
		_("If activated, the directory line history will be presented as a menu. "
		"For most for most Gtk+2 themes, this will not be as attractive as the list view"),
		NULL, FALSE,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_BUILDBARS);
}
