/* $Id: e2_context_menu.c 2095 2010-05-21 00:09:05Z tpgww $

Copyright (C) 2003-2010 tooar <tooar@emelfm2.net>
Portions copyright (C) 1999 Michael Clark.

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.
*/
/* 09-20-2000: Modified by Aurelien Gateau
            Added code for Named actions for filetypes
*/

/**
@file src/e2_context_menu.c
@brief file-pane context menu functions

Some of the functions are used in other contexts eg toolbar menus
Other context menus are handled in the same file as their 'context'.
*/
/**
\page menu the filelist context menu

ToDo -describe how this works
*/
/**
\page othermenu other context menus

ToDo
*/

#include "e2_context_menu.h"
#include <string.h>
#include "e2_filelist.h"
#include "e2_filetype.h"

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

/**
@brief execute action corresponding to item selected from a menu
We do this outside the manu-item "activated" callback to avoid disrupting gtk's
menu-activation process
@param cmd pointer to allocated command-string, free'd here

@return FALSE to remove the source
*/
static gboolean _e2_context_menu_run_filetype_action (gchar *cmd)
{
	printd (DEBUG, "timer callback: context menu run filetype action");
	gdk_threads_enter ();
	e2_filetype_exec_action (cmd);
	gdk_threads_leave ();
	g_free (cmd);
	e2_utils_fake_event (); //CHECKME does this actually help?
	return FALSE;
}
/**
@brief setup to execute action corresponding to item selected from filetype tasks menu
This is the callback for handling a selection of a filetype action from the
context menu. To avoid disrupting gtk's menu-activation process, we do not run
the action here.
@param item the activated menu item widget
@param cmd pointer to allocated command-string to be executed

@return
*/
static void _e2_context_menu_choose_filetype_action_cb (GtkMenuItem *item,
	const gchar *cmd)
{
	printd (DEBUG, "context menu choose filetype action cb");
	//an idle-callback is no good !?
	g_timeout_add (100, (GSourceFunc) _e2_context_menu_run_filetype_action,
		g_strdup(cmd));
}
/*
static gboolean _e2_context_menu_item_click_cb (GtkWidget *menuitem,
	GdkEventButton *event, E2_ActionRuntime *art)
{
	printd (DEBUG, "item_click_cb (widget:_,event:_,rt:_)");
	if (event->button == 1 && event->type == GDK_BUTTON_PRESS
#ifdef E2_MOUSECUSTOM
		&& (event->state & E2_MODIFIER_MASK) == 0
#endif
		)
	{
		gtk_widget_hide (app.context_menu);
		e2_action_run (menuitem, art);
		e2_action_free_runtime (art);
		return TRUE;
	}
	e2_action_free_runtime (art);
	return FALSE;
}
*/
/**
@brief context-menu "selection-done" signal callback
This will be called _after_ running the action initiated by the selected item !
@param menu the menu widget (= app.context_menu)
@param data data specified when the callback was connected

@return
*/
static void _e2_context_menu_selected_cb (GtkWidget *menu, gpointer data)
{
	printd (DEBUG, "_e2_context_menu_selected_cb, menu: %x", menu);
	//must not destroy the menu immediately, gtk upstream has not necessarily finished with it
	//FIXME delay until action has finished running + 1 sec
#ifdef USE_GLIB2_14
	g_timeout_add_seconds (10,
#else
	g_timeout_add (10000,
#endif
		(GSourceFunc)e2_menu_destroy, menu);
	app.context_menu = NULL;
}

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

/**
@brief populate @a menu with items for the deesktop actions for a file

@param menu the menu widget to which the action menu-items are to be added
@param desktopfilepath path of .desktop file to parse

@return
*/
void e2_context_menu_create_desktop_actions_menu (GtkWidget *menu,
	gchar *desktopfilepath)
{
	GKeyFile *key_file = g_key_file_new ();
	if (g_key_file_load_from_file (key_file, desktopfilepath, G_KEY_FILE_NONE,
		NULL))
	{
		gsize count = 0;
		gchar **actions = g_key_file_get_string_list (key_file,
			"Desktop Entry", "Actions", &count, NULL);
		if (count > 0)
		{
			gchar **iterator;
			for (iterator = actions; *iterator != NULL; iterator++)
			{
				gchar *group;
				group = g_strconcat ("Desktop Action ", *iterator, NULL);
				if (g_key_file_has_group (key_file, group))
				{
					gchar *command;
					command = g_key_file_get_string (key_file, group, "Exec", NULL);
					if (command != NULL)
					{
						gchar *label;
						GtkWidget *item;
						label = g_key_file_get_string (key_file, group, "Name", NULL);
						if (label == NULL)
							label = command;
						item = e2_menu_add (menu, label, NULL, NULL,
							_e2_context_menu_choose_filetype_action_cb, command);
						//cleanup during menu destruction
						g_object_set_data_full (G_OBJECT(item), "action-cmd-key",
								command, g_free);
						if (label != command)
							g_free (label);
					}
				}
				g_free (group);
			}
			g_strfreev (actions);
		}
	}
	g_key_file_free (key_file);
}
/**
@brief populate @a menu with items for the actions for a filetype
Each member of @a actions is like "command" or "label@command"
@param menu the menu widget to which the action menu-items are to be added
@param actions NULL-terminated array of utf8 strings, each a command for a filetype

@return
*/
void e2_context_menu_create_filetype_actions_menu (GtkWidget *menu,
	const gchar **actions)
{
	gchar *s;
	GtkWidget *menu_item;

	while (*actions != NULL)
	{
		if ((s = strchr (*actions, '@')) != NULL)  //if always ascii @, don't need g_utf8_strchr()
		{
			*s = '\0';
			menu_item = e2_menu_add (menu, (gchar *)*actions, NULL, NULL,
				_e2_context_menu_choose_filetype_action_cb, s+1);
			*s = '@';	//revert to original form (this is the 'source' data)
			s++;	//point to command
		}
		else
		{
			s = (gchar *)*actions;
			menu_item = e2_menu_add (menu, s, NULL, NULL,
				_e2_context_menu_choose_filetype_action_cb, s);
		}
		//some open-with action code needs the command, from the menu item
		g_object_set_data (G_OBJECT(menu_item), "action-cmd-key", s);
		actions++;
	}
}
/**
@brief process a filetypes item in @a menu
This adds menu-items for a filetype to @a menu
@param menu the menu widget to which the menu-items are to be added

@return
*/
void e2_context_menu_add_filetype_item (GtkWidget *menu)
{
	gboolean exec, freeext = TRUE;
	gchar *ext, *ext2;
	const gchar **actions = NULL;
	FileInfo *info;
	GtkTreeModel *mdl = curr_view->model;
	GtkTreeIter iter;
	if (gtk_tree_model_iter_nth_child (mdl, &iter, NULL, curr_view->row))
		gtk_tree_model_get (mdl, &iter, FINFO, &info, -1);
	else
		return;

	if (e2_fs_is_dir (info, curr_view))
	{
		exec = FALSE;	//no special treatment for dirs with X permission
		ext = g_strconcat (".", _("<directory>"), NULL);
	}
	else
	{
		//this is a tougher exec test than used in context menu
//		exec = e2_fs_is_executable (info, curr_view); //runs 'file' which prompts a refresh
		gchar *localpath = e2_utils_dircat (curr_view, info->filename, TRUE);
#ifdef E2_VFS
#ifdef E2_VFSTMP
		//CHECKME allow exec options for non-local items ?
#endif
		VPATH ddata = { localpath, NULL };
		if (curr_view->spacedata != NULL)
			exec = FALSE;//CHECKME commands for non-local items ?
		else
			exec = e2_fs_is_exec2 (&ddata E2_ERR_NONE());
#else
		exec = e2_fs_is_exec2 (localpath E2_ERR_NONE());
#endif
		g_free (localpath);
		freeext = FALSE;
/*		//FIXME generalise detection of hidden items
		if (!e2_fs_is_hidden (&ddata, E2_ERR_NONE()))
			ext = info->filename; //look for extension from 1st byte
//USELESS else if (*info->filename != '.')
//			ext = info->filename;
//UNREAL else if (*info->filename == '\0')
//			ext = info->filename;
		else // *info->filename == '.', a *NIX-kind of hidden file
			ext = info->filename + sizeof (gchar); //look for extension from 2nd byte
*/
		ext = info->filename;
		if (*ext == '.')
			ext++;
		//assumes extension is that part of the name after the leftmost '.'
		ext = strchr (ext, '.');
		if (ext == NULL || *(ext + sizeof (gchar)) == '\0') //no extension
		{
			if (/*!exec &&*/ e2_fs_is_text (
#ifdef E2_VFS
				&ddata
#else
				info->filename
#endif
				E2_ERR_NONE())) //runs 'file' again!!
				//fake text extension
				//too bad if this is not a recognised text extension in filetypes
				ext = ".txt";
		}
		else //(ext != NULL && *(ext + sizeof (gchar)) != '\0'
		{
			ext2 = ext;
			ext = F_DISPLAYNAME_FROM_LOCALE (ext);	//get utf
			if (ext != ext2)	//conversion actually happened
				freeext = TRUE;	//so free ext before exit
		}
	}

	if (ext != NULL)
	{
		ext2 = ext;	//remember what to free, if need be
		//check all possible extensions for a matching filetype
		do
		{
			//skip leading dot "."
			ext += sizeof (gchar);	//ascii '.' always single char
			actions = e2_filetype_get_actions (ext);
			if (actions != NULL)
			{
				e2_context_menu_create_filetype_actions_menu (menu, actions);
				break;
			}
		} while ((ext = strchr (ext, '.')) != NULL);	//always ascii '.', don't need g_utf8_strchr()

		if (freeext)
			g_free (ext2);
	}

	if (exec)
	{
		//add exec-filetype items unless item has been found in that type already
		const gchar **acts2 = e2_filetype_get_actions (_("<executable>"));
		if (actions != NULL //was a matching extension
			&& actions != acts2 && acts2 != NULL) //CHECKME this test
				e2_context_menu_create_filetype_actions_menu (menu, acts2);
		else if (acts2 != NULL)
			e2_context_menu_create_filetype_actions_menu (menu, acts2);

		//if appropriate, get desktop entry actions and add them too
		gchar *path, *localpath;
		gchar *localname = e2_utils_strcat (info->filename, ".desktop");
		const gchar* const *sysdir = g_get_system_data_dirs ();
		while (*sysdir != NULL)
		{
			path = g_build_filename (*sysdir, "applications", localname, NULL);
			localpath = F_FILENAME_TO_LOCALE (path); //probably redundant
#ifdef E2_VFS
			VPATH data = { localpath, NULL };	//always local
			if (!e2_fs_access (&data, R_OK E2_ERR_NONE()))
#else
			if (!e2_fs_access (localpath, R_OK E2_ERR_NONE()))
#endif
			{
				e2_context_menu_create_desktop_actions_menu (menu, localpath);
				g_free (path);
				F_FREE (localpath, path);
				break;
			}
			g_free (path);
			F_FREE (localpath, path);
			sysdir++;
		}
		g_free (localname);
	}
}
/**
@brief populate destroyable context-menu @a menu
@param menu the context-menu widget
@param model model for context-menu data treestore
@param iter pointer to iter used to interrogate @a model
@param level the current depth in @a model
@param mtype enumerator for type of menu, 0=full ... 3=plugins only

@return
*/
static void _e2_context_menu_add_items (GtkWidget *menu, GtkTreeModel *model,
	GtkTreeIter *iter, gint mtype)
{
	gboolean selected =
		(gtk_tree_selection_count_selected_rows (curr_view->selection) > 0);
	if (mtype == 3)
	{	// shift+ctrl menu
		e2_menu_create_plugins_menu (menu, TRUE, selected);
		return;
	}
	gchar *prefix = g_strconcat (_A(6), ".", NULL);
	do
	{
		gchar *label, *icon, *command, *arg;
		gboolean _mtype;
		switch (mtype)
		{
			case 1:
				// shift menu
				gtk_tree_model_get (model, iter, 0, &label, 1, &icon, 2, &_mtype, 4, &command, 5, &arg, -1);
				break;
			case 2:
				// ctrl menu
				gtk_tree_model_get (model, iter, 0, &label, 1, &icon, 3, &_mtype, 4, &command, 5, &arg, -1);
				break;
			default:
				gtk_tree_model_get (model, iter, 0, &label, 1, &icon,             4, &command, 5, &arg, -1);
				break;
		}
		if (selected || !g_str_has_prefix (command, prefix))
		{
			if ((mtype != 0) && !_mtype)   //CHECKME should be _mtype
			{
				GtkTreeIter iter2;
				if (gtk_tree_model_iter_children (model, &iter2, iter))
					//recurse
					_e2_context_menu_add_items (menu, model, &iter2, mtype);
			}
			else
			{
				gchar *realarg;
				E2_Action *action = e2_action_get_with_custom (command, arg, &realarg);
				switch (action->type)
				{
					case E2_ACTION_TYPE_SUBMENU:
					{
						GtkWidget *submenu;
						if (arg == NULL || *arg == '\0') //no data == not a custom sub-menu
						{
							GtkTreeIter iter2;
							if (gtk_tree_model_iter_children (model, &iter2, iter))
							{
								submenu = gtk_menu_new ();
								//recurse
								_e2_context_menu_add_items (submenu, model, &iter2, mtype);
							}
							else
								submenu = NULL;
						}
						else	//custom submenu
							submenu = e2_menu_create_custom_menu (arg);	//may be NULL

//						GtkWidget *item = e2_menu_add (menu, label, icon, NULL, NULL, NULL);
						if (submenu != NULL)
						{
							GList *children = gtk_container_get_children (GTK_CONTAINER (submenu));
							if (g_list_length (children) == 0)
								gtk_widget_destroy (submenu);
							else
							{
								GtkWidget *item = e2_menu_add (menu, label, icon, NULL, NULL, NULL);
								gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
							}
							g_list_free (children);
						}
					}
						break;
					case E2_ACTION_TYPE_BOOKMARKS:
					{
						GtkTreeIter iter;
						gint pane = E2PANECUR;	//CHECKME always ok for context menu ? action may be specific
						E2_OptionSet *set = e2_option_get ("bookmarks");
						if (arg != NULL && *arg != '\0')
						{
							if (e2_tree_find_iter_from_str (set->ex.tree.model, 0, arg, &iter, TRUE))
							{
								GtkTreeIter iter2;
								if (gtk_tree_model_iter_children (set->ex.tree.model, &iter2, &iter))
									e2_menu_add_bookmark_items (menu, TRUE, action,
										pane, set->ex.tree.model, &iter2);
							}
						}
						else if (gtk_tree_model_get_iter_first (set->ex.tree.model, &iter))
								e2_menu_add_bookmark_items (menu, TRUE, action,
									pane, set->ex.tree.model, &iter);
					}
						break;
					case E2_ACTION_TYPE_HISTORY:
					{
						GtkWidget *item = e2_menu_add (menu, label, icon, NULL, NULL, NULL);
						E2_PaneRuntime *rt = e2_pane_get_runtime (curr_view->treeview,
							arg, NULL);
						GtkWidget *submenu = e2_pane_visited_menu (rt);
						gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
					}
					break;
					case E2_ACTION_TYPE_FILE_ACTIONS:
						e2_context_menu_add_filetype_item (menu);
						break;
					case E2_ACTION_TYPE_PLUGINS:
						e2_menu_create_plugins_menu (menu, TRUE, selected);
						break;
					case E2_ACTION_TYPE_SEPARATOR:
					{
						GList *children = gtk_container_get_children (GTK_CONTAINER (menu));
						if (g_list_length (children) > 0)
							e2_menu_add_separator (menu);
						g_list_free (children);
					}
						break;
					case E2_ACTION_TYPE_TEAR_OFF_MENU:
						e2_menu_add_tear_off (menu);
						break;
					case E2_ACTION_TYPE_TOGGLE:
						e2_menu_add_toggle (menu, TRUE, label, icon, NULL, command, arg);
						break;
					case E2_ACTION_TYPE_ITEM:
					{
						E2_ActionRuntime *art;
						art = e2_action_pack_runtime (action, realarg, g_free);
						if (art != NULL)
						{
							//note: callback relates to destroyable menu
							GtkWidget *item = e2_menu_add (menu, label, icon, NULL,
								e2_menu_action_activated_cb, art);
							g_object_set_data_full (G_OBJECT(item), "e2-free-actruntime",
								art, (GDestroyNotify)e2_action_free_runtime);
							//FIXME add tip
						}
					}
						break;
					default:
						break;
				}
				if (action->type != E2_ACTION_TYPE_ITEM)
					g_free (realarg);
			}
		}
		//cleanup
		g_free (label);
		g_free (icon);
		g_free (command);
		g_free (arg);
	} while (gtk_tree_model_iter_next (model, iter));
	g_free (prefix);
}

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

/**
@brief create and pop up destroyable context-menu for a filelist pane

@param button initiator enumerator, 0-menu-key or action, 1=L etc
@param time time at which event was triggered
@param type menu type enumerator 0=full menu, 1=Shft, 2=Ctrl, 3=Shft+Ctrl

@return
*/
void e2_context_menu_show (guint button, guint32 time, gint type)
{
//	FileInfo *info;
//FIXME if we need to disable refresh during the popup,
// make all menu choices depart via a fn that enables refresh again
//#ifdef E2_REFRESH_DEBUG
//	printd (DEBUG, "disable refresh, context menu show");
//#endif
//	e2_filelist_disable_refresh();

//	if (curr_view->tagged != NULL)
//		info = curr_view->tagged->data;
//	else
//	{
		GtkTreeIter iter;
/* show menu even with empty dir
	if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (curr_view->store),
			&iter, NULL, curr_view->row))
			gtk_tree_model_get (GTK_TREE_MODEL (curr_view->store), &iter, FINFO, &info, -1);
		else
		{
			printd (WARN, "missing model row");
			return;
		} */
//	}
	E2_OptionSet *set = e2_option_get ("context-menu");
	if (gtk_tree_model_get_iter_first (set->ex.tree.model, &iter))
	{
		app.context_menu = gtk_menu_new ();
		_e2_context_menu_add_items (app.context_menu, set->ex.tree.model, &iter,
			type);
		g_signal_connect (G_OBJECT (app.context_menu), "selection-done",
			G_CALLBACK (_e2_context_menu_selected_cb), NULL);

		if (button == 0)
			//this was a menu key press or specific action
			gtk_menu_popup (GTK_MENU (app.context_menu), NULL, NULL,
			(GtkMenuPositionFunc) e2_fileview_set_menu_position,
			curr_view->treeview, 0, time);
		else
			//this was a button-3 click
			gtk_menu_popup (GTK_MENU (app.context_menu), NULL, NULL,
			NULL, NULL, button, time);
	}
}
/**
@brief show context-menu action

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

@return TRUE
*/
gboolean e2_context_menu_show_menu_action (gpointer from, E2_ActionRuntime *art)
{
	const gchar *arg = NULL;
	E2_PaneRuntime *rt = e2_pane_get_runtime (from, art->data, &arg);
	if (rt == NULL)
		return FALSE;
	//swap active pane if needed
	if (rt != curr_pane)
	{
		if (GTK_IS_WIDGET (app.context_menu))
		{
			e2_menu_selection_done_cb (app.context_menu, NULL);
			app.context_menu = NULL;
		}
		e2_pane_activate_other ();
	}
	gint type = 0;
	if (ACTION_BUTTON (art,1) || ACTION_BUTTON (art,3))
	{
		if (ACTION_MASK (art,GDK_SHIFT_MASK))
			type = 1;
		else if (ACTION_MASK (art,GDK_CONTROL_MASK))
			type = 2;
//		else if (ACTION_MASK (art,GDK_MOD1_MASK))
//			type = ?;
	}
	if (type == 0 && arg != NULL)
	{
		gchar *tmp = g_strdup (arg);
		g_strstrip (tmp);
		if (!strcmp (tmp, _A(117)))	//_(shift
			type = 1;
		else if (!strcmp (tmp, _A(110)))	//_(ctrl
			type = 2;
		//CHECKME alt option here ?
		g_free (tmp);
	}
	e2_context_menu_show (0, 0, type);

	return TRUE;
}
/**
@brief install default tree options for context menu
This function is called only if the default is missing from the config file
@param set pointer to set data
@return
*/
static void _e2_context_menu_tree_defaults (E2_OptionSet *set)
{
	e2_option_tree_setup_defaults (set,
	g_strdup("context-menu=<"),  //internal name
	g_strconcat("||false|false|",_A(6),".",_A(23),"|",NULL),
	g_strconcat(_("Open _with.."),"|open_with"E2ICONTB"|false|false|",
		_A(6),".",_A(66),"|",NULL),
	g_strconcat(_("_View"),"|view"E2ICONTB"|false|false|",
		_A(6),".",_A(106),"|",NULL),
	g_strconcat(_("_Info"),"|gtk-dialog-info|false|false|",_A(6),".",_A(55),"|",NULL),
	g_strconcat("||false|false|",_A(21),"|",NULL),
	g_strconcat(_("_Actions"),"|gtk-execute|false|false|",_A(22),"|",NULL),
	g_strconcat("\t",_("_Copy"),"|gtk-copy|true|false|",_A(6),".",_A(36),"|",NULL),
	g_strconcat("\t",_("_Move"),"|move"E2ICONTB"|true|false|",_A(6),".",_A(62),"|",NULL),
	g_strconcat("\t",_("_Link"),"|symlink"E2ICONTB"|true|false|",_A(6),".",_A(96),"|",NULL),
	g_strconcat("\t",_("_Trash"),"|trash"E2ICONTB"|true|false|",_A(6),".",_A(18),"|",NULL),
	g_strconcat("\t","<span foreground='red'>",_("_Delete"),"</span>|delete"E2ICONTB"|true|false|",
		_A(6),".",_A(42),"|",NULL),
	g_strconcat("\t",_("_Rename.."),"|gtk-convert|true|false|",_A(6),".",_A(76),"|",NULL),
	g_strconcat("\t",_("Change _owners.."),"|owners"E2ICONTB"|true|false|",
		_A(6),".",_A(67),"|",NULL),
	g_strconcat("\t",_("Change _permissions.."),"|permissions"E2ICONTB"|true|false|",
		_A(6),".",_A(70),"|",NULL),
	g_strconcat("\t",_("Copy as.."),"|copy_as"E2ICONTB"|true|false|",_A(6),".",_A(37),"|",NULL),
	g_strconcat("\t",_("Move as.."),"|move_as"E2ICONTB"|true|false|",_A(6),".",_A(63),"|",NULL),
	g_strconcat("\t",_("Link as.."),"|symlink_as"E2ICONTB"|true|false|",_A(6),".",_A(97),"|",NULL),
	g_strconcat(_("_Plugins"),"|gtk-index|false|false|",_A(22),"|",NULL),
	g_strconcat("\t||false|false|",_A(16),".",_A(27),"|",NULL),
	g_strconcat("\t||false|false|",_A(21),"|",NULL),
	g_strconcat("\t",_("_Edit plugins.."),"|gtk-preferences|false|false|",_A(3),".",_C(34),"|",NULL),
	g_strconcat(_("_User commands"),"|user_commands"E2ICONTB"|false|false|",_A(22),"|",NULL),
	g_strconcat("\t",_("_Make new file.."),"|gtk-new|false|true|touch|'%{",_("Enter file name:"),"}'",NULL),	//_A(20)
	g_strconcat("\t",_("_Compare files"),"||false|true|>cmp|-s %f %F && echo \"",_("The files are identical"),"\"\\n \\|\\| echo \"",_("The files are different"),"\"\\n",NULL),  	//_A(20)
	g_strconcat("\t",_("Compare _directories"),"||false|true|diff|%d %D",NULL), 	//_A(20)
//	g_strconcat("\t",_("_Easytag"),"||false|true|easytag|%d &",NULL),  //_A(20) not common enough to be a default
	g_strconcat("\t",_("_Remove spaces"),"||false|true|>mv|%f `echo %f \\| sed -e 's/ //g'` 2>/dev/null &",NULL), 	//_A(20)
	g_strconcat("\t",_("_Split file.."),"||false|true|split|-b %{",_("Enter the piece-size (in kB):"),"}k %f %f_",NULL), //_A(20)
	g_strconcat("\t",_("Co_ncatenate files.."),"||false|true|cat|%f > '%{",_("Enter the name of the combined file:"),"}'",NULL), //_A(20)
	g_strconcat("\t",_("Show _usage"),"||false|true|du|-bs %f",NULL),
	g_strconcat("\t",_("_Free space"),"||false|true|>stat|-f %d \\| awk '/Blocks/ {printf \"%2.1f ", _("percent free"),"\",$5/$3*100}'",NULL),  //_A(20)
	g_strconcat("\t||false|true|",_A(21),"|",NULL),
	g_strconcat("\t",_("_Edit user commands.."),"|gtk-preferences|false|true|",_A(3),".",_A(32),"|",_C(8),NULL),
	g_strconcat(_("Ma_ke dir.."),"|gtk-open|false|false|",_A(1),".",_A(60),"|",NULL),  //same _ as main toolbar
	g_strconcat("||false|false|",_A(21),"|",NULL),
	g_strconcat(_("_Bookmarks"),"|bookmark"E2ICONTB"|false|false|",_A(22),"|",NULL),
	g_strconcat("\t",_("Add _top"),"|add_mark_top"E2ICONTB"|false|false|",_A(0),".",_A(30),"|",_A(119),NULL),
	g_strconcat("\t||false|false|",_A(0),".",_A(27),"|",NULL),
	g_strconcat("\t",_("Add _bottom"),"|add_mark_bottom"E2ICONTB"|false|false|",_A(0),".",_A(30),"|",NULL),
	g_strconcat("\t||false|false|",_A(21),"|",NULL),
	g_strconcat("\t",_("_Edit bookmarks.."),"|gtk-preferences|false|false|",_A(3),".",_C(1),"|",NULL),
	g_strconcat(_("_History"),"|gtk-jump-to|false|false|",_A(8),".",_A(27),"|",NULL),
	g_strconcat("||false|false|",_A(21),"|",NULL),
	g_strconcat(_("_Edit filetype.."),"|gtk-preferences|false|false|",_A(3),".",_A(45),"|",NULL),
	g_strdup(">"),	//allow this to be freed
	NULL);
}
/**
@brief register context-menu option

@return
*/
void e2_context_menu_options_register (void)
{
	//this option does not require a rebuild after config change
	gchar *group_name = g_strconcat (_C(33), ".", _C(8), NULL);
	E2_OptionSet *set = e2_option_tree_register ("context-menu", group_name, _C(8),  //_("context menu"
		NULL, NULL, NULL, E2_OPTION_TREE_UP_DOWN | E2_OPTION_TREE_ADD_DEL,
		E2_OPTION_FLAG_ADVANCED | E2_OPTION_FLAG_FREEGROUP);
	e2_option_tree_add_column (set, _("Label"), E2_OPTION_TREE_TYPE_STR, 0, "",
		0, NULL, NULL);
	e2_option_tree_add_column (set, _("Icon"), E2_OPTION_TREE_TYPE_ICON, 0, "",
		0, NULL, NULL);
	//"false" is public name, but is widely used internally, not translated
	e2_option_tree_add_column (set, _("Shift"), E2_OPTION_TREE_TYPE_BOOL, FALSE,"false",
		0, NULL, NULL);
	e2_option_tree_add_column (set, _("Ctrl"), E2_OPTION_TREE_TYPE_BOOL, FALSE,"false",
		0, NULL, NULL);
	e2_option_tree_add_column (set, _("Action"), E2_OPTION_TREE_TYPE_SEL, 0, _A(21),
		0, NULL,
		GINT_TO_POINTER (E2_ACTION_EXCLUDE_GENERAL | E2_ACTION_EXCLUDE_MENU));
	e2_option_tree_add_column (set, _("Argument"), E2_OPTION_TREE_TYPE_SEL, 0, "",
		0, NULL,
		GINT_TO_POINTER (E2_ACTION_EXCLUDE_GENERAL | E2_ACTION_EXCLUDE_MENU
		| E2_ACTION_EXCLUDE_TOGGLE));
	e2_option_tree_create_store (set);

	e2_option_tree_prepare_defaults (set, _e2_context_menu_tree_defaults);
}
