/* $Id$ Copyright (C) 2003-2010 tooar Portions copyright (C) 2004 Florian Zaehringer 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. */ /* Florian Zaehringer is the source of inspiration, and some of the code, here */ /** @file src/config/e2_option_tree_context_menu.c @brief context menu functions for config-dialog tree options This file is #included in e2_option_tree.c (i.e. this won't rebuild unless that is rebuilt) */ //prevent building of separate object file #ifdef INCLUDED_IN_PARENT /*******************/ /***** private *****/ /*******************/ /** @brief create treeview signature This gets the name of the current option. That string is used as a signature to confirm the correct treeview, to enable pasting @param treeview treeview to which the menu belongs @return */ static const gchar *_e2_option_tree_menu_get_signature (GtkTreeView *treeview) { GtkTreeModel *model = gtk_tree_view_get_model (treeview); E2_OptionSet *set = g_object_get_data (G_OBJECT (model), "e2-option-set"); //all toolbars have same data format, so their data are interchangeable E2_ToolbarData **thisbar = app.bars; while (*thisbar != NULL) { if (!strcmp ((*thisbar)->name, set->name)) return "toolbar"; thisbar++; } return (set->name); } /** @brief tree expand-all callback @param widget the menu item widget which activated the callback @param treeview the treeview to be collapsed @return */ static void _e2_option_tree_expand_all_cb (GtkMenuItem *widget, GtkTreeView *treeview) { gtk_widget_set_sensitive (GTK_WIDGET (widget), FALSE); //get the paired item gpointer m = g_object_get_data (G_OBJECT (widget), "e2-menu-pair"); gtk_widget_set_sensitive (GTK_WIDGET (m), TRUE); gtk_tree_view_expand_all (treeview); } /** @brief tree collapse-all callback @param widget the menu item widget which activated the callback @param treeview the treeview to be collapsed @return */ static void _e2_option_tree_collapse_all_cb (GtkMenuItem *widget, GtkTreeView *treeview) { gtk_widget_set_sensitive (GTK_WIDGET (widget), FALSE); //get the paired button gpointer m = g_object_get_data (G_OBJECT (widget), "e2-menu-pair"); gtk_widget_set_sensitive (GTK_WIDGET (m), TRUE); gtk_tree_view_collapse_all (treeview); } /** @brief context-menu copy callback This is called in response to the copy and cut menu items It works on all selected rows, and all their descendants (though robust relation-checks among various selected rows have NOT been implemented, as multiple selection is not currently allowed) A signature string for the treeview and a list of arrays, each with a path string and a whole row contents, are added to a hash table. The table may contain entries for more than 1 treeview @param menu_item UNUSED the activated menu widget @param treeview where the action is to occur @return */ static void _e2_option_tree_menu_copy_cb (GtkMenuItem *menu_item, GtkTreeView *treeview) { GtkTreeSelection *sel = gtk_tree_view_get_selection (treeview); if (gtk_tree_selection_count_selected_rows (sel) > 0) { GList *rowscopied = e2_tree_copy (treeview); const gchar *id_string = _e2_option_tree_menu_get_signature (treeview); printd (DEBUG, "adding to buffer: key %s and %d rows", id_string, g_list_length (rowscopied)); g_hash_table_insert (tree_view_buffer_hash, (gchar*)id_string, rowscopied); //NOT replace } } /** @brief context-menu paste callback An id string for the treeview is first constructed, to ensure that the paste is appropriate Pasted row(s) are placed after the selected row in the destination treeview, as child(ren) if the path depth is appropriate @param menu_item the activated menu widget @param treeview widget where the action is to occur @return */ static void _e2_option_tree_menu_paste_cb (GtkMenuItem *menu_item, GtkTreeView *treeview) { //we only paste after the 1st selected row GtkTreeSelection *sel = gtk_tree_view_get_selection (treeview); if (gtk_tree_selection_count_selected_rows (sel) > 0) { //get data for the current page/view const gchar *id_string = _e2_option_tree_menu_get_signature (treeview); //get the buffered data for the current page GList *copied = g_hash_table_lookup (tree_view_buffer_hash, id_string); if (copied != NULL) { e2_tree_paste (copied, treeview); GtkTreeModel *mdl = gtk_tree_view_get_model (treeview); E2_OptionSet *set = g_object_get_data (G_OBJECT (mdl), "e2-option-set"); e2_option_tree_flag_change (set); } } } /** @brief context-menu cut callback @param menu_item the activated menu widget @param treeview widget where the action is to occur @return */ static void _e2_option_tree_menu_cut_cb (GtkMenuItem *menu_item, GtkTreeView *treeview) { GtkTreeSelection *sel = gtk_tree_view_get_selection (treeview); if (gtk_tree_selection_count_selected_rows (sel) > 0) { _e2_option_tree_menu_copy_cb (menu_item, treeview); e2_tree_delete (treeview); GtkTreeModel *mdl = gtk_tree_view_get_model (treeview); E2_OptionSet *set = g_object_get_data (G_OBJECT (mdl), "e2-option-set"); e2_option_tree_flag_change (set); } } /** @brief add items to treev-option context menu @param menu the menu widget to which the items are to be added @param treeview where the action is to occur @param flags indicator of menu items to be created @param set pointer to data struct for the option that is being edited @return */ static void _e2_option_tree_populate_menu (GtkWidget *menu, GtkTreeView *treeview, E2_TreeContextMenuFlags flags, E2_OptionSet *set) { E2_Nontet *n = e2_utils_nontet_new (); g_object_set_data_full (G_OBJECT (menu), "e2-config-menu", n, (GDestroyNotify) e2_utils_nontet_destroy); if (n == NULL) return; //menu items which modify store content need set for callback data, //other items if ((flags & E2_TREE_CONTEXT_CP_PST) || (flags & E2_TREE_CONTEXT_DEFAULT)) { n->a = e2_menu_add (menu, _("Cu_t"), GTK_STOCK_CUT, _("Cut selected row and any descendant(s)"), _e2_option_tree_menu_cut_cb, treeview); n->b = e2_menu_add (menu, _("_Copy"), GTK_STOCK_COPY, _("Copy selected row and any descendant(s)"), _e2_option_tree_menu_copy_cb, treeview); n->c = e2_menu_add (menu, _("_Paste"), GTK_STOCK_PASTE, _("Paste previously copied or cut row(s) after current row"), _e2_option_tree_menu_paste_cb, treeview); e2_menu_add_separator (menu); } if (flags & E2_TREE_CONTEXT_EXP_COL) { n->d = e2_menu_add (menu, _("_Expand"), GTK_STOCK_ZOOM_IN, _("Expand all rows on this page"), _e2_option_tree_expand_all_cb, treeview); gtk_widget_set_sensitive (GTK_WIDGET(n->d), FALSE); //widget starts with rows expanded n->e = e2_menu_add (menu, _("C_ollapse"), GTK_STOCK_ZOOM_OUT, _("Collapse all rows on this page"), _e2_option_tree_collapse_all_cb, treeview); //set pointers to the paired buttons g_object_set_data (G_OBJECT (n->d), "e2-menu-pair", n->e); g_object_set_data (G_OBJECT (n->e), "e2-menu-pair", n->d); e2_menu_add_separator (menu); //more items added in the calling function } n->f = e2_menu_add (menu, _("_Add"), GTK_STOCK_ADD, _("Add a row after the current one"), (void(*)()) _e2_option_tree_add_below_cb, set); if (flags & E2_TREE_CONTEXT_EXP_COL) n->g = e2_menu_add (menu, _("Add c_hild"), GTK_STOCK_INDENT, _("Add a child to the selected row"), (void(*)()) _e2_option_tree_add_child_cb, set); e2_menu_add_separator (menu); n->h = e2_menu_add (menu, _("_Up"), GTK_STOCK_GO_UP, _("Move selected row up"), (void(*)()) _e2_option_tree_move_up_cb, set); n->i = e2_menu_add (menu, _("_Down"), GTK_STOCK_GO_DOWN, _("Move selected row down"), (void(*)()) _e2_option_tree_move_down_cb, set); } /******************/ /***** public *****/ /******************/ /** @brief (de)sensitize tree-option context-menu items This changes the sensitivity of the paste menu item, depending on whether the current treeview's set name is a key in the hash buffer @param menu the context-menu widget @param treeview widget where the menu was initiated @return */ void e2_option_tree_menu_set_sensitive (GtkWidget *menu, GtkWidget *treeview) { E2_Nontet *m = g_object_get_data (G_OBJECT (menu), "e2-config-menu"); if (m == NULL) return; gboolean state; //first deal with the paste item if (m->c != NULL) { const gchar *id_string = _e2_option_tree_menu_get_signature ( GTK_TREE_VIEW (treeview)); state = (g_hash_table_lookup (tree_view_buffer_hash, id_string) != NULL); //FIXME also check for valid path depth of currently-selected item ? gtk_widget_set_sensitive (m->c, state); } //now adjust other menu items to conform with the page buttons E2_Sextet *st = g_object_get_data (G_OBJECT (treeview), "e2-config-buttons"); if (st == NULL) return; if (st->b != NULL) //up { #ifdef USE_GTK2_18 state = gtk_widget_get_sensitive(st->b); #else state = GTK_WIDGET_SENSITIVE(st->b); #endif if (m->h != NULL) //up gtk_widget_set_sensitive (m->h, state); } if (st->c != NULL) //down { #ifdef USE_GTK2_18 state = gtk_widget_get_sensitive(st->c); #else state = GTK_WIDGET_SENSITIVE(st->c); #endif if (m->i != NULL) //down gtk_widget_set_sensitive (m->i, state); } if (st->d != NULL) //add { #ifdef USE_GTK2_18 state = gtk_widget_get_sensitive(st->d); #else state = GTK_WIDGET_SENSITIVE(st->d); #endif if (m->f != NULL) //add gtk_widget_set_sensitive (m->f, state); } if (st->e != NULL) //add child { #ifdef USE_GTK2_18 state = gtk_widget_get_sensitive(st->e); #else state = GTK_WIDGET_SENSITIVE(st->e); #endif if (m->g != NULL) //add child gtk_widget_set_sensitive (m->g, state); } if (st->f != NULL) //remove { #ifdef USE_GTK2_18 state = gtk_widget_get_sensitive(st->f); #else state = GTK_WIDGET_SENSITIVE(st->f); #endif if (m->a != NULL) //cut gtk_widget_set_sensitive (m->a, state); if (m->b != NULL) //copy gtk_widget_set_sensitive (m->b, state); } } /** @brief free memory used by tree-option context menu hash table entry Each entry in the hash buffer is a glist of value arrays, one array for each row copied to the buffer The first item in an array is the the row's source-view path @param list glist to be freed @return */ void e2_option_tree_menu_hash_clean (GList *list) { GList *tmp; for (tmp = list; tmp != NULL; tmp = tmp->next) { //clear treepath at start of each array GValueArray *value_array = tmp->data; GValue *value = g_value_array_get_nth (value_array, 0); GtkTreePath *path = (GtkTreePath *) g_value_get_pointer (value); gtk_tree_path_free (path); g_value_array_free (value_array); } g_list_free (list); } #endif //def INCLUDED_IN_PARENT