/* $Id$ Copyright (C) 2003-2011 tooar 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 #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 desktop actions for a file @param menu the menu widget to which the action menu-items are to be added @param desktopfilepath path of a .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 (".", _(""), 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 (_("")); 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, menu, action, E2_MARKFLAG_ONETIME, 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, menu, action, E2_MARKFLAG_ONETIME, 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) { //CHECKME if we need to disable refresh during the popup, // make all menu choices depart via a fn that enables refresh again 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","",_("_Delete"),"|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); }