/* $Id: e2_pane.c 894 2008-05-20 21:26:22Z tpgww $

Copyright (C) 2004-2008 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
*/

/**
@file src/e2_pane.c
@brief pane creation and action functions

includes actions on pane contents, but not on panes per se
*/
/**
\page panes the filelist panes

ToDo
\section treeviews
*/

#include "emelfm2.h"
//#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "e2_pane.h"
#include "e2_action.h"
#include "e2_dialog.h"
#ifdef E2_TREEDIALOG
#include "e2_tree_dialog.h"
#endif
#include "e2_context_menu.h"
#include "e2_filelist.h"

static void _e2_pane_change_dir (E2_PaneRuntime *rt, gchar *path,
	gboolean history, gboolean hook);
extern guint last_selected_rows;

  /*****************/
 /***** utils *****/
/*****************/

#ifdef E2_VFS
/**
@brief setup the pane to which @a rt applies to show data for a specified namespace
Any error messages downstream expect BGL closed here
@a spacedescriptor can have various forms, from full-URI to "-1" - see plugin for details
@param rt pointer to pane data struct, NULL for active pane
@param spacedescriptor utf8 string to be interpreted into existing or new namespace

@return TRUE on successful completion or no change needed
*/
gboolean e2_pane_change_space_byuri (E2_PaneRuntime *rt, gchar *spacedescriptor)
{
	if (e2_fs_vfsfunc_ready ((gpointer *)&vfs.set_namedspace))
	{
		ViewInfo *view;
		if (rt == NULL)
			view = curr_view;
		else
			view = (rt == &app.pane1) ? &app.pane1_view : &app.pane2_view;
		return (vfs.set_namedspace (view, spacedescriptor));
		//CHECKME last-used dir is opened when we change space ?
	}
	else
	{
		printd (WARN, "Cannot find vfs plugin");
		return FALSE;
	}
}
/**
@brief setup the pane to which @a rt applies to show data for a specified namespace
If @a utfpath has a NULL localpath, then the relevant history or vtab path will be used
@param rt pointer to pane data struct
@param localpath pointer to path and namespace data struct

@return TRUE on successful change or no change needed
*/
gboolean e2_pane_change_space (E2_PaneRuntime *rt, VPATH *utfpath)
{
	PlaceInfo *current = rt->view->spacedata;
	if (current == utfpath->spacedata)
		return TRUE;	//nothing to change
//E2_VFSTMP may want to change dir anyway
	if (e2_fs_vfsfunc_ready ((gpointer *)&vfs.set_space))
	{
		ViewInfo *view;
		if (rt == NULL)
			view = curr_view;
		else
			view = (rt == &app.pane1) ? &app.pane1_view : &app.pane2_view;
		if (!vfs.set_space (view, utfpath->spacedata, TRUE))
			return FALSE;
//E2_VFSTMP CHECKME last-used dir is opened when we change space ?
		if (utfpath->localpath == NULL)
			_e2_pane_change_dir (rt, "FIXME-HISTORY-ITEM", FALSE, TRUE);
		else
			_e2_pane_change_dir (rt, (gchar *)utfpath->localpath, TRUE, TRUE);
		return TRUE;
	}
	else
	{
		printd (WARN, "Cannot find vfs plugin");
		return FALSE;
	}
}
#endif //fdef E2_VFS

/**
@brief set filepanes' properties to indicate which one is active

The "active-pane-signal" config option is checked, and: the column
headers will be re-coloured, or the filename column title will be
[un]bolded, or the treeviews' sensitivity will be toggled

@return
*/
void e2_pane_flag_active (void)
{
	E2_OptionSet *set = e2_option_get ("active-pane-signal");
	gint htype = e2_option_int_get_direct (set);
	switch (htype)
	{
		case 1:
		{  //set bold Filename-column header title
			gchar *new_title = gettext (e2_all_columns[FILENAME].title);
			gchar *new_title2 = g_strconcat ("<b>", new_title,"</b>", NULL);
			gtk_label_set_markup (curr_view->name_label, new_title2);
			g_free (new_title2);
			//set normal Filename-column header title
			gtk_label_set_text (other_view->name_label, new_title);
			break;
		}
		case 2:
		{  //make active pane sensitive
			gtk_widget_set_sensitive (curr_view->treeview, TRUE);
			//make inactive pane insensitive
			gtk_widget_set_sensitive (other_view->treeview, FALSE);
			break;
		}
		default:
		{
			//set all active columns' header color
			GdkColor *active_btncolor = e2_option_color_get ("color-active-pane");
			GList *base = gtk_tree_view_get_columns (GTK_TREE_VIEW (curr_view->treeview));
			GList *columns;
			for (columns = base; columns !=NULL; columns = columns->next)
				gtk_widget_modify_bg (((GtkTreeViewColumn *)columns->data)->button,
					GTK_STATE_NORMAL, active_btncolor);
			g_list_free (base);
			//revert all inactive columns' header color to default
			base = gtk_tree_view_get_columns (GTK_TREE_VIEW (other_view->treeview));
			for (columns = base; columns != NULL; columns = columns->next)
				gtk_widget_modify_bg (((GtkTreeViewColumn *)columns->data)->button,
					GTK_STATE_NORMAL, NULL);
			g_list_free (base);
			break;
		}
	}
}
/**
@brief change active pane from the current one to the other one
Assumes BGL is closed on arrival here
@return
*/
void e2_pane_activate_other (void)
{
//	printd (DEBUG, "e2_pane_activate_other ()");
	E2_PaneRuntime *tmp = other_pane;
	other_pane = curr_pane;
	curr_pane = tmp;
	//force a status-line update
	last_selected_rows = -1;
#ifdef E2_STATUS_DEMAND
BGL
	e2_window_update_status_bar (NULL);
#endif
	e2_fileview_switch_views ();
//	gtk_widget_grab_focus (tmp->focus_widget);
	if (GTK_WIDGET_HAS_FOCUS (other_view->treeview))
		gtk_widget_grab_focus (curr_view->treeview);
	gdk_threads_leave ();
	e2_hook_list_run (&app.hook_pane_focus_changed, tmp);
	gdk_threads_enter ();
}
/* *
@brief change-directory completion-watch timer-function

@param completed_flag store for flag to watch

@return FALSE when ready to shutdown timer
*/
/*static gboolean _e2_pane_cd_watch (E2_CDType *completed_flag)
{
	return (*completed_flag == CD_NOTFINISHED);
	//FIXME signal that this timer is being shutdown
}
*/
/* *
@brief change the directory for a specified pane and setup completion-watch

This is a wrapper for _e2_pane_change_dir (), with update of
pane history list and hooklist
It must be called with gtk's thread-lock closed

@param rt pointer to pane data struct, or NULL for current pane
@param path utf8 string containing actual or virtual path to directory to be opened
@param completed_flag store for flag to set to CD_SUCCESS if/when cd is completed

@return
*/
/*
void e2_pane_change_dir_watch (E2_PaneRuntime *rt, gchar *path,
	E2_CDType *completed_flag)
{
	*completed_flag = CD_NOTFINISHED;
	_e2_pane_change_dir (rt, path, completed_flag, TRUE, TRUE);
	//FIXME wait for completion, without blocking the cd process
//	app.timers[?] = g_timeout_add (100, (GSourceFunc) _e2_pane_cd_watch, &completed_flag);
	//FIXME nicely block while the timer is still running
}
*/
/**
@brief change the directory for a specified pane

This is a wrapper for _e2_pane_change_dir (), with update of
pane history list and hooklist
gtk's thread-lock (BGL) may be open or closed

@param rt pointer to pane data struct, or NULL for current pane
@param path utf8 string containing actual or virtual path to directory to be opened

@return
*/
void e2_pane_change_dir (E2_PaneRuntime *rt, gchar *path)
{
	_e2_pane_change_dir (rt, path, TRUE, TRUE);
}
/**
@brief change directory displayed in a specified pane
Makes no assumption about whether BGL is active, but uses timer to force-open
BGL for downstream processing where needed
~ at start of path is interpreted to $HOME
Surrounding single- or double-quotes are removed
Redundant separators are removed, and one is added to the end,
if not already there
Any ".." at start or later in the path is corrected if possible (or if not,
ignored)
Does nothing if the path string is empty, shows error msg if
path is unreachable
Session-start check for valid path done in main(), not here
The supplied path string is unchanged.
New path is stored in heap @ rt->path
New path is also stored in array @ rt->view->dir

@param rt pointer to pane data struct, or NULL for current pane
@param path utf8 string containing space-relative path of directory to be opened
@param history TRUE to update pane history list
@param hook TRUE to run change-dir hook functions (if any, normally has at least update dirline)

@return
*/
static void _e2_pane_change_dir (E2_PaneRuntime *rt, gchar *path,
	gboolean history, gboolean hook)
{
	gchar *freeme, *currpath;
	printd (DEBUG, "_e2_pane_change_dir (rt:,path:\"%s\",update:%d,hook:%d)",
		path, history, hook);

	if (path == NULL)
		return;

	path = g_strdup (path);
	g_strchug (path);	//trailing whitespace may be deliberate

	if (*path == '\0')
	{
		//quick exit
		g_free (path);
		return;
	}
	else if (path[0] == '"' || path[0] == '\'')
	{
		gint len = strlen (path);
		gchar *s = path + len - sizeof (gchar);
		if (*s == path[0])
		{
			path[0] = ' ';
			*s = '\0';
			g_strchug (path);	//trailing whitespace may be deliberate
		}
	}

	//work on currently active file pane if argument is NULL
	if (rt == NULL)
		rt = curr_pane;

	//work with a copy of path string which might be in transition by a prior cd
	LISTS_LOCK
	currpath = g_strdup (rt->path);
	LISTS_UNLOCK

	if (g_str_equal (path, "..") &&
		g_str_equal (currpath, G_DIR_SEPARATOR_S)
#ifdef E2_VFSTMP
	//CHECKME how does updir from archive-root work here ?
#endif
		)
	{
		//ignore impossible change
		g_free (path);
		g_free (currpath);
		return;
	}
	//CHECKME do a full interpretation here ?
	//this fails in the event of trailing whitespace
	else if (path[0] == '.' &&
		(path[1] == '\0' || (path[1] == G_DIR_SEPARATOR && path[2] == '\0')))
	{
		g_free (path);
#ifdef E2_VFSTMP
		freeme = ?;
#else
		freeme = g_get_current_dir ();
#endif
		path = F_FILENAME_FROM_LOCALE (freeme);
		if (path != freeme)
			g_free (freeme);
	}
	else if (path[0] == '~') //home dir check
	{
		freeme = path;
		if (path[1] == '\0')
		{
			path = g_strdup (g_get_home_dir ());
			g_free (freeme);
		}
		else
			if (path[1] == G_DIR_SEPARATOR)
		{
			path = g_strconcat (g_get_home_dir (), path + 1, NULL);
			g_free (freeme);
		}
	}

/* #ifndef E2_FILES_UTF8ONLY
	if (!app.utf8_filenames)
	{
		//this test not needed with only-utf8 file coding
		if (! g_utf8_validate (path, -1, NULL))
		{
			freeme = path;
			path = e2_utf8_filename_from_locale (path);
			g_free (freeme);
		}
	}
#endif */

#ifdef E2_VFSTMP
	//FIXME for changes up from a virtual root ...
	if ( !g_str_equal (path, "..")
	  || !g_str_equal (currpath, G_DIR_SEPARATOR_S))
	{
#endif
	//cleanup the new path string
	//these funcs both ensure a trailing /, which gets into rt->path
	//(see below) and onto dir lines
		if (e2_option_bool_get_direct (rt->opt_transparent)
			|| g_str_equal (path, ".."))	//always handle cd .. properly
		{	//relative path segment(s) conversion
			//clean and convert the path string, if needed
			freeme = path;
#ifdef E2_VFSTMP
		//CHECKME confirm this is ok for non-local work-dir
#endif
			path = e2_utils_translate_relative_path (currpath, path);
			g_free (freeme);
		}
		else
		{
			if (!g_path_is_absolute (path))
			{
				freeme = path;
				path = g_strconcat (currpath, path, NULL);
				g_free (freeme);
			}
			//make sure the user didn't enter a 'dirty' path string
#ifdef E2_VFSTMP
//CHECKME confirm this is ok for non-local work-dir
			path = e2_utils_path_clean (path);
#else
			path = e2_utils_path_clean (path);
#endif
		}
#ifdef E2_VFSTMP
	}
#endif

	g_free (currpath);

	E2_Listman *data = (rt == &app.pane1) ?
		&app.pane1_view.listcontrols : &app.pane2_view.listcontrols;
	LISTS_LOCK
	data->view = rt->view;
	data->history = history;
	data->hook = hook;
	//now signal we're ready to do a new cd
//	any prior data->newpath is copied and cleared downstream, when ready
	data->newpath = path;	//this is a copy
	LISTS_UNLOCK
	/*we use a timer, not a thread directly, to control the cd process cuz:
		. we're happy to do the cd at a reasonably non-busy time
		. if gtk's "native" BGL mutex is in play, _must_ ensure that BGL is off,
		  for the downstream code that performs the cd
	  (BUT this does make it tough to wait for the end of any specific cd)
	  we check in the timer callback, not here, for any duplicated timer for this
	  same pane (or the other one), so as to eliminate any race and missed cd request */
	gint interval = (data->cd_working) ? 90 :	//check about every 90 mS for completion of prior cd
		4; //short initial delay in case there's a following request for other pane
//	data->timer = with possible repeated timers for the same pane, the id is useless
//#ifdef DEBUG_MESSAGES
//	guint tid =
//#endif
	g_timeout_add_full (G_PRIORITY_HIGH, interval,
		(GSourceFunc) e2_fileview_cd_manage, data, NULL);
//	printd (DEBUG, "initiated cd timer = %d", tid);
//	LISTS_UNLOCK
/*	//setup for completion-watching if requested
	if (completed_flag != NULL)
	{
		E2_CDwatch *data2 = ALLOCATE (E2_CDwatch);
		CHECKALLOCATEDWARN (data2, return;)
		data2->view = rt->view;
		data2->newpath = g_strdup (path);
		data2->repeats = 0;
		data2->completed_flag = completed_flag;
		//this timer stops after 100 callbacks = 10 seconds
		//CHECKME make this cancellable at session-end ?
		//data->watchtimer_id =
		g_timeout_add (100,
			(GSourceFunc) e2_fileview_cd_watch, data2);
	}
*/
}
/**
@brief show a chosen directory in a pane

@param rt pointer to pane data struct, or NULL to determine the pane
@param entry pointer to widget where the action was initiated, or NULL

@return TRUE if the directory was changed successfully
*/
gboolean e2_pane_goto_choice (E2_PaneRuntime *rt, GtkWidget *entry)
{
#ifdef E2_VFSTMP
	//FIXME ensure this works with non-mounted dirs
#endif
	E2_PaneRuntime *prt;
	E2_CommandLineRuntime *clrt;
	if (entry != NULL && g_str_equal (IS_A (entry), "GtkEntry"))
	{
		if (rt != NULL)
			prt = rt;
		else
		{
			clrt = g_object_get_data (G_OBJECT (entry), "command-line-runtime");
			prt = clrt->pane;
		}
	}
	else
	{	//try to find relevant pane and/or entry
		GList *line;
		for (line = app.command_lines; line != NULL ; line = g_list_next (line))
		{
			clrt = line->data;
			if (! clrt->original	//this is a dir line
				&& (GTK_BIN (clrt->cl)->child == entry//processing a keybinding, entry != NULL
						|| clrt->pane == rt) //processing a mouse-click, rt != NULL
			)
			{
				prt = clrt->pane;
				entry = GTK_BIN (clrt->cl)->child;
				break;
			}
		}
		if (line == NULL)	//OOPS !!
			return FALSE;	//can't do anything
	}

	//create relevant path string
	const gchar *choice = gtk_entry_get_text (GTK_ENTRY (entry));
	gchar *dir;
	if (g_str_has_prefix (choice, G_DIR_SEPARATOR_S))
		dir = g_strdup (choice);
	else
	{
#ifdef E2_VFSTMP
	//FIXME path for non-mounted dirs
#else
		//path string might be involved in a current cd
		LISTS_LOCK
		dir = g_strconcat (prt->path, choice, NULL);
		LISTS_UNLOCK
#endif
	}
#ifdef E2_VFSTMP
	//FIXME handle vfs error reasonably
#endif
	e2_fs_get_valid_path (&dir, FALSE E2_ERR_NONE());

	gchar *newdir = NULL;
	e2_opendir_dialog_create (prt->view, dir, &newdir);
	g_free (dir);
	if (newdir != NULL)
	{	//something was selected
		_e2_pane_change_dir (prt, newdir, TRUE, TRUE);
		g_free (newdir);
		return TRUE;
	}
	//user cancelled
	gtk_widget_grab_focus (entry);
	gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);	//unselect the entry contents
	return FALSE;
}
/* *
@brief timeout function for checking completion of fileview creation

@param userdata data specified when the timer was initialised, here, always NULL, unused

@return TRUE until update is completed, then FALSE
*/
/*gboolean _e2_pane_views_complete (gpointer userdata)
{
	if (!(curr_view->listing || other_view->listing))
	{
		printd (DEBUG, "finished waiting for fileview completion");
		gdk_threads_enter ();
		e2_fileview_switch_views ();
		gtk_widget_grab_focus (curr_view->treeview);
		gdk_threads_leave ();
		//run this with BGL open/off
		e2_hook_list_run (&app.hook_pane_focus_changed, curr_pane);
		return FALSE;
	}
	return TRUE;  //continue waiting
}
*/
/**
@brief pane history goto-menu callback

@param menuitem the selected item
@param rt pointer to pane data struct

@return
*/
static void _e2_pane_history_selected_cb (GtkMenuItem *menuitem, E2_PaneRuntime *rt)
{
#ifdef E2_VFSTMP
	//FIXME ensure this works with non-mounted dirs ie history is specific to the fs type/place
#endif
	gpointer curr = g_object_get_data (G_OBJECT (menuitem), "e2-history-pick");
	rt->opendir_cur = GPOINTER_TO_UINT (curr);
	GtkWidget* label = gtk_bin_get_child (GTK_BIN (menuitem));
	const gchar* dir = gtk_label_get_label (GTK_LABEL (label));
	_e2_pane_change_dir (rt, (gchar *)dir, TRUE, TRUE);
}

  /*******************/
 /***** actions *****/
/*******************/

/**
@brief switch panes if a specified pane is not the active one, or focus current pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if the focus was changed
*/
static gboolean _e2_pane_focus_action (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;
	if (rt != NULL && rt != curr_pane)
	{
		e2_pane_activate_other ();
		return TRUE;
	}
	else if (rt == NULL)
	{
		gtk_widget_grab_focus (curr_view->treeview);
		return TRUE;
	}
	return FALSE;
}
/**
@brief change active pane from the current one to the other one
This also toggles the visible filepane, if only 1 is shown
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
gboolean e2_pane_activate_other_action (gpointer from, E2_ActionRuntime *art)
{
	//if showing only 1 filepane, show the other one now
	if (app.window.panes_paned_ratio < 0.001)
		e2_window_adjust_pane_ratio ("1");
	else if (app.window.panes_paned_ratio > 0.999)
		e2_window_adjust_pane_ratio ("0");
	e2_pane_activate_other ();
	return TRUE;
}
/**
@brief change the directory shown in a specified pane or the current one
The cd is asynchronous, so probably not done immediately
Downstream function expects BGL to be closed
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
gboolean e2_pane_change_dir_action (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *rt = (E2_PaneRuntime *) art->action->data;
	if (rt == NULL)
		rt = curr_pane;
	_e2_pane_change_dir (rt, (gchar *)art->data, TRUE, TRUE);
	return TRUE;
}
/* *
@brief for a specified pane, open the directory @a number places forward in the history list

The last entry in the list will be used if we try to go forward too far

@param rt  pointer to pane data struct
@param number integer no.of places in the history list to go forward

@return
*/
/*static void _e2_pane_go_forward_ (E2_PaneRuntime *rt, gint number)
{
NOTE - IF USED, THIS MAY NEED A BASE-INDEX CORRECTION (1 TO 0) AND LIST-DIRECTION SWAP
	printd (DEBUG, "_e2_pane_go_forward (rt:,number:%d", number);
	guint old = rt->opendir_cur;
	guint len = g_list_length (rt->history);
	rt->opendir_cur += number;
	if (rt->opendir_cur > g_list_length (rt->history))
		rt->opendir_cur = len;
	if (old != rt->opendir_cur)
	{
//		gchar *dir = F_FILENAME_TO_LOCALE (
		gchar *dir =
			g_list_nth_data (rt->history, rt->opendir_cur - 1);
		_e2_pane_change_dir (rt, dir, NULL, FALSE, TRUE);
//		F_FREE (dir);
	}
} */
/**
@brief goto the next dir in the history list for specified or current pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE unless there's nowhere to goto
*/
static gboolean _e2_pane_go_forward (gpointer from, E2_ActionRuntime *art)
{
//	printd (DEBUG, "_e2_pane_go_forward action");
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;
	//action data is pointer to pane data struct, or NULL to use current pane
	if (rt == NULL)
		rt = curr_pane;
	//history entries are prepended to list, forward in history = back in list
	HISTORY_LOCK
	if (rt->opendirs == NULL || rt->opendir_cur == 0)
	{
		HISTORY_UNLOCK
		return FALSE;	//nowhere to go to
	}

	if (g_str_equal (IS_A ((GtkWidget *)from), "GtkButton"))
	{
		GdkModifierType mask = e2_utils_get_modifiers ();
		if (mask & GDK_CONTROL_MASK)
		{
			GtkWidget *menu = gtk_menu_new ();
			GtkWidget *item;
			guint curr = (rt->opendir_cur > 1) ? rt->opendir_cur - 1 : 0;
			GList *member = g_list_nth (rt->opendirs, curr);
			HISTORY_UNLOCK
			while (member != NULL)
			{
				item = gtk_menu_item_new_with_label ((gchar *)member->data);
				gtk_widget_show_all (item);
				gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
				g_object_set_data (G_OBJECT (item), "e2-history-pick",
					GUINT_TO_POINTER (curr));
				g_signal_connect (G_OBJECT (item), "activate",
					G_CALLBACK (_e2_pane_history_selected_cb), rt);
				HISTORY_LOCK
				member = member->prev;
				HISTORY_UNLOCK
				curr--;
			}
			g_signal_connect (G_OBJECT (menu), "selection-done",
				G_CALLBACK (e2_menu_destroy_cb), NULL);
			gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
				(GtkMenuPositionFunc) e2_toolbar_set_menu_position, from, 1,
					gtk_get_current_event_time ());
			return TRUE;
		}
	}

	rt->opendir_cur--;
//	gchar *dir = F_FILENAME_TO_LOCALE (
	gchar *dir = g_list_nth_data (rt->opendirs, rt->opendir_cur);
	HISTORY_UNLOCK
	_e2_pane_change_dir (rt, dir, FALSE, TRUE);
//	F_FREE (dir);

	return TRUE;
}
/* *
@brief for a specified pane, open the directory @a number places back in the history list

The first entry in the list will be used if we try to go back too far

@param rt  pointer to pane data struct
@param number integer no.of places in the history list to go back

@return
*/
/*static void _e2_pane_go_back_ (E2_PaneRuntime *rt, gint number)
{
NOTE - IF USED, THIS MAY NEED A BASE-INDEX CORRECTION (1 TO 0) AND LIST-DIRECTION SWAP
	printd (DEBUG, "_e2_pane_go_back (rt:,number:%d", number);
	guint old = rt->opendir_cur;
	rt->opendir_cur -= number;
	if (rt->opendir_cur < 1)
		rt->opendir_cur = 1;
	if (old != rt->opendir_cur)
	{
		gchar *dir = g_list_nth_data (rt->history, rt->opendir_cur - 1);
		_e2_pane_change_dir (rt, dir, FALSE, TRUE);
	}
}*/
/**
@brief goto the previous dir in the history list for specified or current pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE unless there's nowhere to goto
*/
static gboolean _e2_pane_go_back (gpointer from, E2_ActionRuntime *art)
{
//	printd (DEBUG, "_e2_pane_go_back action");
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;
	if (rt == NULL)
		rt = curr_pane;
	//history entries are prepended to list, so back in history = forward in list
	HISTORY_LOCK
	if (rt->opendirs == NULL || rt->opendir_cur == g_list_length (rt->opendirs) - 1)
	{
		HISTORY_UNLOCK
		return FALSE;
	}

	if (g_str_equal (IS_A ((GtkWidget *)from), "GtkButton"))
	{
		GdkModifierType mask = e2_utils_get_modifiers ();
		if (mask & GDK_CONTROL_MASK)
		{
			GtkWidget *menu = gtk_menu_new ();
			GtkWidget *item;
			guint curr = rt->opendir_cur + 1;
			GList *member = g_list_nth (rt->opendirs, curr);
			HISTORY_UNLOCK
			while (member != NULL)
			{
				item = gtk_menu_item_new_with_label ((gchar *)member->data);
				gtk_widget_show_all (item);
				gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
				g_object_set_data (G_OBJECT (item), "e2-history-pick",
					GUINT_TO_POINTER (curr));
				g_signal_connect (G_OBJECT (item), "activate",
					G_CALLBACK (_e2_pane_history_selected_cb), rt);
				HISTORY_LOCK
				member = member->next;
				HISTORY_UNLOCK
				curr++;
			}
			g_signal_connect (G_OBJECT (menu), "selection-done",
				G_CALLBACK (e2_menu_destroy_cb), NULL);
			gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
				(GtkMenuPositionFunc) e2_toolbar_set_menu_position, from, 1,
					gtk_get_current_event_time ());
			return TRUE;
		}
	}

	rt->opendir_cur++;
	gchar *dir = g_list_nth_data (rt->opendirs, rt->opendir_cur);
	HISTORY_UNLOCK
	_e2_pane_change_dir (rt, dir, FALSE, TRUE);

	return TRUE;
}
/**
@brief in a specified pane or current pane, open the parent of the current directory
The helper function prevents any change if at root dir already
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_go_up (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;
	if (rt == NULL)
		rt = curr_pane;
	_e2_pane_change_dir (rt, "..", TRUE, TRUE);
	return TRUE;
}
/**
@brief open pane other pane directory in this pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_mirror (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;
	E2_PaneRuntime *ort = (rt == &app.pane1) ? &app.pane2 : &app.pane1;
	LISTS_LOCK
	gchar *pathcopy = g_strdup (ort->path);
	LISTS_UNLOCK
	_e2_pane_change_dir (rt, pathcopy, TRUE, TRUE);
	g_free (pathcopy);
	return TRUE;
}
/**
@brief show a chosen directory in a pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if directory was changed
*/
static gboolean _e2_pane_goto_choice (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;	//can be NULL if from != NULL
	GtkWidget *entry = (from == NULL) ? NULL : GTK_WIDGET (from);
	return (e2_pane_goto_choice (rt, entry));
}
/**
@brief open the trash directory in the inactive pane
There is no hook update for the pane
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if trash directory is recorded
*/
static gboolean _e2_pane_goto_trash (gpointer from, E2_ActionRuntime *art)
{
	const gchar *tp = e2_utils_get_trash_path ();
	if (tp != NULL)
	{
		//change the dir line entry, if possible
		E2_CommandLineRuntime *clrt;
		GList *line;
		for (line = app.command_lines; line != NULL ; line = g_list_next (line))
		{
			clrt = line->data;
			if (! clrt->original	//this is a dir line
				&& clrt->pane == other_pane)
			{
				GtkWidget *entry = GTK_BIN (clrt->cl)->child;
				gtk_entry_set_text (GTK_ENTRY (entry), tp);
				break;
			}
		}
		_e2_pane_change_dir (other_pane, (gchar *) tp, TRUE, FALSE);	//NOTE no hook so no change of dirline !

		return TRUE;
	}
	e2_output_print_error (_("No trash directory is available"), FALSE);
	return FALSE;
}
/**
@brief create and pop up a filters menu for a specified pane, adjacent to a toolbar button

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_filter_menu_create (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;
	ViewInfo *view;
	if (rt == NULL)
		view = curr_view;
	else
		view = (rt == &app.pane1) ? &app.pane1_view : &app.pane2_view;
	GtkWidget *menu = e2_menu_create_filter_menu (view);
	guint32 time = gtk_get_current_event_time ();
	gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
		(GtkMenuPositionFunc) e2_toolbar_set_menu_position, from, 1, time);
	return TRUE;
}
/**
@brief create and show filters menu in active pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_filters_show (gpointer from, E2_ActionRuntime *art)
{
	GtkWidget *menu = e2_menu_create_filter_menu (curr_view);
	guint32 time = gtk_get_current_event_time ();
	gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
		(GtkMenuPositionFunc) e2_fileview_set_menu_position,
			curr_view->treeview, 0, time);
	return TRUE;
}
#ifdef E2_VFS
/**
@brief select-namespace action, where a string argument is supplied

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if the change was successful
*/
static gboolean _e2_pane_select_space (gpointer from, E2_ActionRuntime *art)
{
	return (e2_pane_change_space_byuri ((E2_PaneRuntime *)art->action->data, (gchar *)art->data));
}
/**
@brief vfs menu-item callback
This must be here, not in vfs plugin, as it is one mechanism to load that plugin
Other callbacks are in the plugin
@param menu_item the selected menu item widget
@param view pointer to data struct for view to be changed

@return
*/
static void _e2_pane_vfs_dialog_cb (GtkWidget *menu_item, ViewInfo *view)
{
	if (e2_fs_vfsfunc_ready ((gpointer *)&vfs.show_historydialog))
		vfs.show_historydialog (view);
	else
		printd (WARN, "Cannot find vfs plugin");
}
/**
@brief respond to pane vfs-button click

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_vfs_menu_show (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;	//will be NULL for active-pane action
	ViewInfo *view;
	if (rt == NULL)
		view = curr_view;
	else
		view = (rt == &app.pane1) ? &app.pane1_view : &app.pane2_view;

	GtkWidget *item, *menu = gtk_menu_new ();

	if (e2_fs_vfsfunc_ready ((gpointer *)&vfs.create_placesmenu))	//note no cursor change in event of slow loading
	{
		gboolean history = FALSE;
		if (g_str_equal (IS_A ((GtkWidget *)from), "GtkButton"))
		{
			GdkModifierType mask = e2_utils_get_modifiers ();
			if (mask & GDK_CONTROL_MASK)
				history = TRUE;
		}
		vfs.create_placesmenu (menu, view, history);	//add plugin-specific items to menu
	}

	item = e2_menu_add (menu, _("_Edit places"), NULL, NULL,
		_e2_pane_vfs_dialog_cb, view);

	g_signal_connect (G_OBJECT (menu), "selection-done",
		G_CALLBACK (e2_menu_destroy_cb), NULL);

	guint32 time = gtk_get_current_event_time ();
	if (rt == NULL)
		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
			(GtkMenuPositionFunc) e2_fileview_set_menu_position,
				curr_view->treeview, 0, time);
	else
		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
			(GtkMenuPositionFunc) e2_toolbar_set_menu_position, from, 1, time);
	return TRUE;
}
#endif
/**
@brief select all items in specified pane
The pane is made active, if it wasn't already
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_select_all (gpointer from, E2_ActionRuntime *art)
{
	E2_PaneRuntime *rt = (E2_PaneRuntime *)art->action->data;
	if (rt != curr_pane)
		e2_pane_activate_other ();
	e2_fileview_select_all (NULL, curr_view);
#ifdef E2_STATUS_DEMAND
use a selection-change cb ? BGL management
	e2_window_update_status_bar (NULL);
#endif
	return TRUE;
}
/**
@brief select all items in active pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_current_select_all (gpointer from, E2_ActionRuntime *art)
{
	e2_fileview_select_all (NULL, curr_view);
#ifdef E2_STATUS_DEMAND
use a selection-change cb ? BGL management
	e2_window_update_status_bar (NULL);
#endif
	return TRUE;
}
/**
@brief invert selection state of all items in active pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if there is any item in the pane
*/
static gboolean _e2_pane_current_invert_all (gpointer from, E2_ActionRuntime *art)
{
	GtkTreeIter iter;
	GtkTreeModel *mdl = curr_view->model;
	if (gtk_tree_model_get_iter_first (mdl, &iter))
	{
		GtkTreeSelection *sel = curr_view->selection;
		do
		{
			if (gtk_tree_selection_iter_is_selected (sel, &iter))
				gtk_tree_selection_unselect_iter (sel, &iter);
			else
				gtk_tree_selection_select_iter (sel, &iter);
		} while (gtk_tree_model_iter_next (mdl, &iter));
#ifdef E2_STATUS_DEMAND
use a selection-change cb ? BGL management
		e2_window_update_status_bar (NULL);
#endif
		return TRUE;
	}
	return FALSE;
}
/**
@brief invert selection state of focused item in active pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if there is any item in the pane
*/
static gboolean _e2_pane_current_invert_current (gpointer from, E2_ActionRuntime *art)
{
	GtkTreeIter iter;
	if (gtk_tree_model_iter_nth_child (curr_view->model, &iter, NULL, curr_view->row))
	{
		GtkTreeSelection *sel = curr_view->selection;
		if (gtk_tree_selection_iter_is_selected (sel, &iter))
			gtk_tree_selection_unselect_iter (sel, &iter);
		else
			gtk_tree_selection_select_iter (sel, &iter);
#ifdef E2_STATUS_DEMAND
use a selection-change cb ? BGL management
		e2_window_update_status_bar (NULL);
#endif
		return TRUE;
	}
	return FALSE;
}

static gboolean _e2_pane_toggle_hidden_ (ViewInfo *view)
{
	view->show_hidden = !view->show_hidden;
	e2_fileview_refilter_list (view);
#ifdef E2_STATUS_DEMAND
	if (view == curr_view)
	{
use a selection-change cb ? BGL management
		e2_window_update_status_bar (NULL);
	}
#endif
	//toggle the button
	E2_ToggleType num = (view == &app.pane1_view) ?
		E2_TOGGLE_PANE1HIDDEN : E2_TOGGLE_PANE2HIDDEN;
	e2_toolbar_button_toggle (toggles_array[num]);
	return TRUE;
}
/**
@brief toggle display of hidden items in a specified file list
Downstream functions require BGL closed
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_toggle_hidden (gpointer from, E2_ActionRuntime *art)
{
	return (_e2_pane_toggle_hidden_ ((ViewInfo *) art->action->data));
}
/**
@brief toggle display of hidden items in active pane
Downstream functions require BGL closed
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_current_toggle_hidden (gpointer from, E2_ActionRuntime *art)
{
	return (_e2_pane_toggle_hidden_ (curr_view));
}
/**
@brief toggle focus between a dir line and the corresponding file pane

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if the focus was changed
*/
static gboolean _e2_pane_focus_dirline_action (gpointer from, E2_ActionRuntime *art)
{
	//string with "1" (or anything else) which specifies the dir line to focus
	gchar *arg = (gchar *) art->data;
	gint choice = 1;
	if (arg != NULL)
	{
		g_strstrip (arg);
		if (!g_str_equal (arg, "1"))	//CHECKME no translation ??
			choice = 2;
	}
	E2_PaneRuntime *pane = (choice == 1) ? &app.pane1 : &app.pane2;
	//find the first-registered dir-line for the pane
	E2_CommandLineRuntime *cl_rt;
	GList *list;
	for (list = app.command_lines; list != NULL ; list = g_list_next (list))
	{
		cl_rt = (E2_CommandLineRuntime *) list->data;
		if (!cl_rt->original && cl_rt->pane == pane)
			break;
	}
	if (list == NULL)
		return FALSE;	//no dirline found for the pane

	if (GTK_WIDGET_HAS_FOCUS (GTK_BIN (cl_rt->cl)->child))
		gtk_widget_grab_focus (curr_view->treeview);
	else
	{
		gtk_editable_set_position (GTK_EDITABLE (GTK_BIN (cl_rt->cl)->child), -1);
		gtk_widget_grab_focus (GTK_BIN (cl_rt->cl)->child);
	}
	return TRUE;
}
/**
@brief focus active pane file list
This is primarily intended for the start of a chained a key-binding
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE
*/
static gboolean _e2_pane_current_focus (gpointer from, E2_ActionRuntime *art)
{
	gtk_widget_grab_focus (curr_view->treeview);
	return TRUE;
}
/**
@brief action to go to top of active pane file list
This is primarily intended for a custom key-binding
Downstream code expects BGL to be closed
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if there is any item in the pane
*/
static gboolean _e2_pane_current_focus_top (gpointer from, E2_ActionRuntime *art)
{
	if (gtk_tree_model_iter_n_children (curr_view->model, NULL) > 0)
	{
		e2_fileview_focus_row (curr_view, 0, TRUE, TRUE, FALSE, TRUE);
		return TRUE;
	}
	return FALSE;
}
/**
@brief action to go to bottom of active pane file list
This is primarily intended for a custom key-binding
Downstream code expects BGL to be closed
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if there is any item in the pane
*/
static gboolean _e2_pane_current_focus_bottom (gpointer from, E2_ActionRuntime *art)
{
	gint n = gtk_tree_model_iter_n_children (curr_view->model, NULL);
	if (n > 0)
	{
		e2_fileview_focus_row (curr_view, n-1, TRUE, TRUE, FALSE, TRUE);
		return TRUE;
	}
	return FALSE;
}
/**
@brief action to sort a visible column in active pane
This is primarily intended for a custom key-binding
@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE always
*/
static gboolean _e2_pane_sort_active (gpointer from, E2_ActionRuntime *art)
{
	return (e2_fileview_sort_column (GPOINTER_TO_INT (art->action->data), curr_view));
}

  /******************/
 /***** public *****/
/******************/

/**
@brief grab pointers to options previously registered

@param rt pointer to pane data struct

@return
*/
void e2_pane_create_option_data (E2_PaneRuntime *rt)
{
/*	gchar *option_name = (rt == &app.pane1) ?
		"panebar1" : "panebar2" ;	//do not translate
	rt->set = e2_option_get (option_name);
	option_name = g_strconcat (rt->name,"-use-startup-dir",NULL);
	rt->opt_use_startup = e2_option_get (option_name);
	g_free (option_name);
	option_name =  g_strconcat(rt->name,"-use-startup-dir-startup-dir",NULL);
	rt->opt_startup = e2_option_get (option_name);
	g_free (option_name);
	//create dependency
	rt->opt_startup->depends = rt->opt_use_startup->name;
*/
	rt->opt_transparent = e2_option_get ("transparent-dir-links");
}
/**
@brief create file pane and all its contents
This is used only at session-start
@param rt pointer to pane data struct

@return
*/
void e2_pane_create (E2_PaneRuntime *rt)
{
	gint num = (rt == &app.pane1) ? 1 : 2;
	rt->name = g_strdup_printf ("pane%d", num);
	rt->pane_sw = NULL;

	e2_pane_create_option_data (rt);
	//waiiting until first-registration of a hook func may be too late
	g_hook_list_init (&rt->hook_change_dir, sizeof (GHook));
//	g_hook_list_init (&rt->view->hook_refresh, sizeof (GHook));

#ifdef E2_VFSTMP
	//CHECKME path when non-mounted dir cached at session end
#endif
	e2_cache_str_register (rt->name, &rt->path, G_DIR_SEPARATOR_S);
	gchar *freeme = NULL;
	//do we need to over-ride the cached startup dir by a runtime option ?
	gchar *startdir = (num == 1) ? e2_cl_options.pane1_path :
		e2_cl_options.pane2_path ;
	if (startdir != NULL)
		freeme = startdir = F_FILENAME_FROM_LOCALE (startdir);	//don't worry about trailer
#ifdef E2_VFSTMP
	//CHECKME startup dir always local? set other paths accordingly
#endif
	else //or by a config option ?
	{
		gchar *option_name = g_strconcat (rt->name,"-use-startup-dir", NULL);
		if (e2_option_bool_get (option_name))
		{
			g_free (option_name);
			option_name =  g_strconcat (rt->name,"-use-startup-dir-startup-dir", NULL);
			startdir = e2_option_str_get (option_name);
//			freeme = startdir = F_FILENAME_FROM_LOCALE (startdir);
		}
		g_free (option_name);
	}
	if (startdir != NULL)
	{
		g_free (rt->path);
		rt->path = g_strdup (startdir);
	}
	if (freeme != NULL)
		F_FREE (freeme);

	E2_BarType thisbar = (num == 1) ? E2_BAR_PANE1 : E2_BAR_PANE2;
	e2_toolbar_initialise (thisbar);
/* no need to "go back" to places visited in a prior session
	//CHECKME do this again after post-config window recreation ?
	gchar *history_name = g_strconcat (rt->name, "-history", NULL);
	e2_cache_list_register (history_name, &rt->history);
	if (rt->history != NULL)  //if history list found in cache
		rt->opendir_cur = 0;	//g_list_length (rt->history);
	g_free (history_name);
*/
	//register pane-specific actions (all name strings freed in action creation function)
	gchar *pstr = (num == 1) ? _A(11) : _A(12);
	gchar *action_name = g_strconcat (pstr,".",_A(23), NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_COMMAND_LINE,
		e2_command_line_cd_activated_cb, "dir line", TRUE, //name not translated
		E2_ACTION_EXCLUDE_MENU | E2_ACTION_EXCLUDE_ACCEL, rt);

	action_name = g_strconcat (pstr,".",_A(42), NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_focus_action, rt, FALSE);

	action_name = g_strconcat (pstr,".",_A(46), NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_go_forward, rt, FALSE);
	action_name = g_strconcat (pstr,".",_A(45), NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_go_back, rt, FALSE);
	action_name = g_strconcat (pstr,".",_A(47), NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_go_up, rt, FALSE);
	action_name = g_strconcat (pstr,".",_A(60), NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_goto_choice, rt, FALSE);
	action_name = g_strconcat (pstr,".",_A(55),NULL);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_mirror, rt, FALSE);
	action_name = g_strdup ((num == 1) ?
		toggles_array [E2_TOGGLE_PANE1HIDDEN] : toggles_array [E2_TOGGLE_PANE2HIDDEN]);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_toggle_hidden, rt->view, FALSE);
	action_name = g_strconcat (pstr,".",_A(95),NULL);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_select_all, rt, FALSE);
	action_name = g_strconcat (pstr,".",_A(21), NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_BOOKMARKS,
		e2_bookmark_open,
		GINT_TO_POINTER (num), TRUE, E2_ACTION_EXCLUDE_ACCEL | E2_ACTION_EXCLUDE_MENU, NULL);
	action_name = g_strdup ((num == 1) ?
		toggles_array [E2_TOGGLE_PANE1FILTERS] : toggles_array [E2_TOGGLE_PANE2FILTERS]);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_filter_menu_create, rt, FALSE);
#ifdef E2_VFS
	action_name = g_strconcat (pstr,".",_A(117),NULL);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_select_space, rt, TRUE);
	action_name = g_strdup ((num == 1) ?
		toggles_array [E2_TOGGLE_PANE1SPACE] : toggles_array [E2_TOGGLE_PANE2SPACE]);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_vfs_menu_show, rt, FALSE);
#endif
	//after the actions are in place, fill in the pane contents
	e2_pane_create_part (rt);
}
/**
@brief part of pane creation, used also in re-creation

@param rt pointer to pane data struct

@return
*/
void e2_pane_create_part (E2_PaneRuntime *rt)
{
	//existing hooklists are cleared if appropriate in e2_window_recreate()
	rt->pane_sw = e2_fileview_create_list (rt->view);
	rt->outer_box = gtk_hbox_new (FALSE, 0);
	rt->inner_box = gtk_vbox_new (FALSE, 0);
	gtk_box_pack_start_defaults (GTK_BOX (rt->inner_box), rt->pane_sw);
	gtk_box_pack_start_defaults (GTK_BOX (rt->outer_box), rt->inner_box);

	gtk_widget_show (rt->inner_box);
	gtk_widget_show (rt->outer_box);

	e2_toolbar_create (&rt->toolbar);

	if (e2_option_bool_get_direct (rt->toolbar.show))
	{
		//show full/split window button according to cached pane ratio
		if (rt == &app.pane1 && app.window.panes_paned_ratio > 0.999)
			e2_toolbar_toggle_button_set_state
				(toggles_array [E2_TOGGLE_PANE1FULL], TRUE);
		else if (rt == &app.pane2 && app.window.panes_paned_ratio < 0.001)
			e2_toolbar_toggle_button_set_state
				(toggles_array [E2_TOGGLE_PANE2FULL], TRUE);
		//show (un)hide toggle buttons which match corresponding cached state
		E2_ToggleType num = (rt == &app.pane1) ?
			E2_TOGGLE_PANE1HIDDEN : E2_TOGGLE_PANE2HIDDEN;
		e2_toolbar_toggle_button_set_state (toggles_array [num], rt->view->show_hidden);
		num = (rt == &app.pane1) ?
			E2_TOGGLE_PANE1FILTERS : E2_TOGGLE_PANE2FILTERS;
		gboolean filtered =
			(  rt->view->name_filter.active
			|| rt->view->size_filter.active
			|| rt->view->date_filter.active);
		//this needs to be inverted to show correct button
		e2_toolbar_toggle_button_set_state (toggles_array [num], !filtered);
		//set initial filter tooltip
		if (filtered)
			e2_toolbar_toggle_filter_button (rt->view);
#ifdef E2_VFSTMP
		//FIXME
		gchar *tip = "faketip";
		e2_toolbar_update_vfs_toggle_button (rt->view, tip);
#endif
	}
}
/* *
@brief destroy pane data

@param rt pointer to pane data struct

@return
*/
/*static void _e2_pane_destroy (E2_PaneRuntime *rt)
{
	gchar *history_name = g_strconcat (rt->name, "-history", NULL);
	e2_cache_unregister (history_name);
	e2_list_free_with_data (&<GList used for history_name>);
	g_free (history_name);
//	e2_toolbar_destroy (&rt->toolbar);
	//pane toolbar may be outside rt->outer_box, so destroy it independently
	gtk_widget_destroy (rt->toolbar.toolbar_container);
//	gtk_widget_destroy (rt->pane_sw);
	gtk_widget_destroy (rt->outer_box);
	g_hook_list_clear (&rt->hook_change_dir);
}*/
/* *
@brief destroy and re-create specified pane

@param rt pointer to pane data struct

@return
*/
/*void e2_pane_recreate (E2_PaneRuntime *rt)
{
	_e2_pane_destroy (rt);
	e2_pane_create_part (rt);
}*/
/**
@brief register pane-related actions that don't have a pane-specifc action

Actions with pane-specific function are registered in e2_pane_create()

@return
*/
void e2_pane_actions_register (void)
{  //these strings must be freeable
	gchar *action_name = g_strconcat(_A(10),".",_A(46),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_go_forward, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(45),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_go_back, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(47),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_go_up, NULL, FALSE);
	action_name = g_strconcat(_A(13),".",_A(60),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_goto_choice, NULL, FALSE);
	action_name = g_strconcat(_A(6),".",_A(97),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_goto_trash, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(80),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_current_toggle_hidden, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(54),NULL);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_current_invert_all, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(96),NULL);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_current_invert_current, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(95),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_current_select_all, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(81),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		e2_context_menu_show_menu_action, NULL, TRUE,
		E2_ACTION_EXCLUDE_MENU | E2_ACTION_EXCLUDE_TOOLBAR, NULL);
#ifdef E2_TREEDIALOG
	action_name = g_strconcat(_A(10),".",_A(99),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		e2_tree_dialog_show_action, NULL, FALSE);
#endif
	action_name = g_strconcat(_A(4),".",_A(43),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_focus_dirline_action, NULL, TRUE,
		E2_ACTION_EXCLUDE_MENU | E2_ACTION_EXCLUDE_TOOLBAR, NULL);
	action_name = g_strconcat(_A(10),".",_A(42),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_current_focus, NULL, FALSE,
		E2_ACTION_EXCLUDE_MENU | E2_ACTION_EXCLUDE_TOOLBAR, NULL);
	action_name = g_strconcat(_A(10),".",_A(49),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_current_focus_top, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(48),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_current_focus_bottom, NULL, FALSE);
	action_name = g_strconcat(_A(10),".",_A(22),NULL);
	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_filters_show, NULL, FALSE);
	//CHECKME which exclusions for these sort-actions ?
	action_name = g_strconcat(_A(10),".",_A(86),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_sort_active, GINT_TO_POINTER (FILENAME), FALSE,
		0, NULL);
	action_name = g_strconcat(_A(10),".",_A(88),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_sort_active, GINT_TO_POINTER (SIZE), FALSE,
		0, NULL);
	action_name = g_strconcat(_A(10),".",_A(87),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_sort_active, GINT_TO_POINTER (PERM), FALSE,
		0, NULL);
	action_name = g_strconcat(_A(10),".",_A(89),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_sort_active, GINT_TO_POINTER (OWNER), FALSE,
		0, NULL);
	action_name = g_strconcat(_A(10),".",_A(84),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_sort_active, GINT_TO_POINTER (GROUP), FALSE,
		0, NULL);
	action_name = g_strconcat(_A(10),".",_A(85),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_sort_active, GINT_TO_POINTER (MODIFIED), FALSE,
		0, NULL);
	action_name = g_strconcat(_A(10),".",_A(82),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_sort_active, GINT_TO_POINTER (ACCESSED), FALSE,
		0, NULL);
	action_name = g_strconcat(_A(10),".",_A(83),NULL);
	e2_action_register (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_sort_active, GINT_TO_POINTER (CHANGED), FALSE,
		0, NULL);
#ifdef E2_VFS
	action_name = g_strconcat (_A(10),".",_A(117),NULL);
 	e2_action_register_simple (action_name, E2_ACTION_TYPE_ITEM,
		_e2_pane_select_space, NULL, TRUE);
#endif
}
/**
@brief register pane-related config options
Panebar default contents are set here, too
@param num integer pane number (1 or 2)

@return
*/
void e2_pane_options_register (gint num)
{
	gint i; gchar *option_name, *desc, *tip, *grey_me;
	E2_OptionFlags flags;
	desc = (num == 1) ? _A(11) : _A(12);
//	group_name = g_strconcat (parent, ".", _C(3), ":", desc, NULL);    // _("panes.columns:pane N"
	gchar *group_name = g_strconcat ((num == 1) ? _C(28) : _C(30), ":", _C(3), NULL);    // _("paneN:columns"
	for (i = 0; i < MAX_COLUMNS; i++)
	{
		option_name = g_strdup_printf ("pane%d-show-column%d", num, i);
		desc = g_strdup_printf (_("show %s column"), gettext(e2_all_columns[i].title));
		tip = g_strdup_printf (_("This causes the the named column to be displayed in pane %d (duh ...)"), num);
		flags = E2_OPTION_FLAG_BASIC | E2_OPTION_FLAG_FREENAME | E2_OPTION_FLAG_FREEDESC
			| E2_OPTION_FLAG_FREETIP | E2_OPTION_FLAG_BUILDPANES;
		if (i > 0)
			grey_me = (num == 1) ? "!pane1-uses-other" : "!pane2-uses-other";
		else
		{
			flags |= E2_OPTION_FLAG_FREEGROUP;
			//disable hiding of col 0 = filename
			grey_me = g_strconcat ("!",option_name,NULL); //a 'depends' entry = !<self> disables any change
		}
		e2_option_bool_register (option_name, group_name, desc, tip, grey_me,
		TRUE, flags);
	}
	//this needs to be after at least one non-tree options, so that the
	//first-registered option is non-tree and dialog scrolling is setup properly
	e2_toolbar_panebar_register (num);	//setup the pane bar

	if (num == 1)
		e2_toolbar_options_register (E2_BAR_PANE1);  //set general toolbar options
	else
		e2_toolbar_options_register (E2_BAR_PANE2);
	//hack to make general options show after the panes, in basic mode
	if (num == 2)
	{
		for (num = 1; num < 3; num++)
		{
			option_name = g_strdup_printf ("pane%d-use-startup-dir", num);
			gchar *dep = g_strdup (option_name);
			group_name = g_strconcat (_C(17), ":", _C(36), NULL);    // _("general:startup"
			desc = g_strdup_printf (_("At startup, show a specific directory in pane %d"), num);
			e2_option_bool_register (option_name, group_name,
				desc, _("This causes the directory named below, instead of the last-opened directory, to be shown at session start"),
				NULL, FALSE,
				E2_OPTION_FLAG_BASIC | E2_OPTION_FLAG_FREEGROUP
				| E2_OPTION_FLAG_FREENAME | E2_OPTION_FLAG_FREEDESC );
			option_name = g_strconcat (option_name,"-startup-dir", NULL);
			desc = g_strdup_printf (_("pane %d startup directory:"), num);
			e2_option_str_register (option_name, group_name,
				desc,  _("This is the directory to show in this pane, at session start"),
				dep, (num == 1) ? "~" : G_DIR_SEPARATOR_S,   //pane1 default = home, pane2 default = root directory
				E2_OPTION_FLAG_BASIC | E2_OPTION_FLAG_FREENAME
				| E2_OPTION_FLAG_FREEDESC | E2_OPTION_FLAG_FREEDEPENDS);
		}
	}
}
