/* $Id: e2_option_tree_context_menu.c 868 2008-05-06 04:42:09Z tpgww $

Copyright (C) 2003-2008 tooar <tooar@gmx.net>
Portions copyright (C) 2004 Florian Zaehringer <flo.zaehringer@web.de>

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
*/

/* 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 set popup menu position

This function is supplied when calling gtk_menu_popup(), to position
the displayed menu.
set @a push_in to TRUE for menu completely inside the screen,
FALSE for menu clamped to screen size

@param menu UNUSED the GtkMenu to be positioned
@param x	place to store gint representing the menu left
@param y  place to store gint representing the menu top
@param push_in place to store pushin flag
@param treeview where the menu is to be shown

@return
*/
static void _e2_option_tree_menu_set_position (GtkMenu *menu,
	gint *x, gint *y, gboolean *push_in, GtkWidget *treeview)
{
	gint left, top;
	gtk_window_get_position (GTK_WINDOW (config_dialog), &left, &top);
	GtkAllocation alloc = treeview->allocation;
	*x = left + alloc.x + alloc.width;
	*y = top + alloc.y + alloc.height/2;
	*push_in = FALSE;
}
/**
@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 (g_str_equal ((*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 (GtkWidget *widget, GtkTreeView *treeview)
{
	gtk_widget_set_sensitive (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);

	e2_tree_expand_all_cb (widget, 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 (GtkWidget *widget, GtkTreeView *treeview)
{
	gtk_widget_set_sensitive (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);

	e2_tree_collapse_all_cb (widget, 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 selected menu widget
@param treeview where the action is to occur

@return
*/
static void _e2_option_tree_menu_copy_cb (GtkWidget *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 selected menu widget
@param treeview widget where the action is to occur

@return
*/
static void _e2_option_tree_menu_paste_cb (GtkWidget *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 selected menu widget
@param treeview widget where the action is to occur

@return
*/
static void _e2_option_tree_menu_cut_cb (GtkWidget *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 treeview context menu items

@param treeview where the action is to occur
@param menu the menu widget to which the items are to be added
@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_menu_items_add (GtkTreeView *treeview,
	GtkWidget *menu, 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"), _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"), _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"), _e2_option_tree_move_up_cb, set);

	n->i = e2_menu_add (menu, _("_Down"), GTK_STOCK_GO_DOWN,
		_("Move selected row down"), _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 *s2 = g_object_get_data (G_OBJECT (treeview), "e2-config-buttons");
	if (s2 == NULL)
		return;
	if (s2->b != NULL)	//up
	{
		state = GTK_WIDGET_SENSITIVE(s2->b);
		if (m->h != NULL)	//up
			gtk_widget_set_sensitive (m->h, state);
	}
	if (s2->c != NULL)	//down
	{
		state = GTK_WIDGET_SENSITIVE(s2->c);
		if (m->i != NULL)	//down
			gtk_widget_set_sensitive (m->i, state);
	}
	if (s2->d != NULL)	//remove
	{
		state = GTK_WIDGET_SENSITIVE(s2->d);
		if (m->a != NULL)	//cut
			gtk_widget_set_sensitive (m->a, state);
		if (m->b != NULL)	//copy
			gtk_widget_set_sensitive (m->b, state);
	}
	if (s2->e != NULL)	//add
	{
		state = GTK_WIDGET_SENSITIVE(s2->e);
		if (m->f != NULL)	//add
			gtk_widget_set_sensitive (m->f, state);
		}
	if (s2->f != NULL)	//add child
	{
		state = GTK_WIDGET_SENSITIVE(s2->f);
		if (m->g != NULL)	//add child
			gtk_widget_set_sensitive (m->g, 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
