/* $Id: e2_tree.c 639 2007-09-05 03:10:56Z tpgww $

Copyright (C) 2004-2007 tooar <tooar@gmx.net>

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

#include "emelfm2.h"
#include <string.h>
#include "e2_tree.h"

/**
@brief try to find tree iter in @a model, at or after @a iter, which contains string @a search
This is a wrapper for the real search function. It performs several checks
The scan traverses @a model checking each iter and any children, depth-first
@param model treemodel to be interrogated
@param column index of the column in @a model to scan
@param search the string to be searched for
@param iter pointer to treeiter for model interrogation (start and match)
@param with_children TRUE if matched iter must have child(ren)

@return TRUE if the string was found
*/
gboolean e2_tree_find_iter_from_str (GtkTreeModel *model, gint column,
	const gchar *search, GtkTreeIter *iter, gboolean with_children)
{
	if (model == NULL)
		return FALSE;
	gint num_columns = gtk_tree_model_get_n_columns (model);
	if (column > (num_columns - 1))
	{
		printd (WARN, "bad call of e2_tree_find_iter_from_str");
		printd (WARN, "requesting column %d of %d columns", column + 1, num_columns);
		return FALSE;
	}
	if (gtk_tree_model_get_column_type (model, column) != G_TYPE_STRING)
	{
		printd (WARN, "bad call of e2_tree_find_iter_from_str");
		printd (WARN, "column %d is not of type string", column + 1);
		return FALSE;
	}

	if (gtk_tree_model_get_iter_first (model, iter))
		return e2_tree_find_iter_from_str_simple (model, column, search,
			iter, with_children);
	return FALSE;
}
/**
@brief starting from @a iter, check all iters at the same level as @a iter in
  @a model, and all their descendants, to find the first iter which contains string @a search
@a iter is set to matching iter if any, or is unchanged if no match is found
@param model treemodel to be interrogated
@param column index of the column in @a model to scan
@param search the string to be searched for
@param iter pointer to initial treeiter for model interrogation
@param with_children TRUE if matched iter must have child(ren)

@return TRUE if the string was found
*/
gboolean e2_tree_find_iter_from_str_simple (GtkTreeModel *model, gint column,
	const gchar *search, GtkTreeIter *iter, gboolean with_children)
{
	GtkTreeIter backup;
	backup = *iter;
	do
	{
		GtkTreeIter iter2;
		gchar *value;
		gtk_tree_model_get (model, iter, column, &value, -1);
		if (value == NULL)
			continue;
		if (g_str_equal (value, search))
		{
			g_free (value);
			if (with_children)
			{
				if (gtk_tree_model_iter_children (model, &iter2, iter))
					return TRUE;
				else
					continue;
			}
			else
				return TRUE;
		}
		if (gtk_tree_model_iter_children (model, &iter2, iter))
		{
			if (e2_tree_find_iter_from_str_simple (model, column, search,
					&iter2, with_children))
			{
				*iter = iter2;
				g_free (value);
				return TRUE;
			}
		}
		g_free (value);
	} while (gtk_tree_model_iter_next (model, iter));
	*iter = backup;
	return FALSE;
}
/**
@brief starting from @a iter, check all iters at the same level as @a iter in
@a model, to find the first iter which contains string @a search
@a iter is unchanged if no match is found
@param model treemodel to be interrogated
@param column index of the column in @a model to scan
@param search the string to be searched for
@param iter pointer to initial treeiter for model interrogation, set to matching iter

@return TRUE if the string was found
*/
gboolean e2_tree_find_iter_from_str_same (GtkTreeModel *model, gint column,
	const gchar *search, GtkTreeIter *iter)
{
	GtkTreeIter backup;
	backup = *iter;
	do
	{
		gchar *value;
		gtk_tree_model_get (model, iter, column, &value, -1);
		if (value != NULL)
		{
			if (g_str_equal (value, search))
			{
				g_free (value);
				return TRUE;
			}
			g_free (value);
		}
	} while (gtk_tree_model_iter_next (model, iter));
	*iter = backup;
	return FALSE;
}
/**
@brief find tree iter which matches the last "."-separated segment of string @a search
@a search is formatted like level1[.level2.level3 ...] Each "." (if any)
indicates a child of the previous segment
If successful, @a iter is set to the matching iter in the model
@param model treemodel to be interrogated
@param column index of the column in @a model to scan
@param search string describing the iter to be searched for
@param iter pointer to treeiter for model interrogation and result

@return TRUE if the string was found
*/
gboolean e2_tree_find_lowest_iter_from_str (GtkTreeModel *model,
	gint column, const gchar *search, GtkTreeIter *iter)
{
	if (!gtk_tree_model_get_iter_first (model, iter))
		return FALSE;
	gchar **split = g_strsplit (search, ".", -1);
	if (!e2_tree_find_iter_from_str_simple (model, column, split[0], iter, FALSE))
	{
		g_strfreev (split);
		return FALSE;
	}
//FIXME this should continue searching at iterative parent levels whenever
// a match fails, in case there are multiple values
	gboolean retval = TRUE;
	GtkTreeIter child;
	gint i = 1;
	while (split[i] != NULL)
	{
		if (*split[i] == '\0')
		{
			retval = FALSE;	//don't have empty items in the store
			break;
		}
		if (!gtk_tree_model_iter_children (model, &child, iter))
		{
			retval = FALSE;
			break;	//FIXME iterate
		}
		gboolean found = FALSE;
		gchar *value;
		*iter = child;
		do
		{
			gtk_tree_model_get (model, iter, column, &value, -1);
			if (g_str_equal (value, split[i]))
			{
				g_free (value);
				found = TRUE;
				break;
			}
			g_free (value);
		} while (gtk_tree_model_iter_next (model, iter));
		if (!found)
		{
			retval = FALSE;
			break;	//FIXME iterate
		}
		i++;
	}
	g_strfreev (split);
	return retval;
}
#ifdef E2_TREEINCREMENT
/**
@brief create treestore entry which matches @a name
@a name is formatted like level1[.level2.level3 ...] Each "." (if any)
indicates a child of the previous segment
@a iter is set to the newly created iter in the model
@param model treemodel to be interrogated
@param column index of the column in @a model to scan
@param name segmented name string desribing the iter to be created
@param iter pointer to treeiter for model interrogation and result

@return
*/
//this func could be amended a bit to do the job of
// e2_tree_find_lowest_iter_from_str() too
void e2_tree_create_lowest_iter_from_str (GtkTreeModel *model,
	gint column, gchar *name, GtkTreeIter *iter)
{
	gchar **split = g_strsplit (name, ".", -1);
	gint i;
	GtkTreeIter child;
	if (gtk_tree_model_get_iter_first (model, iter)
		&& e2_tree_find_iter_from_str_simple (model, column, split[0], iter, FALSE))
	{	//at least the first level exists
//FIXME this should continue searching at iterative parent levels whenever
// a match fails, in case there are multiple values
		i = 1;
		while (split[i] != NULL)
		{
			if (*split[i] == '\0')
				i++;	//ignore it FIXME eliminate it from the array
			else if (gtk_tree_model_iter_children (model, &child, iter))
			{
				gboolean found = FALSE;
				gchar *value;
				*iter = child;
				do
				{
					gtk_tree_model_get (model, iter, column, &value, -1);
					if (g_str_equal (value, split[i]))
					{
						g_free (value);
						found = TRUE;
						break;
					}
					g_free (value);
				} while (gtk_tree_model_iter_next (model, iter));
				if (!found)
					break;	//FIXME iterate
				i++;
			}
			else
				break;	//we've run out of matching levels	FIXME iterate
		}
		if (split[i] == NULL)
			return;	//we found everything
	}
	else
		//we need to create all of the segments
		i = 0;
	//populate the new row(s)
	GtkTreeStore *store = GTK_TREE_STORE (model);
	while (split[i] != NULL)
	{
		if (i == 0)
			gtk_tree_store_append (store, &child, NULL);
		else
			gtk_tree_store_append (store, &child, iter);
		*iter = child;
		gtk_tree_store_set (store, iter, column, split[i], -1);
		i++;
	}
	g_strfreev (split);
}
#endif	//def E2_TREEINCREMENT
/**
@brief get string in column @a columm in the last row in @a model

@param model treemodel to be interrogated
@param column index of the column in @a model from which to get the return value

@return pointer to newly-allocated copy of the string from the model, or NULL if there's a problem
*/
/* UNUSED
gchar *e2_tree_get_last_string (GtkTreeModel *model, gint column)
{
	if (gtk_tree_model_get_column_type (model, column) != G_TYPE_STRING)
		return NULL;
	GtkTreeIter iter;
/ *FIXME: don't iterate through the whole model to get the last row
	GtkTreePath *path = gtk_tree_path_new_first ();
	while (gtk_tree_model_get_iter (model, &iter, path))
		gtk_tree_path_next (path);
	gtk_tree_path_prev (path);

	gchar *value = NULL;
	if (gtk_tree_model_get_iter (model, &iter, path))
		gtk_tree_model_get (model, &iter, column, &value, -1);
	gtk_tree_path_free (path);
	return value; * /
	GtkTreeIter *parent = NULL;	//initially check root node
	gint children;
	while ((children = gtk_tree_model_iter_n_children (model, parent)) > 0)
	{
		// go to last child at this level
		gtk_tree_model_iter_nth_child (model, &iter, parent, children-1);
		parent = &iter;	//after the root node, we use the actual
	}
	if (parent == NULL)
		return NULL;	//nothing in the model
	gchar *value;
	gtk_tree_model_get (model, &iter, column, &value, -1);
	return value;
} */
/**
@brief determine the number of rows in treestore model @a model
This is mainly for debugging
@param model treemodel to be interrogated

@return the no. of rows
*/
/*guint e2_tree_store_count (GtkTreeModel *model)
{
	GtkTreeIter iter;
	if (!gtk_tree_model_get_iter_first (model, &iter))
		return 0;
	//CHECKME is there a way to start at the real root of the tree ?
	guint count = 0;
	GtkTreePath *path = gtk_tree_path_new_first ();
	while (gtk_tree_model_get_iter (model, &iter, path))
	{
		GNode *rootnode = iter.user_data;
		count += g_node_n_nodes (rootnode, G_TRAVERSE_ALL);
		gtk_tree_path_next (path);
	}
	gtk_tree_path_free (path);
	return count;
} */
/**
@brief create a tree row reference for @a iter in @a store

@param store pointer to treestore
@param iter pointer to treeiter to be referenced

@return the reference
*/
GtkTreeRowReference *e2_tree_iter_to_ref (GtkTreeStore *store, GtkTreeIter *iter)
{
	GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
	GtkTreeRowReference *ref = gtk_tree_row_reference_new
		(GTK_TREE_MODEL (store), path);
	gtk_tree_path_free (path);
	return ref;
}
/**
@brief set @a iter to match @a ref for @a store

@param store pointer to treestore
@param ref pointer to treerow reference
@param iter pointer to treeiter to be set

@return TRUE if @a iter is valid
*/
gboolean e2_tree_ref_to_iter (GtkTreeStore *store, GtkTreeRowReference *ref,
	GtkTreeIter *iter)
{
	if (gtk_tree_row_reference_valid (ref))
	{
		GtkTreePath *path = gtk_tree_row_reference_get_path (ref);
		gboolean retval = gtk_tree_model_get_iter (GTK_TREE_MODEL (store),
			iter, path);
		gtk_tree_path_free (path);
		return retval;
	}
	return FALSE;
}
/**
@brief tree expand-all callback

@param widget UNUSED the widget which activated the callback
@param treeview the treeview to be collapsed

@return TRUE (to prevent further handlers)
*/
gboolean e2_tree_expand_all_cb (GtkWidget *widget, GtkTreeView *treeview)
{
	gtk_tree_view_expand_all (treeview);
	return TRUE;
}
/**
@brief tree collapse-all callback

@param widget UNUSED the widget which activated the callback
@param treeview the treeview to be collapsed

@return TRUE (to prevent further handlers)
*/
gboolean e2_tree_collapse_all_cb (GtkWidget *widget, GtkTreeView *treeview)
{
	gtk_tree_view_collapse_all (treeview);
	return TRUE;
}
/**
@brief find previous iter in a tree model
This is because gtk doesn't have one.
On entry, @a iter is assumed valid
Sets @a iter to point to the node preceding it at the current level.

@param model the tree model being interrogated
@param iter pointer to reference iter

@return TRUE if a prior node was found, and iter was set accordingly
*/
gboolean e2_tree_iter_previous (GtkTreeModel *model, GtkTreeIter *iter)
{
	GtkTreePath *path = gtk_tree_model_get_path (model, iter);
	gboolean retval = gtk_tree_path_prev (path);
	if (retval)
		retval = gtk_tree_model_get_iter (model, iter, path);
	gtk_tree_path_free (path);
	return retval;
}
/**
@brief add treerowreferences for row and its descendants to list

A depth-first search scan creates references for the specified iter and
all its descendants, appending each ref. to a specified glist.
It is recursive, to handle descendants

@param model the treemodel that is being processed
@param iter _pointer_ to tree iter to be processed
@param rowrefs **glist that holds the result

@return
*/
static void _e2_tree_reference_treerow
	(GtkTreeModel *model, GtkTreeIter *iter, GList **rowrefs)
{
	GtkTreePath *path = gtk_tree_model_get_path (model, iter);
	GtkTreeRowReference *ref;
	if ((ref = gtk_tree_row_reference_new (model, path)) != NULL)
		*rowrefs = g_list_append (*rowrefs, ref);
	gtk_tree_path_free (path);

	gint children;
	if ((children = gtk_tree_model_iter_n_children (model, iter)) > 0)
	{
		GtkTreeIter iter2;
		gint n;
		for (n=0; n<children; n++)
		{
			gtk_tree_model_iter_nth_child (model, &iter2, iter, n);
			_e2_tree_reference_treerow (model, &iter2, rowrefs);
		}
	}
}
/**
@brief delete selected row(s), if any, from @a treeview

@param treeview where the action is to occur

@return
*/
void e2_tree_delete (GtkTreeView *treeview)
{
	GtkTreeSelection *sel = gtk_tree_view_get_selection (treeview);
	if (gtk_tree_selection_count_selected_rows (sel) > 0)
	{
		GList *rowrefs = NULL;

		GtkTreeModel *model;
		GList *selpaths = gtk_tree_selection_get_selected_rows (sel, &model);
		GList *tmp;
		//identify a place to goto after the deletion
		//(current place, if we're at the start of the tree)
		GtkTreePath *newpath = gtk_tree_path_copy (selpaths->data);
		if (!gtk_tree_path_prev (newpath))
			if (gtk_tree_path_get_depth (newpath) > 1)
				gtk_tree_path_up (newpath);
		GtkTreePath *path;
		GtkTreeIter iter;

		//create treerowrefs for all selected rows
		for (tmp = selpaths; tmp!=NULL; tmp=tmp->next)
		{
			if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) tmp->data))
				_e2_tree_reference_treerow (model, &iter, &rowrefs);
		}

		for (tmp = rowrefs; tmp!=NULL; tmp=tmp->next)
		{
			if ((path = gtk_tree_row_reference_get_path
				((GtkTreeRowReference *) tmp->data)) != NULL)
			{
				if (gtk_tree_model_get_iter (model, &iter, path))
				{
					//gtk_tree_selection_unselect_path (sel, path);	//needed ?
					//this deletes all children too
					gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
				}
			}
		}

		//go to the new place
		if (!gtk_tree_model_get_iter (model, &iter, newpath)
			&& gtk_tree_model_get_iter_first (model, &iter))
		{
			gtk_tree_path_free (newpath);
			newpath = gtk_tree_model_get_path (model, &iter);
		}
		gtk_tree_view_set_cursor (treeview, newpath,
			gtk_tree_view_get_column (treeview, 0), FALSE);

		//cleanups
		g_list_foreach (rowrefs, (GFunc) gtk_tree_row_reference_free, NULL);
		g_list_free (rowrefs);
		g_list_foreach (selpaths, (GFunc) gtk_tree_path_free, NULL);
		g_list_free (selpaths);
		gtk_tree_path_free (newpath);
	}
}
/**
@brief add treemodel row, and all its descendants, to list

A recursive depth-first copy process puts the path and corresponding
column values for @a iter into an array, then appends that array to
the list @a rowscopy

@param model the treemodel that is being processed
@param iter pointer to tree iter for the row to be processed
@param columncount no. of columns in @a model
@param rowscopy store for pointer to list that holds the result

@return
*/
static void _e2_tree_copy_branch (GtkTreeModel *model, GtkTreeIter *iter,
	gint columncount, GList **rowscopy)
{
	GValueArray *row_array = g_value_array_new (columncount + 1);

	//add path of copied node to the start of the buffer entry, to support proper pasting
	GValue value = {0, };
//	GValue *valptr = g_value_init (&value, G_TYPE_STRING);
	GValue *valptr = g_value_init (&value, G_TYPE_POINTER);
	GtkTreePath *path = gtk_tree_model_get_path (model, iter);
//	gchar *pathstring = gtk_tree_path_to_string (path);
//	g_value_set_static_string (valptr, pathstring);  //no pathstring copy created, don't free it here
	g_value_set_pointer (valptr, path);
	g_value_array_append (row_array, valptr);
	g_value_unset (valptr);
//	gtk_tree_path_free (path);
	//add store column values to the buffer entry
	gint i;
	for (i = 0; i < columncount ; i++)
	{
		gtk_tree_model_get_value (model, iter, i, valptr);
		g_value_array_append (row_array, valptr);
		g_value_unset (valptr);
	}
	//add this row to the buffer list
	*rowscopy = g_list_append (*rowscopy, row_array);
	//recurse to handle any child(ren)
	gint children = gtk_tree_model_iter_n_children (model, iter);
	if (children > 0)
	{
		GtkTreeIter iter2;
		gint n;
		for (n=0; n<children; n++)
		{
			gtk_tree_model_iter_nth_child (model, &iter2, iter, n);
			_e2_tree_copy_branch (model, &iter2, columncount, rowscopy);
		}
	}
}
/**
@brief create a list of selected rows in @a treeview

This is recursive, to handle descendants of any selected row, too

@param treeview the treeview to be processed

@return the list of arrays, or NULL if nothing is selected
*/
GList *e2_tree_copy (GtkTreeView *treeview)
{
	GList *rowscopied = NULL;
	GtkTreeSelection *sel = gtk_tree_view_get_selection (treeview);
	if (gtk_tree_selection_count_selected_rows (sel) > 0)
	{
		GtkTreeModel *model;
		GList *base = gtk_tree_selection_get_selected_rows (sel, &model);
		GtkTreeIter iter;
		gint columns = gtk_tree_model_get_n_columns (model);
		GList *selpaths;

		//copy the selected rows and their descendants
		for (selpaths = base; selpaths != NULL; selpaths = selpaths->next)
		{
			if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) selpaths->data))
				_e2_tree_copy_branch (model, &iter, columns, &rowscopied);
		}

		g_list_foreach (base, (GFunc) gtk_tree_path_free, NULL);
		g_list_free (base);

		printd (DEBUG, "copied %d rows", g_list_length (rowscopied));
	}
	return rowscopied;
}

/*static gboolean _e2_tree_path_compare (GtkTreePath *a, GtkTreePath *b)
{
	gint path_equal = gtk_tree_path_compare (a, b);
	return !path_equal;
} */
/**
@brief paste tree rows into @a treeview

Row(s) are inserted after the 1st selected row in @a treeview,
as child(ren) if the path depth is appropriate
It is assumed here that the buffer hash has pastable data

@param rowscopied list of copied value-arrays
@param treeview the treeview to be processed

@return
*/
void e2_tree_paste (GList *rowscopied, GtkTreeView *treeview)
{
	GtkTreeSelection *sel = gtk_tree_view_get_selection (treeview);
	if (gtk_tree_selection_count_selected_rows (sel) > 0)
	{
		//get source-view path for the 1st buffered item, the
		//root of the copied branch (or one of several roots)
		GValueArray *value_array = rowscopied->data;
		GValue *value = g_value_array_get_nth (value_array, 0);
//		gchar *pathstring = g_value_dup_string (value);
//		GtkTreePath *path = gtk_tree_path_new_from_string (pathstring);
		GtkTreePath *rootpath = (GtkTreePath *) g_value_get_pointer (value);
		gint copyrootdepth = gtk_tree_path_get_depth (rootpath);
//		gtk_tree_path_free (path);
		//store for references (hence iters) needed to preserve relations
		//among pasted rows
		GHashTable *ref_hash = NULL;
		GtkTreeRowReference *ref;

		GtkTreeModel *model;
		GList *selpaths = gtk_tree_selection_get_selected_rows (sel, &model);
		gint columns = gtk_tree_model_get_n_columns (model);
		GList *tmp;
		GtkTreeIter local1;
		GtkTreeIter local2;
		GtkTreeIter *iter = &local1;
		GtkTreeIter *parent = &local2;

		//find a selected item where it's valid to paste
		for (tmp = selpaths; tmp!=NULL; tmp=tmp->next)
		{
			if (gtk_tree_model_get_iter (model, iter, (GtkTreePath *) tmp->data))
			{
				gint thisdepth = gtk_tree_path_get_depth (tmp->data);
				if (thisdepth == copyrootdepth)
				{	//the selected row is a valid sibling of the buffered row(s)
					//paste the root of the copied branch
					printd (DEBUG, "pasting branch root, at depth %d", thisdepth);
/*					if (gtk_tree_model_iter_parent (model, parent, iter))
					{	//pasted row (iter) will be _pre_pended to parent's children
						gtk_tree_store_insert_after (GTK_TREE_STORE (model), iter, parent, NULL);
					}
					else //parent is the root node
					{ */
					//pasted row (iter) will be inserted after selected row
						gtk_tree_store_insert_after (GTK_TREE_STORE (model), parent, NULL, iter);
						iter = parent;
//					}
					break;
				}
				else if (thisdepth == copyrootdepth - 1)
				{	//the selected item is a parent of the buffered row(s)
					//pasted row (iter) will be appended to parent's children
					printd (DEBUG, "pasting branch root, at depth %d", thisdepth);
					gtk_tree_store_insert_before (GTK_TREE_STORE (model), parent, iter, NULL);
					iter = parent;
					break;
				}
			}
			//keep looking ...
		}
		if (tmp != NULL)
		{	//found a place where it's ok to paste
			//populate the newly-added "root" iter
			gint i;
			for (i = 0; i < columns; i++)
			{
				value = g_value_array_get_nth (value_array, i+1);
				gtk_tree_store_set_value (GTK_TREE_STORE (model), iter, i, value);
			}

			//remember how to find the parent of each pasted iter
			//the hash key (a path from the source treeview) is used
			//determine parent/child relations between pasted rows
			ref_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
				g_free, (GDestroyNotify) gtk_tree_row_reference_free);
//			ref_hash = g_hash_table_new_full (g_direct_hash,
//				(GEqualFunc) _e2_tree_path_compare,
//				NULL,	//we don't want to damage the buffered paths
//				(GDestroyNotify) gtk_tree_row_reference_free);
			GtkTreePath *path = gtk_tree_model_get_path (model, iter);
			ref = gtk_tree_row_reference_new (model, path);
			gtk_tree_path_free (path);
//			g_hash_table_insert (ref_hash, pathstring, ref);
			gchar *rootstring = gtk_tree_path_to_string (rootpath);
			g_hash_table_insert (ref_hash, rootstring, ref);

			//now the rest of the list, if any
			//there may be descendant(s) and/or sibling(s) of the initial item
			GtkTreePath *src_path;
			gchar *ppstring;
			GList *tmp2 = rowscopied;
			while ((tmp2=tmp2->next) != NULL)
			{
				value_array = tmp2->data;
				//get source-view path and find where it should sit in the new tree
				value = g_value_array_get_nth (value_array, 0);
//				pathstring = g_value_dup_string (value);
				src_path = (GtkTreePath *) g_value_get_pointer (value);
				path = gtk_tree_path_copy (src_path);
				//get former parent path string, by truncating the last segment
//				gchar *s = g_strrstr (pathstring, ":");
//				if (s != NULL)
//				{
//					*s = '\0';
				if (gtk_tree_path_up (path))
				{
					//try to find an already-pasted parent iter
					ppstring = gtk_tree_path_to_string (path);
					if (ppstring != NULL)
					{	//we're not at the root node now
						ref = g_hash_table_lookup (ref_hash, ppstring);
						gtk_tree_path_free (path);
						g_free (ppstring);
					}
					else
						ref = NULL;
					if (ref != NULL)
					{
						path = gtk_tree_row_reference_get_path (ref);
						gtk_tree_model_get_iter (model, parent, path);
						gtk_tree_path_free (path);
						//append row (iter) to parent's children
						gtk_tree_store_insert_before (
							GTK_TREE_STORE (model), iter, parent, NULL);
					}
					else
					{	//this is probably a sibling
						ref = g_hash_table_lookup (ref_hash, rootstring);
						path = gtk_tree_row_reference_get_path (ref);
						gtk_tree_model_get_iter (model, parent, path);
						gtk_tree_path_free (path);
						//append row (iter) to as a sibling
						gtk_tree_store_insert_after (
							GTK_TREE_STORE (model), iter, NULL, parent);
					}
				}
				else
				{
					//CHECKME probably can't be at a root already
					printd (DEBUG, "root node problem");
					continue;
				}
				//populate row
				for (i = 0; i < columns; i++)
				{
					value = g_value_array_get_nth (value_array, i+1);
					gtk_tree_store_set_value (GTK_TREE_STORE (model), iter, i, value);
				}
//				g_free (pathstring);
				//remember newly-added iter in case it's a parent
				path = gtk_tree_model_get_path (model, iter);
				ref = gtk_tree_row_reference_new (model, path);
				gtk_tree_path_free (path);
				ppstring = gtk_tree_path_to_string (src_path);
//				g_hash_table_insert (ref_hash, g_value_dup_string (value),
//					ref);
				g_hash_table_insert (ref_hash, ppstring, ref);
			}
			//no change to (single row) selection, as all new rows are after
			if (ref_hash != NULL)
				g_hash_table_destroy (ref_hash);
		}
		g_list_foreach (selpaths, (GFunc) gtk_tree_path_free, NULL);
		g_list_free (selpaths);
	}
}
#ifdef STORECOPY
/**
@brief helper function to recursively copy a treestore
@param model treemodel for store being copied
@param parent pointer to treeiter which has been confirmed to have child(ren)
@param ncols no. of columns in @a model
@param colnums array of column indices
@param types array of GTypes corresponding to columns
@param values array in which to store values being transferred

@return
*/
static void _e2_tree_copy_descendants (GtkTreeModel *model, GtkTreeIter *parent, gint ncols,
	gint colnums[], GType types[], GValue values[])
{
	gint i;
	GtkTreeStore *newtstore = GTK_TREE_STORE (model);
	GtkTreeIter child;
	gtk_tree_model_iter_children (model, &child, parent);
	do
	{
		if (gtk_tree_model_iter_has_child (model, &child))
			_e2_tree_copy_descendants (model, &child, ncols, colnums, types, values);
		//read iter into pointers array
		for (i = 0; i < ncols; i++)
			gtk_tree_model_get_value (model, &child, i, &values[i]);
#ifdef USE_GTK2_10
		gtk_tree_store_insert_with_valuesv (newtstore, &child, parent, -1, colnums, values, ncols);
#else
		gtk_tree_store_append (newtstore, &child, parent);
		for (i = 0; i < ncols; i++)
			gtk_tree_store_set_value (newtstore, &child, i, &values[i]);
#endif
		for (i = 0; i < ncols; i++)
			g_value_unset (&values[i]);
	} while (gtk_tree_model_iter_next (model, &child));
}
/**
@brief copy a liststore or treestore
This is for when reffing is not enough e.g. need independent sorting in
attached views. Pointers to data are simply copied, so if the "source" data will
be cleared before fhe new store, then all such data must be replicated or otherwise
preserved by code elsewhere which understands what that data really is.
Officially, the new store will be unsorted, though in practice it will be the
same as the old store.
@param model treemodel of the store being copied
@param treetype TRUE for treestore, FALSE for liststore
@param newstore store for pointer to new store

@a return
*/
void e2_tree_store_copy (GtkTreeModel *model, gboolean treetype, gpointer *newstore)
{
	GtkTreeModel *newmodel;
	GtkTreeIter src;
	GtkTreeIter dest;
	gint i, ncols = gtk_tree_model_get_n_columns (model);
	gint colnums[ncols];
	GType types[ncols];
	GValue values[ncols];

	for (i = 0; i < ncols; i++)
	{
		colnums[i] = i;
		types[i] = gtk_tree_model_get_column_type (model, i);
		memset (&values[i], 0, sizeof (GValue));
	}

	if (treetype)
	{
		GtkTreeStore *newtstore;
		newtstore = gtk_tree_store_newv (ncols, types);
		if (gtk_tree_model_get_iter_first (model, &src))
		{
			newmodel = GTK_TREE_MODEL (newtstore);
			do
			{
				//read iter into values array
				for (i = 0; i < ncols; i++)
					gtk_tree_model_get_value (model, &src, i, &values[i]);
#ifdef USE_GTK2_10
				gtk_tree_store_insert_with_valuesv (newtstore, &dest, NULL, -1, colnums, values, ncols);
#else
				gtk_tree_store_append (newtstore, &dest, NULL);
				for (i = 0; i < ncols; i++)
					gtk_tree_store_set_value (newtstore, &dest, i, &values[i]);
#endif
				for (i = 0; i < ncols; i++)
					g_value_unset (&values[i]);
				if (gtk_tree_model_iter_has_child (model, &src))
					_e2_tree_copy_descendants (model, NULL, ncols, colnums, types, values);

			} while (gtk_tree_model_iter_next (model, &src));
		}
		*newstore = newtstore;
	}
	else
	{
		GtkListStore *newlstore;
		newlstore = gtk_list_store_newv (ncols, types);
		if (gtk_tree_model_get_iter_first (model, &src))
		{
			newmodel = GTK_TREE_MODEL (newlstore);
			do
			{
				for (i = 0; i < ncols; i++)
					gtk_tree_model_get_value (model, &src, i, &values[i]);
				gtk_list_store_insert_with_valuesv (newlstore, &dest, -1, colnums, values, ncols);
				for (i = 0; i < ncols; i++)
					g_value_unset (&values[i]);
			} while (gtk_tree_model_iter_next (model, &src));
		}
		*newstore = newlstore;
	}
}
#endif
