/* $Id: e2_context_menu.c 853 2008-04-07 10:38:17Z tpgww $

Copyright (C) 2003-2008 tooar <tooar@gmx.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, contact the Free Software
Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* 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 filetype tasks menu
This is the callback for handling a selection of a filetype action from
the context menu
@param widget the selected menu item widget

@return
*/
static void _e2_context_menu_choose_filetype_action_cb (GtkWidget *widget)
{
	printd (DEBUG, "context menu choose filetype action cb");
	e2_filetype_exec_action ((gchar *) gtk_widget_get_name (widget));
}
/*
static gboolean _e2_context_menu_item_click_cb (GtkWidget *widget,
	GdkEventButton *event, E2_ActionRuntime *rt)
{
	printd (DEBUG, "item_click_cb (widget:_,event:_,rt:_)");
	if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
	{
		e2_action_run (widget, rt);
		return TRUE;
	}
	return FALSE;
}
*/
/**
@brief context-menu destroy callback

@param menu the menu widget (= app.context_menu)
@param data data specified when the callback was connected

@return
*/
static void _e2_context_menu_destroy_menu_cb (GtkWidget *menu, gpointer data)
{
	printd (DEBUG, "destroy_menu_cb");
//	printd (DEBUG, "destroy_menu_cb, refresh ref = %d", refresh_refcount);
	//CHECKME is this specific destruction redundant ?
	gtk_container_foreach (GTK_CONTAINER (menu), (GtkCallback) gtk_widget_destroy, NULL);
	gtk_widget_destroy (menu);
	//no enable refresh, here as it is done somewhere else!!
//#ifdef E2_REFRESH_DEBUG
//	printd (DEBUG, "enable refresh, destroy_menu_cb");
//#endif
//	e2_filelist_enable_refresh();
	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 *group, *label, *command;
			gchar **iterator;
			for (iterator = actions; *iterator != NULL; iterator++)
			{
				group = g_strconcat ("Desktop Action ", *iterator, NULL);
				if (g_key_file_has_group (key_file, group))
				{
					command = g_key_file_get_string (key_file, group, "Exec", NULL);
					if (command != NULL)
					{
						label = g_key_file_get_string (key_file, group, "Name", NULL);
						if (label == NULL)
							label = command;
						GtkWidget *menu_item = e2_menu_add (menu, label, NULL, NULL,
							_e2_context_menu_choose_filetype_action_cb, NULL);
						gtk_widget_set_name (GTK_WIDGET(menu_item), command);
						if (label != command)
							g_free (label);
						g_free (command);
					}
				}
				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 *sep;
	GtkWidget *menu_item;

	while (*actions != NULL)
	{
		if ((sep = strchr (*actions, '@')) != NULL)  //if always ascii @, don't need g_utf8_strchr()
		{
			*sep = '\0';
			menu_item = e2_menu_add (menu, (gchar *)*actions, NULL, NULL,
				_e2_context_menu_choose_filetype_action_cb, NULL);
			*sep = '@';	//revert to original form (this is the 'source' data)
			gtk_widget_set_name (menu_item, sep+1);
		}
		else
		{
			menu_item = e2_menu_add (menu, (gchar *)*actions, NULL, NULL,
				_e2_context_menu_choose_filetype_action_cb, NULL);
			gtk_widget_set_name (menu_item, *actions);
		}
		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
*/
static 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_access (&ddata, X_OK E2_ERR_NONE());
#else
		exec = !e2_fs_access (localpath, X_OK E2_ERR_NONE());
#endif
		g_free (localpath);
		freeext = FALSE;
		//assumes extension is that part of the name after the leftmost '.'
		ext = strchr (info->filename, '.');	//localised text, but assumes . is ascii
		if (ext == info->filename //it's a dot file
			|| (ext == NULL //no extension
#ifdef E2_VFS
				&& e2_fs_is_text (&ddata E2_ERR_NONE()))) //runs 'file' again!!
#else
				&& e2_fs_is_text (info->filename E2_ERR_NONE()))) //runs 'file' again!!
#endif
			//fake text extension
			ext = ".txt";	//too bad if this is not a recognised text extension in filetypes
		else if (ext != NULL)
		{
			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++;	//NCHR(ext); 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);
				break;
			}
			g_free (path);
			F_FREE (localpath);
			sysdir++;
		}
		g_free (localname);
	}
}
/**
@brief populate 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, selected);
		return;
	}
	gchar *prefix = g_strconcat (_A(5), ".", 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 *item = e2_menu_add (menu, label, icon, NULL, NULL, NULL);
						GtkWidget *menu2;
						if (arg == NULL || *arg == '\0') //no data == not a custom sub-menu
						{
							GtkTreeIter iter2;
							if (gtk_tree_model_iter_children (model, &iter2, iter))
							{
								menu2 = gtk_menu_new ();
								//recurse
								_e2_context_menu_add_items (menu2, model, &iter2, mtype);
							}
							else
								menu2 = NULL;
						}
						else	//custom submenu
							menu2 = e2_menu_create_custom_menu (arg);	//may be NULL

						if (menu2 != NULL)
						{
							GList *children = gtk_container_get_children (GTK_CONTAINER (menu2));
							if (g_list_length (children) == 0)
							{
								gtk_widget_destroy (item);
								gtk_widget_destroy (menu2);
							}
							else
								gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu2);
							g_list_free (children);
						}
						else
							gtk_widget_destroy (item);
					}
						break;
					case E2_ACTION_TYPE_BOOKMARKS:
					{
						E2_OptionSet *set = e2_option_get ("bookmarks");
						GtkTreeIter iter;
						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_create_bookmarks_menu (action, set, menu, &iter2);
							}
						} else if (gtk_tree_model_get_iter_first (set->ex.tree.model, &iter))
								e2_menu_create_bookmarks_menu (action, set, menu, &iter);
					}
						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, 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, label, icon, "", command, arg);
						break;
					case E2_ACTION_TYPE_ITEM:
					{
						E2_ActionRuntime *actionrt = e2_action_pack_runtime (action, realarg, g_free);
						GtkWidget *item;
						//FIXME add tips
						item = e2_menu_add (menu, label, icon, NULL, e2_action_run,
								actionrt);
						g_object_set_data_full (G_OBJECT (item), "free-callback-data", actionrt,
							(GDestroyNotify) e2_action_free_runtime);
					}
						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 show main (filepane) context menu

@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_destroy_menu_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)
{
	gint type = 0;
	gchar *arg = (gchar *)art->data;
	if (arg != NULL)
	{
		g_strstrip (arg);
		if (g_str_equal (arg, _A(112)))	//_(shift
			type = 1;
		else if (g_str_equal (arg, _A(105)))	//_(ctrl
			type = 2;
		//CHECKME alt option here ?
	}
	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(5),".",_A(20),"|",NULL),
	g_strconcat(_("Open _with.."),"|open_with_"E2IP".png|false|false|",
		_A(5),".",_A(62),"|",NULL),
	g_strconcat(_("_View"),"|view_"E2IP".png|false|false|",
		_A(5),".",_A(101),"|",NULL),
	g_strconcat(_("_Info"),"|gtk-dialog-info|false|false|",_A(5),".",_A(52),"|",NULL),
	g_strconcat("||false|false|",_A(18),"|",NULL),
	g_strconcat(_("_Actions"),"|gtk-execute|false|false|",_A(19),"|",NULL),
	g_strconcat("\t",_("_Copy"),"|gtk-copy|true|false|",_A(5),".",_A(33),"|",NULL),
	g_strconcat("\t",_("_Move"),"|move_"E2IP".png|true|false|",_A(5),".",_A(58),"|",NULL),
	g_strconcat("\t",_("_Link"),"|symlink_"E2IP".png|true|false|",_A(5),".",_A(91),"|",NULL),
	g_strconcat("\t",_("_Trash"),"|trash_"E2IP".png|true|false|",_A(5),".",_A(97),"|",NULL),
	g_strconcat("\t","<span foreground='red'>",_("_Delete"),"</span>|delete_"E2IP".png|true|false|",
		_A(5),".",_A(38),"|",NULL),
	g_strconcat("\t",_("_Rename.."),"|gtk-convert|true|false|",_A(5),".",_A(73),"|",NULL),
	g_strconcat("\t",_("Change _owners.."),"|owners_"E2IP".png|true|false|",
		_A(5),".",_A(63),"|",NULL),
	g_strconcat("\t",_("Change _permissions.."),"|permissions_"E2IP".png|true|false|",
		_A(5),".",_A(67),"|",NULL),
	g_strconcat("\t",_("Copy as.."),"|copy_as_"E2IP".png|true|false|",_A(5),".",_A(34),"|",NULL),
	g_strconcat("\t",_("Move as.."),"|move_as_"E2IP".png|true|false|",_A(5),".",_A(59),"|",NULL),
	g_strconcat("\t",_("Link as.."),"|symlink_as_"E2IP".png|true|false|",_A(5),".",_A(92),"|",NULL),
	g_strconcat(_("_Plugins"),"|gtk-index|false|false|",_A(19),"|",NULL),
	g_strconcat("\t||false|false|",_A(14),".",_A(24),"|",NULL),
	g_strconcat("\t||false|false|",_A(18),"|",NULL),
	g_strconcat("\t",_("_Edit plugins.."),"|gtk-preferences|false|false|",_A(2),".",_C(33),"|",NULL),
	g_strconcat(_("_User commands"),"|user_commands_"E2IP".png|false|false|",_A(19),"|",NULL),
	g_strconcat("\t",_("_Make new file.."),"|gtk-new|false|true|touch|'%{",_("Enter file name:"),"}'",NULL),	//_A(17)
	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(17)
	g_strconcat("\t",_("Compare _directories"),"||false|true|diff|%d %D",NULL), 	//_A(17)
//	g_strconcat("\t",_("_Easytag"),"||false|true|easytag|%d &",NULL),  //_A(17) 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(17)
	g_strconcat("\t",_("_Split file.."),"||false|true|split|-b %{",_("Enter the piece-size (in kB):"),"}k %f %f_",NULL), //_A(17)
	g_strconcat("\t",_("Co_ncatenate files.."),"||false|true|cat|%f > '%{",_("Enter the name of the combined file:"),"}'",NULL), //_A(17)
	g_strconcat("\t",_("_Free space"),"||false|true|>stat|-f %d \\| awk '/Blocks/ {printf \"%2.1f ", _("percent free"),"\",$5/$3*100}'",NULL),  //_A(17)
	g_strconcat("\t||false|true|",_A(18),"|",NULL),
	g_strconcat("\t",_("_Edit user commands.."),"|gtk-preferences|false|true|",_A(2),".",_A(32),"|",_C(8),NULL),
	g_strconcat(_("Ma_ke dir.."),"|gtk-open|false|false|",_A(1),".",_A(56),"|",NULL),  //same _ as main toolbar
	g_strconcat("||false|false|",_A(18),"|",NULL),
	g_strconcat(_("_Bookmarks"),"|bookmark_"E2IP".png|false|false|",_A(19),"|",NULL),
	g_strconcat("\t",_("Add _top"),"|add_mark_top_"E2IP".png|false|false|",_A(0),".",_A(26),"|",_A(114),NULL),
	g_strconcat("\t||false|false|",_A(0),".",_A(24),"|",NULL),
	g_strconcat("\t",_("Add _bottom"),"|add_mark_bottom_"E2IP".png|false|false|",_A(0),".",_A(26),"|",NULL),
	g_strconcat("\t||false|false|",_A(18),"|",NULL),
	g_strconcat("\t",_("_Edit bookmarks.."),"|gtk-preferences|false|false|",_A(2),".",_C(1),"|",NULL),
	g_strconcat("||false|false|",_A(18),"|",NULL),
	g_strconcat(_("_Edit filetype.."),"|gtk-preferences|false|false|",_A(2),".",_C(15),"|",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(32), ".", _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(18),
		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);
}
