/*
 * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
 * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
 *
 * This file is part of NetSurf, http://www.netsurf-browser.org/
 *
 * NetSurf 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; version 2 of the License.
 *
 * NetSurf 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/** \file
 * Generic tree handling (implementation).
 */

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "content/content.h"
#include "content/hlcache.h"
#include "css/utils.h"
#include "desktop/browser.h"
#include "desktop/knockout.h"
#include "desktop/textarea.h"
#include "desktop/textinput.h"
#include "desktop/tree.h"
#include "desktop/options.h"
#include "desktop/plotters.h"
#include "render/font.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/utils.h"
#include "utils/url.h"

#undef TREE_NOISY_DEBUG

#define MAXIMUM_URL_LENGTH 1024

#define TREE_TEXT_SIZE_PT 11
#define TREE_ICON_SIZE 17
#define NODE_INSTEP 20

static int tree_text_size_px;
static int TREE_LINE_HEIGHT;

static char *tree_icons_dir = NULL;

static plot_font_style_t plot_fstyle = {
	.family = PLOT_FONT_FAMILY_SANS_SERIF,
	.size = TREE_TEXT_SIZE_PT * FONT_SIZE_SCALE,
	.weight = 400,
	.flags = FONTF_NONE
};

static plot_font_style_t plot_fstyle_def_folder = {
	.family = PLOT_FONT_FAMILY_SANS_SERIF,
	.size = TREE_TEXT_SIZE_PT * FONT_SIZE_SCALE,
	.weight = 700,
	.flags = FONTF_NONE
};

static plot_font_style_t plot_fstyle_selected = {
	.family = PLOT_FONT_FAMILY_SANS_SERIF,
	.size = TREE_TEXT_SIZE_PT * FONT_SIZE_SCALE,
	.weight = 400,
	.flags = FONTF_NONE
};

static plot_font_style_t plot_fstyle_selected_def_folder = {
	.family = PLOT_FONT_FAMILY_SANS_SERIF,
	.size = TREE_TEXT_SIZE_PT * FONT_SIZE_SCALE,
	.weight = 700,
	.flags = FONTF_NONE
};

/** plot style for treeview backgrounds. */
static plot_style_t plot_style_fill_tree_background = {
	.fill_type = PLOT_OP_TYPE_SOLID
};

/** plot style for treeview backgrounds. */
static plot_style_t plot_style_fill_tree_selected = {
	.fill_type = PLOT_OP_TYPE_SOLID
};

/** plot style for treeview furniture lines. */
static plot_style_t plot_style_stroke_tree_furniture = {
	.stroke_type = PLOT_OP_TYPE_SOLID
};

/** plot style for treeview furniture fills. */
static plot_style_t plot_style_fill_tree_furniture = {
	.fill_type = PLOT_OP_TYPE_SOLID
};

struct node;
struct tree;

struct node_element_box {
	int x;				/**< X offset from origin */
	int y;				/**< Y offset from origin */
	int width;			/**< Element width */
	int height;			/**< Element height */
};

struct node_element {
	struct node *parent;		/**< Parent node */
	node_element_type type;		/**< Element type */
	struct node_element_box box;	/**< Element bounding box */
	const char *text;		/**< Text for the element */
	void *bitmap;			/**< Bitmap for the element */
	struct node_element *next;	/**< Next node element */
	unsigned int flag;		/**< Client specified flag for data
					   being represented */
	bool editable;			/**< Whether the node text can be
					 * modified, editable text is deleted
					 * without noticing the tree user
					 */
};

struct node {
	bool selected;			/**< Whether the node is selected */
	bool expanded;			/**< Whether the node is expanded */
	bool folder;			/**< Whether the node is a folder */
	bool def_folder;		/**< Whether the node is the default folder */
	bool retain_in_memory;		/**< Whether the node remains
					   in memory after deletion */
	bool deleted;			/**< Whether the node is currently
					   deleted */
	bool processing;		/**< Internal flag used when moving */
	struct node_element_box box;	/**< Bounding box of all elements */
	struct node_element data;	/**< Data to display */
	struct node *parent;		/**< Parent entry (NULL for root) */
	struct node *child;		/**< First child */
	struct node *last_child;	/**< Last child */
	struct node *previous;		/**< Previous child of the parent */
	struct node *next;		/**< Next child of the parent */

	/** Sorting function for	the node (for folder nodes only) */
	int (*sort) (struct node *, struct node *);
	/** Gets called for each deleted node_element and on node launch */
	tree_node_user_callback user_callback;
	/** User data to be passed to delete_callback */
	void *callback_data;
};

struct tree {
	struct node *root;		/* Tree root element */
	int width;			/* Tree width */
	int height;			/* Tree height */
	unsigned int flags;		/* Tree flags */
	struct text_area *textarea;	/* Handle for UTF-8 textarea */
	bool textarea_drag_start;	/* whether the start of a mouse drag
					   was in the textarea */
	struct node_element *editing;	/* Node element being edited */

	bool redraw;			/* Flag indicating whether the tree
					   should be redrawn on layout
					   changes */
	tree_drag_type drag;
	const struct treeview_table *callbacks;
	void *client_data;		/* User assigned data for the
					   callbacks */
	struct node *def_folder;	/* Node to be used for additions by default */
};

void tree_set_icon_dir(char *icon_dir)
{
	LOG(("Tree icon directory set to %s", icon_dir));
	tree_icons_dir = icon_dir;
}

/**
 * Set up colours for plot styles used in tree redraw.
 */
static void tree_setup_colours(void)
{
	/* Background colour */
	plot_style_fill_tree_background.fill_colour = option_gui_colour_bg_1;

	/* Selection background colour */
	plot_style_fill_tree_selected.fill_colour = option_gui_colour_fg_1;

	/* Furniture line colour */
	plot_style_stroke_tree_furniture.stroke_colour = blend_colour(
			option_gui_colour_bg_1, option_gui_colour_fg_1);

	/* Furniture fill colour */
	plot_style_fill_tree_furniture.fill_colour = option_gui_colour_fg_2;

	/* Text colour */
	plot_fstyle.foreground = option_gui_colour_fg_1;
	plot_fstyle.background = option_gui_colour_bg_1;
	plot_fstyle_def_folder.foreground = option_gui_colour_fg_1;
	plot_fstyle_def_folder.background = option_gui_colour_bg_1;

	/* Selected text colour */
	plot_fstyle_selected.foreground = option_gui_colour_fg_2;
	plot_fstyle_selected.background = option_gui_colour_fg_1;
	plot_fstyle_selected_def_folder.foreground = option_gui_colour_fg_2;
	plot_fstyle_selected_def_folder.background = option_gui_colour_fg_1;
}


/**
 * Creates and initialises a new tree.
 *
 * \param flags		Flag word for flags to create the new tree with
 * \param callbacks	Callback functions to support the tree in the frontend.
 * \param client_data	Data to be passed to start_redraw and end_redraw
 * \return		The newly created tree, or NULL on memory exhaustion
 */
struct tree *tree_create(unsigned int flags,
		const struct treeview_table *callbacks, void *client_data)
{
	struct tree *tree;
	char *title;

	tree = calloc(sizeof(struct tree), 1);
	if (tree == NULL) {
		LOG(("calloc failed"));
		warn_user("NoMemory", 0);
		return NULL;
	}

	title = strdup("Root");
	if (title == NULL) {
		LOG(("malloc failed"));
		warn_user("NoMemory", 0);
		free(tree);
		return NULL;
	}
	tree->root = tree_create_folder_node(NULL, NULL, title,
					     false, false, false);
	if (tree->root == NULL) {
		free(title);
		free(tree);
		return NULL;
	}
	tree->root->expanded = true;

	tree->width = 0;
	tree->height = 0;
	tree->flags = flags;
	tree->textarea = NULL;
	tree->textarea_drag_start = false;
	tree->editing = NULL;
	tree->redraw = false;
	tree->drag = TREE_NO_DRAG;
	tree->callbacks = callbacks;
	tree->client_data = client_data;

	/* Set text height in pixels */
	tree_text_size_px =
			(TREE_TEXT_SIZE_PT * FIXTOINT(nscss_screen_dpi) + 36) /
			72;
	/* Set line height appropriate for this text height in pixels
	 * Using 4/3 text height */
	TREE_LINE_HEIGHT = (tree_text_size_px * 8 + 3) / 6;

	/* But if that's too small for the icons, base the line height on
	 * the icon height. */
	if (TREE_LINE_HEIGHT < TREE_ICON_SIZE + 2)
		TREE_LINE_HEIGHT = TREE_ICON_SIZE + 2;

	tree_setup_colours();

	return tree;
}


/**
 * Recalculates the dimensions of a node element.
 *
 * \param tree	   the tree to which the element belongs, may be NULL
 * \param element  the element to recalculate
 */
static void tree_recalculate_node_element(struct tree *tree,
		struct node_element *element)
{
	struct bitmap *bitmap = NULL;
	int width, height;
	static char *cache_text = NULL;
	static int cache_size = 0;
	plot_font_style_t *fstyle;
	static plot_font_style_t *cache_fstyle = NULL;

	assert(element != NULL);

	if (element->parent->def_folder)
		fstyle = &plot_fstyle_def_folder;
	else
		fstyle = &plot_fstyle;

	switch (element->type) {
	case NODE_ELEMENT_TEXT_PLUS_ICON:
	case NODE_ELEMENT_TEXT:
		if(element->text == NULL)
			break;

		if (tree != NULL && element == tree->editing) {
			textarea_get_dimensions(tree->textarea,
						&element->box.width, NULL);
		} else {
			if ((cache_text != NULL) &&
				(strcmp(cache_text, element->text) == 0) &&
				(cache_fstyle == fstyle)) {
				element->box.width = cache_size;
				#ifdef TREE_NOISY_DEBUG
					LOG(("Tree font width cache hit"));
				#endif
			} else {
				if(cache_text != NULL) free(cache_text);
				nsfont.font_width(fstyle,
						  element->text,
						  strlen(element->text),
						  &cache_size);
				element->box.width = cache_size;
				cache_text = strdup(element->text);
				cache_fstyle = fstyle;
			}
		}

		element->box.width += 8;
		element->box.height = TREE_LINE_HEIGHT;

		if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON)
			element->box.width += NODE_INSTEP;

		break;

	case NODE_ELEMENT_BITMAP:
		bitmap = element->bitmap;
		if (bitmap != NULL) {
			width = bitmap_get_width(bitmap);
			height = bitmap_get_height(bitmap);
			element->box.width = width + 1;
			element->box.height = height + 2;
		} else {
			element->box.width = 0;
			element->box.height = 0;
		}
		break;
	}
}


/**
 * Calculates the height of a node including any children
 *
 * \param node	the node to calculate the height of
 * \return the total height of the node and children
 */
static int tree_get_node_height(struct node *node)
{
	int y1;

	assert(node != NULL);

	if ((node->child == NULL) || (node->expanded == false)) {
		return node->box.height;
	}

	y1 = node->box.y;
	if (y1 < 0) {
		y1 = 0;
	}
	node = node->child;

	while ((node->next != NULL) ||
	       ((node->child != NULL) && (node->expanded))) {
		for (; node->next != NULL; node = node->next);

		if ((node->child != NULL) && (node->expanded)) {
			node = node->child;
		}
	}
	return node->box.y + node->box.height - y1;
}


/**
 * Calculates the width of a node including any children
 *
 * \param node	the node to calculate the height of
 * \return the total width of the node and children
 */
static int tree_get_node_width(struct node *node)
{
	int width = 0;
	int child_width;

	assert(node != NULL);

	for (; node != NULL; node = node->next) {
		if (width < (node->box.x + node->box.width)) {
			width = node->box.x + node->box.width;
		}

		if ((node->child != NULL) && (node->expanded)) {
			child_width = tree_get_node_width(node->child);
			if (width < child_width) {
				width = child_width;
			}
		}
	}
	return width;
}


/**
 * Recalculates the position of a node, its siblings and children.
 *
 * \param tree	the tree to which 'root' belongs
 * \param root	the root node to update from
 */
static void tree_recalculate_node_positions(struct tree *tree,
		struct node *root)
{
	struct node *parent;
	struct node *node;
	struct node *child;
	struct node_element *element;
	int y;
	bool has_icon;

	for (node = root; node != NULL; node = node->next) {

		parent = node->parent;

		if (node->previous != NULL) {
			node->box.x = node->previous->box.x;
			node->box.y = node->previous->box.y +
				tree_get_node_height(node->previous);
		} else if (parent != NULL) {
			node->box.x = parent->box.x + NODE_INSTEP;
			node->box.y = parent->box.y +
				parent->box.height;
			for (child = parent->child; child != node;
			     child = child->next)
				node->box.y += child->box.height;
		} else {
			node->box.x = tree->flags & TREE_NO_FURNITURE
				? -NODE_INSTEP + 4 : 0;
			node->box.y = -TREE_LINE_HEIGHT;
		}

		if (!node->expanded) {
			node->data.box.x = node->box.x;
			node->data.box.y = node->box.y;
			continue;
		}

		if (node->folder) {
			node->data.box.x = node->box.x;
			node->data.box.y = node->box.y;
			tree_recalculate_node_positions(tree, node->child);
		} else {
			y = node->box.y;
			has_icon = false;
			for (element = &node->data; element != NULL;
			     element = element->next)
				if (element->type ==
				    NODE_ELEMENT_TEXT_PLUS_ICON) {
					has_icon = true;
					break;
				}

			for (element = &node->data; element != NULL;
			     element = element->next) {
				element->box.x = node->box.x;
				if (element->type !=
				    NODE_ELEMENT_TEXT_PLUS_ICON &&
				    has_icon)
					element->box.x += NODE_INSTEP;
				element->box.y = y;
				y += element->box.height;
			}
		}

	}
}


/**
 * Recalculates the size of a node.
 *
 * \param tree			the tree to which node belongs, may be NULL
 * \param node			the node to update
 * \param recalculate_sizes	whether the node elements have changed
 */
static void tree_recalculate_node_sizes(struct tree *tree, struct node *node,
		bool recalculate_sizes)
{
	struct node_element *element;
	int height;

	assert(node != NULL);

	height = node->box.height;
	node->box.width = 0;
	node->box.height = 0;
	if (node->expanded) {
		for (element = &node->data; element != NULL;
		     element = element->next) {
			if (recalculate_sizes) {
				#ifdef TREE_NOISY_DEBUG
					if(element->text) LOG(("%s", element->text));
				#endif
				tree_recalculate_node_element(tree, element);
			}
			node->box.width = (node->box.width > element->box.x +
					   element->box.width - node->box.x) ?
				node->box.width :
				element->box.width + element->box.x -
				node->box.x;
			node->box.height += element->box.height;
		}
	} else {
		if (recalculate_sizes)
			for (element = &node->data; element != NULL;
			     element = element->next) {
				#ifdef TREE_NOISY_DEBUG
					if(element->text) LOG(("%s", element->text));
				#endif
				tree_recalculate_node_element(tree, element);
			}

		node->box.width = node->data.box.width;
		node->box.height = node->data.box.height;
	}

	if (tree != NULL && height != node->box.height)
		tree_recalculate_node_positions(tree, tree->root);
}


/**
 * Creates a folder node with the specified title, and optionally links it into
 * the tree.
 *
 * \param tree		    the owner tree of 'parent', may be NULL
 * \param parent	    the parent node, or NULL not to link
 * \param title		    the node title (not copied, used directly)
 * \param editable	    if true, the node title will be editable
 * \param retain_in_memory  if true, the node will stay in memory after deletion
 * \param deleted	    if true, the node is created with the deleted flag
 * \return		    the newly created node.
 */
struct node *tree_create_folder_node(struct tree *tree, struct node *parent,
		const char *title, bool editable, bool retain_in_memory,
		bool deleted)
{
	struct node *node;

	assert(title != NULL);

	node = calloc(sizeof(struct node), 1);
	if (node == NULL) {
		LOG(("calloc failed"));
		warn_user("NoMemory", 0);
		return NULL;
	}
	node->folder = true;
	node->retain_in_memory = retain_in_memory;
	node->deleted = deleted;
	node->data.parent = node;
	node->data.type = NODE_ELEMENT_TEXT;
	node->data.text = title;
	node->data.flag = TREE_ELEMENT_TITLE;
	node->data.editable = editable;
	node->sort = NULL;
	node->user_callback = NULL;
	node->previous = NULL;

	tree_recalculate_node_sizes(tree, node, true);
	if (parent != NULL)
		tree_link_node(tree, parent, node, false);

	return node;
}


/**
 * Creates a leaf node with the specified title, and optionally links it into
 * the tree.
 *
 * \param tree		    the owner tree of 'parent', may be NULL
 * \param parent	    the parent node, or NULL not to link
 * \param title		    the node title (not copied, used directly)
 * \param editable	    if true, the node title will be editable
 * \param retain_in_memory  if true, the node will stay in memory after deletion
 * \param deleted	    if true, the node is created with the deleted flag
 * \return		    the newly created node.
 */
struct node *tree_create_leaf_node(struct tree *tree, struct node *parent,
		const char *title, bool editable, bool retain_in_memory,
		bool deleted)
{
	struct node *node;

	assert(title != NULL);

	node = calloc(sizeof(struct node), 1);
	if (node == NULL) {
		LOG(("calloc failed"));
		warn_user("NoMemory", 0);
		return NULL;
	}

	node->folder = false;
	node->retain_in_memory = retain_in_memory;
	node->deleted = deleted;
	node->data.parent = node;
	node->data.type = NODE_ELEMENT_TEXT;
	node->data.text = title;
	node->data.flag = TREE_ELEMENT_TITLE;
	node->data.editable = editable;
	node->sort = NULL;
	node->user_callback = NULL;
	node->previous = NULL;

	tree_recalculate_node_sizes(tree, node, true);
	if (parent != NULL)
		tree_link_node(tree, parent, node, false);

	return node;
}


/**
 * Creates an empty text node element and links it to a node.
 *
 * \param parent  the parent node
 * \param type	  the required element type
 * \param flag	  user assigned flag used for searches
 * \return	  the newly created element.
 */
struct node_element *tree_create_node_element(struct node *parent,
		node_element_type type, unsigned int flag, bool editable)
{
	struct node_element *element;

	element = calloc(sizeof(struct node_element), 1);
	if (element == NULL)
		return NULL;

	element->parent = parent;
	element->flag = flag;
	element->type = type;
	element->editable = editable;
	element->next = parent->data.next;
	parent->data.next = element;

	return element;
}


/**
 * Inserts a node into the correct place according to the parent's sort function
 *
 * \param parent  the node whose child node 'node' becomes
 * \param node	  the node to be inserted
 */
static void tree_sort_insert(struct node *parent, struct node *node)
{
	struct node *after;

	assert(node != NULL);
	assert(parent != NULL);
	assert(parent->sort != NULL);

	after = parent->last_child;
	while ((after != NULL) &&
	       (parent->sort(node, after) == -1))
		after = after->previous;

	if (after != NULL) {
		if (after->next != NULL)
			after->next->previous = node;
		node->next = after->next;
		node->previous = after;
		after->next = node;
	} else {
		node->previous = NULL;
		node->next = parent->child;
		if (parent->child != NULL) {
			parent->child->previous = node;
		}
		parent->child = node;
	}

	if (node->next == NULL)
		parent->last_child = node;

	node->parent = parent;
}


/**
 * Recalculates the size of a tree.
 *
 * \param tree	the tree to recalculate
 */
static void tree_recalculate_size(struct tree *tree)
{
	int width, height;

	assert(tree != NULL);

	width = tree->width;
	height = tree->height;

	tree->width = tree_get_node_width(tree->root);
	tree->height = tree_get_node_height(tree->root);

	if ((width != tree->width) || (height != tree->height))
		tree->callbacks->resized(tree, tree->width, tree->height,
					 tree->client_data);
}

/**
 * Recalculate the node data and redraw the relevant section of the tree.
 *
 * \param tree		     the tree to redraw, may be NULL
 * \param node		     the node to update
 * \param recalculate_sizes  whether the elements have changed
 * \param expansion	     the request is the result of a node expansion
 */
static void tree_handle_node_changed(struct tree *tree, struct node *node,
		bool recalculate_sizes, bool expansion)
{
	int node_width, node_height, tree_width, tree_height;

	assert(node != NULL);

	node_width = node->box.width;
	node_height = node->box.height;
	tree_width = tree->width;
	tree_height = tree->height;

	if ((recalculate_sizes) || (expansion)) {
		tree_recalculate_node_sizes(tree, node, true);
	}

	if (tree != NULL) {
		if ((node->box.height != node_height) || (expansion)) {
			tree_recalculate_node_positions(tree, tree->root);
			tree_recalculate_size(tree);
			if (tree->width > tree_width)
				tree_width = tree->width;
			if (tree->height > tree_height)
				tree_height = tree->height;
			if (tree->redraw) {
				tree->callbacks->redraw_request(0, node->box.y,
						tree_width,
						tree_height - node->box.y,
						tree->client_data);
			}
		} else {
			if (node->box.width > node_width)
				node_width = node->box.width;
			if (tree->redraw)
				tree->callbacks->redraw_request(node->box.x,
						node->box.y,
						node_width, node->box.height,
						tree->client_data);
			if (recalculate_sizes) {
				tree_recalculate_size(tree);
			}
		}
	}
}


/**
 * Links a node to another node.
 *
 * \param tree	  the tree in which the link takes place, may be NULL
 * \param link	  the node to link before/as a child (folders)
 *		  or before/after (link)
 * \param node	  the node to link
 * \param before  whether to link siblings before or after the supplied node
 */
void tree_link_node(struct tree *tree, struct node *link, struct node *node,
		bool before)
{

	struct node *parent;
	bool sort = false;

	assert(link != NULL);
	assert(node != NULL);

	if ((link->folder == 0) || (before)) {
		parent = node->parent = link->parent;
		if (parent->sort) {
			sort = true;
		} else {
			if (before) {
				node->next = link;
				node->previous = link->previous;
				if (link->previous != NULL)
					link->previous->next = node;
				link->previous = node;
				if ((parent != NULL) && (parent->child == link))
					parent->child = node;
			} else {
				node->previous = link;
				node->next = link->next;
				if (link->next != NULL)
					link->next->previous = node;
				link->next = node;
				if ((parent != NULL) &&
				    (parent->last_child == link))
					parent->last_child = node;
			}
		}
	} else {
		parent = node->parent = link;
		if (parent->sort != NULL) {
			sort = true;
		} else {
			node->next = NULL;
			if (link->child == NULL) {
				link->child = link->last_child = node;
				node->previous = NULL;
			} else {
				link->last_child->next = node;
				node->previous = link->last_child;
				link->last_child = node;
			}
		}

	}

	if (sort) {
		tree_sort_insert(parent, node);
	}

	tree_handle_node_changed(tree, link, false, true);

	node->deleted = false;
}


/**
 * Recalculate the node element and redraw the relevant section of the tree.
 * The tree size is not updated.
 *
 * \param tree	   the tree to redraw, may be NULL
 * \param element  the node element to update
 */
static void tree_handle_node_element_changed(struct tree *tree,
		struct node_element *element, bool text_changed)
{
	int width, height;

	assert(element != NULL);

	width = element->box.width;
	height = element->box.height;

	if(text_changed == true) {
		#ifdef TREE_NOISY_DEBUG
			if(element->text) LOG(("%s", element->text));
		#endif
		tree_recalculate_node_element(tree, element);
	}

	if (element->box.height != height) {
		tree_recalculate_node_sizes(tree, element->parent, false);
		if ((tree != NULL) && (tree->redraw)) {
			tree->callbacks->redraw_request(0, element->box.y,
					tree->width + element->box.width -
					width,
					tree->height - element->box.y +
					element->box.height - height,
					tree->client_data);
		}
	} else {
		if (element->box.width != width) {
			tree_recalculate_node_sizes(tree, element->parent,
						    false);
		}

		if (tree != NULL) {
			width = (width > element->box.width) ? width :
				element->box.width;
			if (tree->redraw) {
				tree->callbacks->redraw_request(element->box.x,
						element->box.y,
						width,
						element->box.height,
						tree->client_data);
			}
		}
	}
}


/**
 * Stops editing a node_element
 *
 * \param tree		The tree to stop editing for
 * \param keep_changes	If true the changes made to the text will be kept,
 *			if false they will be dropped
 */
static void tree_stop_edit(struct tree *tree, bool keep_changes)
{
	int text_len;
	char *text = NULL;
	struct node_element *element;
	struct node_msg_data msg_data;
	node_callback_resp response;

	assert(tree != NULL);

	if (tree->editing == NULL || tree->textarea == NULL)
		return;

	element = tree->editing;

	if (keep_changes) {
		text_len = textarea_get_text(tree->textarea, NULL, 0);
		text = malloc(text_len * sizeof(char));
		if (text == NULL) {
			LOG(("malloc failed"));
			warn_user("NoMemory", 0);
			textarea_destroy(tree->textarea);
			tree->textarea = NULL;
			return;
		}
		textarea_get_text(tree->textarea, text, text_len);
	}


	if (keep_changes && element->parent->user_callback != NULL) {
		msg_data.msg = NODE_ELEMENT_EDIT_FINISHING;
		msg_data.flag = element->flag;
		msg_data.node = element->parent;
		msg_data.data.text = text;
		response = element->parent->user_callback(
			element->parent->callback_data,
			&msg_data);

		switch (response) {
		case NODE_CALLBACK_REJECT:
			free(text);
			text = NULL;
			break;
		case NODE_CALLBACK_CONTINUE:
			free(text);
			text = NULL;
			return;
		case NODE_CALLBACK_HANDLED:
		case NODE_CALLBACK_NOT_HANDLED:
			text = msg_data.data.text;
			break;
		}
	}

	textarea_destroy(tree->textarea);
	tree->textarea = NULL;
	tree->editing = NULL;

	if (text != NULL)
		tree_update_node_element(tree, element, text, NULL);
	else
		tree_handle_node_element_changed(tree, element, true);


	tree_recalculate_size(tree);
	if (element->parent->user_callback != NULL) {
		msg_data.msg = keep_changes ? NODE_ELEMENT_EDIT_FINISHED :
				NODE_ELEMENT_EDIT_CANCELLED;
		msg_data.flag = element->flag;
		msg_data.node = element->parent;
		element->parent->user_callback(element->parent->callback_data,
					       &msg_data);
	}
}


/**
 * Delinks a node from the tree structures.
 *
 * \param tree	the tree in which the delink takes place, may be NULL
 * \param node	the node to delink
 */
void tree_delink_node(struct tree *tree, struct node *node)
{
	struct node *parent;

	assert(node != NULL);

	/* do not remove the root */
	if (tree != NULL && node == tree->root)
		return;
	if ((tree != NULL) && (tree->editing != NULL)) {
		parent = tree->editing->parent;
		while (parent != NULL) {
			if (node == parent) {
				tree_stop_edit(tree, false);
				break;
			}
			parent = parent->parent;
		}
	}

	if (node->parent->child == node)
		node->parent->child = node->next;
	if (node->parent->last_child == node)
		node->parent->last_child = node->previous;
	parent = node->parent;
	node->parent = NULL;

	if (node->previous != NULL)
		node->previous->next = node->next;
	if (node->next != NULL)
		node->next->previous = node->previous;
	node->previous = NULL;
	node->next = NULL;

	tree_handle_node_changed(tree, parent, false, true);
}


/**
 * Deletes a node from the tree.
 *
 * \param tree	    the tree to delete from, may be NULL
 * \param node	    the node to delete
 * \param siblings  whether to delete all siblings
 */
static void tree_delete_node_internal(struct tree *tree, struct node *node,
		bool siblings)
{
	struct node *next, *child, *parent;
	struct node_element *e, *f;
	node_callback_resp response;
	struct node_msg_data msg_data;

	assert(node != NULL);

	if (tree != NULL && tree->root == node)
		return;

	next = node->next;
	parent = node->parent;
	if (tree != NULL && parent == tree->root)
		parent = NULL;
	if ((tree != NULL) && (tree->def_folder == node))
		tree->def_folder = NULL;
	tree_delink_node(tree, node);
	child = node->child;
	node->child = NULL;

	node->deleted = true;
	if (child != NULL)
		tree_delete_node_internal(tree, child, true);

	if (!node->retain_in_memory) {
		node->retain_in_memory = true;
		for (e = &node->data; e != NULL; e = f) {
			if (e->text != NULL) {
				response = NODE_CALLBACK_NOT_HANDLED;
				if (!e->editable &&
				    node->user_callback != NULL) {
					msg_data.msg = NODE_DELETE_ELEMENT_TXT;
					msg_data.flag = e->flag;
					msg_data.node = node;
					msg_data.data.text = (void *)e->text;
					response = node->user_callback(
						node->callback_data,
						&msg_data);
				}
				if (response != NODE_CALLBACK_HANDLED)
					free((void *)e->text);
				e->text = NULL;
			}
			if (e->bitmap != NULL) {
				response = NODE_CALLBACK_NOT_HANDLED;
				if (node->user_callback != NULL) {
					msg_data.msg = NODE_DELETE_ELEMENT_IMG;
					msg_data.flag = e->flag;
					msg_data.node = node;
					msg_data.data.bitmap =
						(void *)e->bitmap;
					response = node->user_callback(
						node->callback_data,
						&msg_data);
				}
				/* TODO the type of this field is platform
				   dependent */
				if (response != NODE_CALLBACK_HANDLED)
					free(e->bitmap);
				e->bitmap = NULL;
			}
			f = e->next;
			if (e != &node->data)
				free(e);
		}
		free(node);
	}

	if (siblings && next)
		tree_delete_node_internal(tree, next, true);
	if ((tree->flags & TREE_DELETE_EMPTY_DIRS) && parent != NULL &&
	    parent->child == NULL && !parent->deleted)
		tree_delete_node_internal(tree, parent, false);
}


/**
 * Deletes all nodes of a tree and the tree itself.
 *
 * \param tree the tree to be deleted
 */
void tree_delete(struct tree *tree)
{
	tree->redraw = false;

	if (tree->root->child != NULL)
		tree_delete_node_internal(tree, tree->root->child, true);

	free((void *)tree->root->data.text);
	free(tree->root);
	free(tree);
}


/**
 * Gets the redraw property of the given tree.
 *
 * \param tree	the tree for which to retrieve the property
 * \return	the redraw property of the tree
 */
bool tree_get_redraw(struct tree *tree)
{
	return tree->redraw;
}


/**
 * Deletes a node from the tree.
 *
 * \param tree	    the tree to delete from, may be NULL
 * \param node	    the node to delete
 * \param siblings  whether to delete all siblings
 */
void tree_delete_node(struct tree *tree, struct node *node, bool siblings)
{
	int y = node->box.y;
	int height = tree->height;
	int width = tree->width;
	bool redraw_setting = tree->redraw;

	tree->redraw = false;

	tree_delete_node_internal(tree, node, siblings);
	tree_recalculate_node_positions(tree, tree->root);

	tree->redraw = redraw_setting;

	if (tree->redraw)
		tree->callbacks->redraw_request(0, y,
				width, height, tree->client_data);
	tree_recalculate_size(tree);
}


/**
 * Sets an icon for a node
 *
 * \param tree		The tree to which node belongs, may be NULL
 * \param node		The node for which the icon is set
 * \param icon		the image to use
 */
void tree_set_node_icon(struct tree *tree, struct node *node,
		hlcache_handle *icon)
{
	node->data.type = NODE_ELEMENT_TEXT_PLUS_ICON;
	tree_update_node_element(tree, &(node->data), NULL, icon);
}


/**
 * Updates all siblings and descendants of a node to an expansion state.
 * No update is performed for the tree changes.
 *
 * \param tree		the tree to which 'node' belongs
 * \param node		the node to set all siblings and descendants of
 * \param expanded	the expansion state to set
 */
static void tree_set_node_expanded_all(struct tree *tree, struct node *node,
		bool expanded)
{
	for (; node != NULL; node = node->next) {
		if (node->expanded != expanded) {
			node->expanded = expanded;
			tree_recalculate_node_sizes(tree, node, false);
		}
		if ((node->child != NULL) && (node->expanded))
			tree_set_node_expanded_all(tree, node->child, expanded);
	}
}


/**
 * Updates [all siblings and descendants of] a node to an expansion state.
 *
 * \param tree	    the tree to update
 * \param node	    the node to set [all siblings and descendants of]
 * \param expanded  the expansion state to set
 * \param folder    whether to update folders, if this together with leaf
 *		    will be false only 'node' will be updated
 * \param leaf	    whether to update leaves (check also description for folder)
 * \return	    whether any changes were made
 */
static bool tree_set_node_expanded_internal(struct tree *tree,
		struct node *node, bool expanded, bool folder, bool leaf)
{
	bool redraw = false;
	struct node *end = (folder == false && leaf == false) ?
		node->next : NULL;

	if (tree->editing != NULL && node == tree->editing->parent)
		tree_stop_edit(tree, false);

	for (; node != end; node = node->next) {
		if ((node->expanded != expanded) && (node != tree->root) &&
		    ((folder && (node->folder)) ||
		     (leaf && (!node->folder)) ||
		     (!folder && !leaf))) {
			node->expanded = expanded;
			if (node->child != NULL)
				tree_set_node_expanded_all(tree,
							   node->child, false);
			if ((node->data.next != NULL) &&
			    (node->data.next->box.height == 0))
				tree_recalculate_node_sizes(tree, node, true);
			else
				tree_recalculate_node_sizes(tree, node, false);
			redraw = true;
		}
		if ((folder || leaf) && (node->child != NULL) &&
		    (node->expanded))
			redraw |= tree_set_node_expanded_internal(tree,
								  node->child, expanded, folder, leaf);
	}
	return redraw;
}


/**
 * Updates [all siblings and descendants of] a node to an expansion state.
 *
 * \param tree	    the tree to update
 * \param node	    the node to set [all siblings and descendants of]
 * \param expanded  the expansion state to set
 * \param folder    whether to update folders, if this together with leaf
 *		    will be false only 'node' will be updated
 * \param leaf	    whether to update leaves (check also description for folder)
 */
void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded,
		bool folder, bool leaf)
{
	if (tree_set_node_expanded_internal(tree, node, expanded, folder, leaf))
		tree_handle_node_changed(tree, node, false, true);
}


/**
 * Updates a node to an selected state. The required areas of the tree are
 * redrawn.
 *
 * \param tree	    the tree to update nodes for, may be NULL
 * \param node	    the node to set all siblings and descendants of
 * \param all	    if true update node together with its siblings and
 *		    descendants
 * \param selected  the selection state to set
 */
void tree_set_node_selected(struct tree *tree, struct node *node, bool all,
		bool selected)
{
	struct node *end;

	if (tree != NULL && node == tree->root)
		node = tree->root->child;
	if (node == NULL)
		return;

	end = all ? NULL : node->next;

	for (; node != end; node = node->next) {
		if (node->selected != selected) {
			node->selected = selected;
			if (tree != NULL && tree->redraw)
				tree->callbacks->redraw_request(
						node->data.box.x,
						node->data.box.y,
						node->data.box.width,
						node->data.box.height,
						tree->client_data);
		}
		if (all && (node->child != NULL) && (node->expanded))
			tree_set_node_selected(tree, node->child, all,
					       selected);
	}
}


/**
 * Sets the sort function for a node
 *
 * \param tree	the tree to which 'node' belongs, may be NULL
 * \param node	the node to be inserted
 * \param sort	pointer to the sorting function
 */
void tree_set_node_sort_function(struct tree *tree, struct node *node,
		int (*sort) (struct node *, struct node *))
{
	struct node *child;

	node->sort = sort;

	if (tree != NULL && tree->editing != NULL)
		tree_stop_edit(tree, false);

	/* the node had already some children so they must get sorted */
	if (node->child != NULL) {

		child = node->child;
		node->child = NULL;

		while (child != NULL) {
			tree_sort_insert(node, child);
			child = child->next;
		}

	}

	if (tree != NULL)
		tree_recalculate_node_positions(tree, node->child);
}


/**
 * Sets the delete callback for a node.
 *
 * \param node	    the node for which the callback is set
 * \param callback  the callback functions to be set
 * \param data	    user data to be passed to callback
 */
void tree_set_node_user_callback(struct node *node,
		tree_node_user_callback callback, void *data)
{
	node->user_callback = callback;
	node->callback_data = data;
}


/**
 * Sets the redraw property to the given value. If redraw is true, the tree will
 * be redrawn on layout/appearance changes.
 *
 * \param tree	  the tree for which the property is set
 * \param redraw  the value to set
 */
void tree_set_redraw(struct tree *tree, bool redraw)
{
	/* the tree might have no graphical representation, do not set the
	   redraw flag in such case */
	if (tree->callbacks == NULL)
		return;
	tree->redraw = redraw;
}


/**
 * Checks whether a node, its siblings or any children are selected.
 *
 * \param node	the root node to check from
 * \return	whether 'node', its siblings or any children are selected.
 */
bool tree_node_has_selection(struct node *node)
{
	for (; node != NULL; node = node->next) {
		if (node->selected)
			return true;
		if ((node->child != NULL) && (node->expanded) &&
		    (tree_node_has_selection(node->child)))
			return true;
	}
	return false;
}


/**
 * Returns the current value of the nodes deleted property.
 *
 * \param node	the node to be checked
 * \return	the current value of the nodes deleted property
 */
bool tree_node_is_deleted(struct node *node)
{
	return node->deleted;
}


/**
 * Returns true if the node is a folder
 *
 * \param node	the node to be checked
 * \return	true if the node is a folder, false otherwise
 */
bool tree_node_is_folder(struct node *node)
{
	return node->folder;
}


/**
 * Returns true if the node is the default folder for a tree
 *
 * \param node	the node to be checked
 * \return	true if the node is a default folder, false otherwise
 */
bool tree_node_is_default(struct node *node)
{
	return node->def_folder;
}


/**
 * Update the text of a node element if it has changed.
 *
 * \param element The node element to update.
 * \param text The text to update the element with. The ownership of
 *	       this string is taken by this function and must not be
 *	       referred to after the function exits.
 */
bool tree_update_element_text(struct tree *tree,
		struct node_element *element, char *text)
{
	const char *node_text; /* existing node text */

	if (text == NULL)
		return false;

	if (element == NULL) {
		free(text);
		return false;
	}

	node_text = tree_node_element_get_text(element);

	if ((node_text == NULL) || (strcmp(node_text, text) != 0)) {
		tree_update_node_element(tree, element, text, NULL);
	} else {
		/* text does not need changing, free it */
		free(text);
	}
	return true;
}


/**
 * Updates the content of a node_element.
 *
 * \param tree	    the tree owning element, may be NULL
 * \param element   the element to be updated
 * \param text	    new text to be set, may be NULL
 * \param bitmap    new bitmap to be set, may be NULL
 */
void tree_update_node_element(struct tree *tree, struct node_element *element,
		const char *text, void *bitmap)
{
	node_callback_resp response;
	struct node_msg_data msg_data;
	bool text_changed = false;

	assert(element != NULL);

	if (tree != NULL && element == tree->editing)
		tree_stop_edit(tree, false);

	if (text != NULL && (element->type == NODE_ELEMENT_TEXT ||
			     element->type == NODE_ELEMENT_TEXT_PLUS_ICON)) {
		if (element->text != NULL) {
			if(strcmp(element->text, text) == 0) text_changed = true;

			response = NODE_CALLBACK_NOT_HANDLED;
			if (!element->editable &&
			    element->parent->user_callback !=
			    NULL) {
				msg_data.msg = NODE_DELETE_ELEMENT_TXT;
				msg_data.flag = element->flag;
				msg_data.node = element->parent;
				msg_data.data.text = (void *)element->text;
				response = element->parent->user_callback(
					element->parent->callback_data,
					&msg_data);
			}
			if (response != NODE_CALLBACK_HANDLED)
				free((void *)element->text);
		}
		element->text = text;
	}

	if (bitmap != NULL && (element->type == NODE_ELEMENT_BITMAP ||
			       element->type == NODE_ELEMENT_TEXT_PLUS_ICON)) {
		if (element->bitmap != NULL) {
			response = NODE_CALLBACK_NOT_HANDLED;
			if (element->parent->user_callback != NULL) {
				msg_data.msg = NODE_DELETE_ELEMENT_IMG;
				msg_data.flag = element->flag;
				msg_data.node = element->parent;
				msg_data.data.bitmap = (void *)element->bitmap;
				response = element->parent->user_callback(
					element->parent->callback_data,
					&msg_data);
			}
			if (response != NODE_CALLBACK_HANDLED)
				free(element->bitmap);
		}
		else {
			/* Increase the box width to accomodate the new icon */
			element->box.width += NODE_INSTEP;
		}

		element->bitmap = bitmap;
	}

	tree_handle_node_element_changed(tree, element, text_changed);
}


/**
 * Returns the node element's text
 *
 * \return  the node element's text
 */
const char *tree_node_element_get_text(struct node_element *element)
{
	return element->text;
}


/**
 * Get the root node of a tree
 *
 * \param tree	the tree to get the root of
 * \return	the root of the tree
 */
struct node *tree_get_root(struct tree *tree)
{
	return tree->root;
}


/**
 * Returns whether the current tree is being edited at this time
 *
 * \param tree	the tree to be checked
 * \return	true if the tree is currently being edited
 */
bool tree_is_edited(struct tree *tree)
{
	return tree->editing == NULL ? false : true;
}


/**
 * Get the drag state of a tree
 *
 * \param tree	the tree to get the state of
 * \return	drag type (defined in desktop/tree.h)
 */
tree_drag_type tree_drag_status(struct tree *tree)
{
	return tree->drag;
}


/**
 * Get the default node of a tree for additions
 *
 * \param tree	the tree to get the default node of
 * \return	the default node
 */
struct node *tree_get_default_folder_node(struct tree *tree)
{
	if (tree->def_folder != NULL) {
		return tree->def_folder;
	} else {
		return tree_get_root(tree);
	}
}


/**
 * Set the default node of a tree to the selected node
 *
 * \param tree	the tree to set the default node of
 * \param node  the node to set as default (NULL for selected node)
 * \return	success
 */
bool tree_set_default_folder_node(struct tree *tree, struct node *node)
{
	struct node *sel_node;

	if (node == NULL) {
		sel_node = tree_get_selected_node(tree->root);
	} else {
		sel_node = node;
	}

	if((sel_node == NULL) ||
		(tree_node_is_folder(sel_node) == false)) {
		return false;
	}

	tree_clear_default_folder_node(tree);

	tree->def_folder = sel_node;
	sel_node->def_folder = true;
	tree_handle_node_changed(tree, sel_node, true, false);

	return true;
}


/**
 * Clear the default node of a tree
 *
 * \param tree	the tree to clear the default node of
 */
void tree_clear_default_folder_node(struct tree *tree)
{
	struct node *def_node = NULL;
	def_node = tree_get_default_folder_node(tree);

	if (def_node != NULL) {
		tree->def_folder = NULL;
		def_node->def_folder = false;
		tree_handle_node_changed(tree, def_node, true, false);
	}
}


/**
 * Returns the parent of a node
 *
 * \param node	the node to get the parent of
 * \return	the node's parent
 */
struct node *tree_node_get_parent(struct node *node)
{
	return node->parent;
}


/**
 * Returns the first child of a node
 *
 * \param node	the node to get the child of
 * \return	the nodes first child
 */
struct node *tree_node_get_child(struct node *node)
{
	return node->child;
}


/**
 * Returns the closest sibling a node
 *
 * \param node	the node to get the sibling of
 * \return	the nodes sibling
 */
struct node *tree_node_get_next(struct node *node)
{
	return node->next;
}


/**
 * Draws an element's expansion icon
 *
 * \param tree		the tree to draw the expansion for
 * \param element	the element to draw the expansion for
 * \param tree_x	X coordinate of the tree
 * \param tree_y	Y coordinate of the tree
 * \param ctx		current redraw context
 */
static void tree_draw_node_expansion_toggle(struct tree *tree,
		struct node *node, int tree_x, int tree_y,
		const struct redraw_context *ctx)
{
	const struct plotter_table *plot = ctx->plot;
	int x, y;

	assert(tree != NULL);
	assert(node != NULL);

	if ((node->child != NULL) || (node->data.next != NULL)) {
		x = tree_x + node->box.x - (NODE_INSTEP / 2) - 4;
		y = tree_y + node->box.y + (TREE_LINE_HEIGHT - 9) / 2;
		plot->rectangle(x, y, x + 9, y + 9,
				&plot_style_fill_tree_furniture);
		plot->rectangle(x , y, x + 8, y + 8,
				&plot_style_stroke_tree_furniture);
		plot->line(x + 2, y + 4, x + 7, y + 4,
				&plot_style_stroke_tree_furniture);
		if (!node->expanded)
			plot->line(x + 4, y + 2, x + 4, y + 7,
					&plot_style_stroke_tree_furniture);

	}

}


/**
 * Draws an element, including any expansion icons
 *
 * \param tree		the tree to draw an element for
 * \param element	the element to draw
 * \param tree_x	X coordinate to draw the tree at (wrt plot origin)
 * \param tree_y	Y coordinate to draw the tree at (wrt plot origin)
 * \param clip		clipping rectangle (wrt plot origin)
 * \param ctx		current redraw context
 */
static void tree_draw_node_element(struct tree *tree,
		struct node_element *element, int tree_x, int tree_y,
		const struct rect *clip, const struct redraw_context *ctx)
{
	const struct plotter_table *plot = ctx->plot;
	struct bitmap *bitmap = NULL;
	int x, y, width;
	bool selected = false;
	bool def_folder = false;
	hlcache_handle *icon;
	plot_font_style_t *fstyle;
	const int icon_inset = (TREE_LINE_HEIGHT - TREE_ICON_SIZE) / 2;

	assert(tree != NULL);
	assert(element != NULL);
	assert(element->parent != NULL);

	x = tree_x + element->box.x;
	y = tree_y + element->box.y;
	width = element->box.width;
	if (&element->parent->data == element) {
		if (element->parent->selected)
			selected = true;
		if (element->parent->def_folder)
			def_folder = true;
	}

	switch (element->type) {
	case NODE_ELEMENT_TEXT_PLUS_ICON:
		icon = element->bitmap;
		if (icon != NULL && (content_get_status(icon) ==
				CONTENT_STATUS_READY ||
				content_get_status(icon) ==
				CONTENT_STATUS_DONE) &&
				x + TREE_ICON_SIZE > clip->x0 &&
				x < clip->x1) {
			struct rect c;
			/* Clip to image area */
			c.x0 = x;
			c.y0 = y + icon_inset;
			c.x1 = x + TREE_ICON_SIZE;
			c.y1 = y + icon_inset + TREE_ICON_SIZE;
			if (c.x0 < clip->x0) c.x0 = clip->x0;
			if (c.y0 < clip->y0) c.y0 = clip->y0;
			if (c.x1 > clip->x1) c.x1 = clip->x1;
			if (c.y1 > clip->y1) c.y1 = clip->y1;

			if (c.x1 > c.x0 && c.y1 > c.y0) {
				/* Valid clip rectangles only */
				struct content_redraw_data data;

				plot->clip(&c);

				data.x = x;
				data.y = y + icon_inset;
				data.width = TREE_ICON_SIZE;
				data.height = TREE_ICON_SIZE;

				data.background_colour = 0xFFFFFF;
				data.scale = 1;
				data.repeat_x = false;
				data.repeat_y = false;

				content_redraw(icon, &data, &c, ctx);

				/* Restore previous clipping area */
				plot->clip(clip);
			}
		}

		x += NODE_INSTEP;
		width -= NODE_INSTEP;

		/* fall through */
	case NODE_ELEMENT_TEXT:
		if (element->text == NULL || clip->x1 < x)
			break;

		if (element == tree->editing)
			return;

		if (selected) {
			if (def_folder == true)
				fstyle = &plot_fstyle_selected_def_folder;
			else
				fstyle = &plot_fstyle_selected;

			plot->rectangle(x, y, x + width,
				       y + element->box.height,
				       &plot_style_fill_tree_selected);
		} else {
		if (def_folder == true)
			fstyle = &plot_fstyle_def_folder;
		else
			fstyle = &plot_fstyle;
		}

		plot->text(x + 4, y + (TREE_LINE_HEIGHT * 3 + 2) / 4,
				element->text, strlen(element->text),
				fstyle);
		break;
	case NODE_ELEMENT_BITMAP:
		bitmap = element->bitmap;
		if (bitmap == NULL)
			break;
		plot->bitmap(x, y, element->box.width - 1,
			    element->box.height - 2,
			    bitmap, 0xFFFFFF, BITMAPF_NONE);
		if (!(tree->flags & TREE_NO_FURNITURE))
			plot->rectangle(x, y, x + element->box.width - 1,
				       y + element->box.height - 3,
				       &plot_style_stroke_tree_furniture);

		break;
	}

}


/**
 * Redraws a node.
 *
 * \param tree		the tree to draw
 * \param node		the node to draw children and siblings of
 * \param tree_x	X coordinate to draw the tree at (wrt plot origin)
 * \param tree_y	Y coordinate to draw the tree at (wrt plot origin)
 * \param clip		clipping rectangle (wrt plot origin)
 * \param ctx		current redraw context
 */
static void tree_draw_node(struct tree *tree, struct node *node,
		int tree_x, int tree_y, struct rect clip,
		const struct redraw_context *ctx)
{
	const struct plotter_table *plot = ctx->plot;
	struct node_element *element;
	struct node *parent;
	int x0, y0, x1, y1;
	struct rect node_extents;

	assert(tree != NULL);
	assert(node != NULL);

	/* Find node's extents, including children's area */
	node_extents.x0 = tree_x + node->box.x - NODE_INSTEP;
	node_extents.y0 = tree_y + node->box.y;
	node_extents.x1 = tree_x + node->box.x + node->box.width + NODE_INSTEP;
	if (node->next != NULL)
		node_extents.y1 = tree_y + node->next->box.y;
	else
		node_extents.y1 = tree_y + node->box.y + node->box.height;

	/* Nothing to draw, if node is outside clip region */
	if ((node_extents.x1 < clip.x0) && (node_extents.y1 < clip.y0) &&
			(node_extents.x0 > clip.x1) &&
			(node_extents.y0 > clip.y1)) {
		return;
	}

	/* Intersect clip region with node's extents */
	if (clip.x0 < node_extents.x0) clip.x0 = node_extents.x0;
	if (clip.y0 < node_extents.y0) clip.y0 = node_extents.y0;
	if (clip.x1 > node_extents.x1) clip.x1 = node_extents.x1;
	if (clip.y1 > node_extents.y1) clip.y1 = node_extents.y1;

	if (clip.x0 >= clip.x1 || clip.y0 >= clip.y1) {
		/* Invalid clip rectangle */
		return;
	}

	/* Set up the clipping area */
	plot->clip(&clip);

	/* Draw node's furniture */
	if (!(tree->flags & TREE_NO_FURNITURE)) {
		/* Display furniture */
		if (node->previous != NULL) {
			/* There is a node above this
			 * Display furniture; line connecting up to previous */
			x0 = x1 = tree_x + node->box.x - (NODE_INSTEP / 2);
			y0 = tree_y + node->previous->box.y;
			y1 = tree_y + node->box.y + (TREE_LINE_HEIGHT / 2);
			plot->line(x0, y0, x1, y1,
					&plot_style_stroke_tree_furniture);
		}
		if (node->next != NULL) {
			/* There is a node below this
			 * Display furniture; line connecting down to next */
			x0 = x1 = tree_x + node->box.x - (NODE_INSTEP / 2);
			y0 = tree_y + node->box.y + (TREE_LINE_HEIGHT / 2);
			y1 = tree_y + node->next->box.y;
			plot->line(x0, y0, x1, y1,
					&plot_style_stroke_tree_furniture);
		}

		parent = node->parent;
		if ((parent != NULL) && (parent != tree->root) &&
				(parent->child == node)) {
			/* Node is first child */
			x0 = x1 = tree_x + parent->box.x + (NODE_INSTEP / 2);
			y0 = tree_y + parent->data.box.y +
					parent->data.box.height;
			y1 = y0 + (TREE_LINE_HEIGHT / 2);
			plot->line(x0, y0, x1, y1,
					&plot_style_stroke_tree_furniture);
		}
		/* Line from expansion toggle to icon */
		x0 = tree_x + node->box.x - (NODE_INSTEP / 2);
		x1 = x0 + (NODE_INSTEP / 2) - 2;
		y0 = y1 = tree_y + node->data.box.y + node->data.box.height -
				(TREE_LINE_HEIGHT / 2);
		plot->line(x0, y0, x1, y1, &plot_style_stroke_tree_furniture);

		tree_draw_node_expansion_toggle(tree, node,
				tree_x, tree_y, ctx);
	}

	/* Draw node's element(s)
	 * NOTE: node's children are handled later in tree_draw_tree() */
	if (node->expanded) {
		for (element = &node->data; element != NULL;
				element = element->next) {
			/* Draw each element of expanded node */
			tree_draw_node_element(tree, element, tree_x, tree_y,
					&clip, ctx);
		}
	} else {
		/* Draw main title element of node */
		tree_draw_node_element(tree, &node->data, tree_x, tree_y,
				&clip, ctx);
	}
}


/**
 * Redraws a node's descendants.
 *
 * \param tree		the tree to draw
 * \param node		the node to draw children and siblings of
 * \param tree_x	X coordinate to draw the tree at (wrt plot origin)
 * \param tree_y	Y coordinate to draw the tree at (wrt plot origin)
 * \param clip		clipping rectangle (wrt plot origin)
 * \param ctx		current redraw context
 */
static void tree_draw_tree(struct tree *tree, struct node *node,
		int tree_x, int tree_y, struct rect clip,
		const struct redraw_context *ctx)
{
	struct node *child;

	assert(tree != NULL);
	assert(node != NULL);

	for (child = node->child; child != NULL; child = child->next) {
		/* Draw children that are inside the clip region */

		if (child->next != NULL &&
				(child->next->box.y + tree_y < clip.y0))
			/* Child is above clip region */
			continue;
		if (child->box.y + tree_y > clip.y1)
			/* Child is below clip region
			 * further siblings will be too */
			return;

		/* Draw current child */
		tree_draw_node(tree, child, tree_x, tree_y, clip, ctx);
		/* And its children */
		if ((child->child != NULL) && (child->expanded)) {
			/* Child has children and they are visible */
			tree_draw_tree(tree, child, tree_x, tree_y, clip, ctx);
		}
	}
}


/**
 * Redraws a tree.
 *
 * \param tree		the tree to draw
 * \param x		X coordinate to draw the tree at (wrt plot origin)
 * \param y		Y coordinate to draw the tree at (wrt plot origin)
 * \param clip_x	minimum x of the clipping rectangle (wrt tree origin)
 * \param clip_y	minimum y of the clipping rectangle (wrt tree origin)
 * \param clip_width	width of the clipping rectangle
 * \param clip_height	height of the clipping rectangle
 * \param ctx		current redraw context
 */
void tree_draw(struct tree *tree, int x, int y,
		int clip_x, int clip_y, int clip_width, int clip_height,
		const struct redraw_context *ctx)
{
	struct redraw_context new_ctx = *ctx;
	struct rect clip;

	assert(tree != NULL);
	assert(tree->root != NULL);

	/* Start knockout rendering if it's available for this plotter */
	if (ctx->plot->option_knockout)
		knockout_plot_start(ctx, &new_ctx);

	/* Set up clip rectangle */
	clip.x0 = x + clip_x;
	clip.y0 = y + clip_y;
	clip.x1 = clip.x0 + clip_width;
	clip.y1 = clip.y0 + clip_height;
	new_ctx.plot->clip(&clip);

	/* Flat fill extents of clipping area */
	new_ctx.plot->rectangle(clip.x0, clip.y0, clip.x1, clip.y1,
		       &plot_style_fill_tree_background);

	/* don't draw empty trees or trees with redraw flag set to false */
	if (tree->root->child != NULL && tree->redraw) {

		/* Draw the tree */
		tree_draw_tree(tree, tree->root, x, y, clip, &new_ctx);

		/* Draw textarea, if present */
		if (tree->editing != NULL) {
			x = x + tree->editing->box.x;
			y = y + tree->editing->box.y;
			if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON)
				x += NODE_INSTEP;
			textarea_redraw(tree->textarea, x, y, &clip, &new_ctx);
		}
	}

	/* Rendering complete */
	if (ctx->plot->option_knockout)
		knockout_plot_end();
}


/**
 * Finds a node element from a node with a specific user_type
 *
 * \param node	the node to examine
 * \param flag	user assinged flag used is searches
 * \param after if this is not NULL the search will start after the given
 *		node_element
 * \return	the corresponding element
 */
struct node_element *tree_node_find_element(struct node *node,
		unsigned int flag, struct node_element *after)
{
	struct node_element *element;

	if (after == NULL)
		element = &node->data;
	else {
		assert(after->parent == node);
		element = after->next;
	}

	for (; element != NULL; element = element->next)
		if (element->flag == flag) return element;

	return NULL;
}


/**
 * Deletes all selected nodes from the tree.
 *
 * \param tree	the tree to delete from
 * \param node	the node to delete
 */
void tree_delete_selected_nodes(struct tree *tree, struct node *node)
{
	struct node *next;
	int y = node->box.y;
	int height = tree->height;
	int width = tree->width;
	bool redraw_setting = tree->redraw;

	tree->redraw = false;

	if (node == tree->root) {
		if (node->child != NULL)
			tree_delete_selected_nodes(tree, node->child);

		tree->redraw = redraw_setting;

		if (tree->redraw)
			tree->callbacks->redraw_request(0, y,
					width, height,
					tree->client_data);
		return;
	}

	while (node != NULL) {
		next = node->next;
		if (node->selected)
			tree_delete_node(tree, node, false);
		else if (node->child != NULL)
			tree_delete_selected_nodes(tree, node->child);
		node = next;
	}

	tree->redraw = redraw_setting;

	if (tree->redraw)
		tree->callbacks->redraw_request(0, y,
				width, height,
				tree->client_data);
}


/**
 * Returns the selected node, or NULL if multiple nodes are selected.
 *
 * \param node	the node to search sibling and children
 * \return	the selected node, or NULL if multiple nodes are selected
 */
struct node *tree_get_selected_node(struct node *node)
{
	struct node *result = NULL;
	struct node *temp;

	for (; node != NULL; node = node->next) {
		if (node->selected) {
			if (result != NULL)
				return NULL;
			result = node;
		}
		if ((node->child != NULL) && (node->expanded)) {
			temp = tree_get_selected_node(node->child);
			if (temp != NULL) {
				if (result != NULL)
					return NULL;
				else
					result = temp;
			}
		}
	}
	return result;
}


/**
 * Finds a node element at a specific location.
 *
 * \param node	     the root node to check from
 * \param x	     the x co-ordinate
 * \param y	     the y co-ordinate
 * \param expansion_toggle  whether the coordinate was in an expansion toggle
 * \return the node at the specified position, or NULL for none
 */
static struct node_element *tree_get_node_element_at(struct node *node,
		int x, int y, bool *expansion_toggle)
{
	struct node_element *element;
	int x0, x1, y0, y1;

	*expansion_toggle = false;
	for (; node != NULL; node = node->next) {
		if (node->box.y > y) return NULL;
		if ((node->box.x - NODE_INSTEP < x) && (node->box.y < y) &&
		    (node->box.x + node->box.width >= x) &&
		    (node->box.y + node->box.height >= y)) {
			if (node->expanded) {
				for (element = &node->data; element != NULL;
				     element = element->next) {
					x0 = element->box.x;
					y0 = element->box.y;
					x1 = element->box.x +
						element->box.width;
					y1 = element->box.y +
						element->box.height;
					if ((x0 < x) && (y0 < y) && (x1 >= x)
					    && (y1 >= y))
						return element;
				}
			} else {
				x0 = node->data.box.x;
				y0 = node->data.box.y;
				x1 = node->data.box.x +	node->data.box.width;
				y1 = node->data.box.y +	node->data.box.height;
				if ((x0 < x) && (y0 < y) && (x1 >= x) &&
				    (y1>= y))
					return &node->data;
			}
			if (((node->child != NULL) ||
			     (node->data.next != NULL)) &&
			    (node->data.box.x - NODE_INSTEP + 4 < x)
			    && (node->data.box.y + 4 < y) &&
			    (node->data.box.x > x) &&
			    (node->data.box.y + TREE_LINE_HEIGHT > y)) {
				/* Node either has node children, or node
				 * has more than one element.
				 * Coordinate is over node expansion toggle area
				 */
				*expansion_toggle = true;
				return &node->data;
			}
		}

		element = tree_get_node_element_at(node->child, x, y,
				expansion_toggle);
		if ((node->child != NULL) && (node->expanded) &&
		    (element != NULL))
			return element;
	}
	return NULL;
}


/**
 * Finds a node at a specific location.
 *
 * \param root	     the root node to check from
 * \param x	     the x co-ordinate
 * \param y	     the y co-ordinate
 * \param expansion_toggle  whether the coordinate was in an expansion toggle
 * \return the node at the specified position, or NULL for none
 */
static struct node *tree_get_node_at(struct node *root, int x, int y,
		bool *expansion_toggle)
{
	struct node_element *result;

	if ((result = tree_get_node_element_at(root, x, y, expansion_toggle)))
		return result->parent;
	return NULL;
}


/**
 * Gets link characteristics to insert a node at a specified position.
 *
 * \param tree	  the tree to find link information for
 * \param x	  the x co-ordinate
 * \param y	  the y co-ordinate
 * \param before  set to whether the node should be linked before on exit
 * \return	  the node to link with
 */
struct node *tree_get_link_details(struct tree *tree, int x, int y,
		bool *before)
{
	struct node *node = NULL;
	bool expansion_toggle;

	assert(tree != NULL);
	assert(tree->root != NULL);

	*before = false;
	if (tree->root->child != NULL)
		node = tree_get_node_at(tree->root->child, x, y,
				&expansion_toggle);
	if ((node == NULL) || (expansion_toggle))
		return tree->root;

	if (y < (node->box.y + (node->box.height / 2))) {
		*before = true;
	} else if ((node->folder) && (node->expanded) &&
		   (node->child != NULL)) {
		node = node->child;
		*before = true;
	}
	return node;
}


/**
 * Launches all the selected nodes of the tree
 *
 * \param tree	the tree for which all nodes will be launched
 * \param node	the node which will be checked together with its children
 * \param tabs	launch node in a new tab instead of a new window
 */
static void tree_launch_selected_internal(struct tree *tree, struct node *node,
		bool tabs)
{
	struct node_msg_data msg_data;

	msg_data.data.bw = NULL;

	for (; node != NULL; node = node->next) {
		if (node->selected && node->user_callback != NULL) {
			msg_data.msg = NODE_LAUNCH;
			if (tabs == true) {
				msg_data.flag = TREE_ELEMENT_LAUNCH_IN_TABS;
			} else {
				msg_data.flag = TREE_ELEMENT_TITLE;
			}

			msg_data.node = node;
			node->user_callback(node->callback_data, &msg_data);
		}
		if (node->child != NULL)
			tree_launch_selected_internal(tree, node->child, tabs);
	}
}


/**
 * Launches all the selected nodes of the tree
 *
 * \param tree	the tree for which all nodes will be launched
 * \param tabs	launch nodes in new tabs instead of new windows
 */
void tree_launch_selected(struct tree *tree, bool tabs)
{
	if (tree->root->child != NULL)
		tree_launch_selected_internal(tree, tree->root->child, tabs);
}


/**
 * Updates the node at position x,y to a selected state.
 * The required areas of the tree are redrawn.
 *
 * \param tree	    the tree to update nodes for, may be NULL
 * \param x	        x position in tree
 * \param y	        y position in tree
 * \param selected  the selection state to set
 */
void tree_set_node_selected_at(struct tree *tree, int x, int y, bool selected)
{
	bool expansion_toggle;
	struct node *node;

	node = tree_get_node_at(tree->root, x, y, &expansion_toggle);

	if ((node == NULL) || (expansion_toggle == true))
		return;

	tree_set_node_selected(tree, node, false, selected);
}


/**
 * Handles a mouse action for a tree
 *
 * \param tree	 the tree to handle a click for
 * \param mouse	 the mouse state
 * \param x	 X coordinate of mouse action
 * \param y	 Y coordinate of mouse action
 * \return	 whether the click was handled
 */
bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x,
		int y)
{
	bool expansion_toggle;
	struct node *node;
	struct node *last;
	struct node_element *element;
	struct node_msg_data msg_data;

	bool double_click_1 = mouse & BROWSER_MOUSE_DOUBLE_CLICK &&
			mouse & BROWSER_MOUSE_CLICK_1;
	bool double_click_2 = mouse & BROWSER_MOUSE_DOUBLE_CLICK &&
			mouse & BROWSER_MOUSE_CLICK_2;

	assert(tree != NULL);
	assert(tree->root != NULL);

	if (tree->root->child == NULL)
		return true;

	element = tree_get_node_element_at(tree->root->child, x, y,
			&expansion_toggle);

	/* pass in-textarea mouse action and drags which started in it
	   to the textarea */
	if (tree->editing != NULL) {
		int x0, x1, y0, y1;
		x0 = tree->editing->box.x;
		if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON)
			x0 += NODE_INSTEP;
		x1 = tree->editing->box.x + tree->editing->box.width;
		y0 = tree->editing->box.y;
		y1 = tree->editing->box.y + tree->editing->box.height;

		if (tree->textarea_drag_start &&
				(mouse & (BROWSER_MOUSE_HOLDING_1 |
				BROWSER_MOUSE_HOLDING_2))) {
			/* Track the drag path */
			textarea_mouse_action(tree->textarea, mouse,
					      x - x0, y - y0);
			return true;
		}



		if ((x >= x0) && (x < x1) && (y >= y0) && (y < y1)) {
			/* Inside the textarea */
			if (mouse & (BROWSER_MOUSE_DRAG_1 |
				     BROWSER_MOUSE_DRAG_2)) {
				/* Drag starting */
				tree->textarea_drag_start = true;
				tree->drag = TREE_TEXTAREA_DRAG;
			} else {
				/* Other action */
				tree->textarea_drag_start = false;
			}
			textarea_mouse_action(tree->textarea, mouse,
					      x - x0, y - y0);
			return true;

		}
	}

	tree->textarea_drag_start = false;

	/* we are not interested in the drag path, return */
	if (mouse & (BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_HOLDING_2))
		return true;

	/* cancel edit */
	if (tree->editing != NULL)
		tree_stop_edit(tree, false);

	/* no item either means cancel selection on (select) click or a drag */
	if (element == NULL) {
		if (tree->flags & TREE_SINGLE_SELECT) {
			tree_set_node_selected(tree, tree->root->child, true,
					       false);
			return true;
		}
		if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_DRAG_1))
			tree_set_node_selected(tree, tree->root->child, true,
					       false);
		if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) {

			/** @todo the tree window has to scroll the tree when
			 * mouse reaches border while dragging this isn't
			 * solved for the browser window too.
			 */
			tree->drag = TREE_SELECT_DRAG;
		}
		return true;
	}

	node = element->parent;

	/* A click on expansion toggle or double click on folder toggles node
	 * expansion */
	if (((expansion_toggle) && (mouse & (BROWSER_MOUSE_CLICK_1 |
			BROWSER_MOUSE_CLICK_2))) ||
			(((!expansion_toggle) && (node->child != NULL)) &&
			(double_click_1 || double_click_2))) {

		/* clear any selection */
		tree_set_node_selected(tree, tree->root->child, true, false);

		/* expand / contract node and redraw */
		tree_set_node_expanded(tree, node, !node->expanded,
				       false, false);

		/* find the last child node if expanded */
		last = node;
		if ((last->child != NULL) && (last->expanded)) {
			last = last->child;
			while ((last->next != NULL) ||
			       ((last->child != NULL) &&
				(last->expanded))) {
				if (last->next != NULL)
					last = last->next;
				else
					last = last->child;
			}
		}
		/* scroll to the bottom element then back to the top */
		element = &last->data;
		if (last->expanded)
			for (; element->next != NULL; element = element->next);
		tree->callbacks->scroll_visible(element->box.y,
						element->box.height,
						tree->client_data);
		tree->callbacks->scroll_visible(node->data.box.y,
						node->data.box.height,
						tree->client_data);
		return true;
	}

	/* no use for any other expansion toggle click */
	if (expansion_toggle)
		return true;

	/* single/double ctrl+click or alt+click starts editing */
	if ((element->editable) && (!tree->editing) &&
	    ((element->type == NODE_ELEMENT_TEXT) ||
	     (element->type == NODE_ELEMENT_TEXT_PLUS_ICON)) &&
	    (mouse & BROWSER_MOUSE_CLICK_1 || double_click_1) &&
	    (mouse & BROWSER_MOUSE_MOD_2 ||
	     mouse & BROWSER_MOUSE_MOD_3)) {
		tree_set_node_selected(tree, tree->root->child, true, false);
		tree_start_edit(tree, element);
		return true;
	}

	/* double click launches the leaf */
	if (double_click_1 || double_click_2) {
		if (node->user_callback == NULL)
			return false;
		msg_data.msg = NODE_LAUNCH;
		msg_data.flag = TREE_ELEMENT_TITLE;
		msg_data.node = node;
		if (node->user_callback(node->callback_data, &msg_data) !=
		    NODE_CALLBACK_HANDLED)
			return false;

		return true;
	}

	/* single click (select) cancels current selection and selects item */
	if (mouse & BROWSER_MOUSE_CLICK_1 || (mouse & BROWSER_MOUSE_CLICK_2 &&
					      tree->flags & TREE_SINGLE_SELECT)) {
		if (tree->flags & TREE_NO_SELECT)
			return true;
		if (!node->selected) {
			tree_set_node_selected(tree, tree->root->child, true,
					       false);
			node->selected = true;
			tree_handle_node_element_changed(tree, &node->data, false);
		}
		return true;
	}

	/* single click (adjust) toggles item selection */
	if (mouse & BROWSER_MOUSE_CLICK_2) {
		if (tree->flags & TREE_NO_SELECT)
			return true;
		node->selected = !node->selected;
		tree_handle_node_element_changed(tree, &node->data, false);
		return true;
	}

	/* drag starts a drag operation */
	if ((!tree->editing) && (mouse & (BROWSER_MOUSE_DRAG_1 |
					  BROWSER_MOUSE_DRAG_2))) {
		if (tree->flags & TREE_NO_DRAGS)
			return true;

		if (!node->selected) {
			tree_set_node_selected(tree, tree->root->child, true,
					       false);
			node->selected = true;
			tree_handle_node_element_changed(tree, &node->data, false);
		}

		if (tree->flags & TREE_MOVABLE)
			tree->drag = TREE_MOVE_DRAG;
		else tree->drag = TREE_UNKNOWN_DRAG;

		return true;
	}


	return false;
}


/**
 * Updates the selected state for a region of nodes.
 *
 * \param tree	  the tree to update
 * \param node	  the node to update children and siblings of
 * \param y	  the minimum y of the selection rectangle
 * \param height  the height of the selection rectangle
 * \param invert  whether to invert the selected state
 */
static void tree_handle_selection_area_node(struct tree *tree,
		struct node *node, int y, int height, bool invert)
{
	struct node_element *element;
	struct node *update;
	int y_max;
	int y0, y1;

	assert(tree != NULL);
	assert(node != NULL);

	y_max = y + height;

	for (; node != NULL; node = node->next) {
		if (node->box.y > y_max) return;
		y0 = node->box.y;
		y1 = node->box.y + node->box.height;
		if ((y0 < y_max) && (y1 >= y)) {
			update = NULL;
			if (node->expanded) {
				for (element = &node->data; element != NULL;
				     element = element->next) {
					y0 = element->box.y;
					y1 = element->box.y +
						element->box.height;
					if ((y0 < y_max) && (y1 >= y)) {
						update = element->parent;
						break;
					}
				}
			} else {
				y0 = node->data.box.y;
				y1 = node->data.box.y +	node->data.box.height;
				if ((y0 < y_max) && (y1 >= y))
					update = node->data.parent;
			}
			if ((update) && (node != tree->root)) {
				if (invert) {
					node->selected = !node->selected;
					tree_handle_node_element_changed(tree,
							&node->data, false);
				} else if (!node->selected) {
					node->selected = true;
					tree_handle_node_element_changed(tree,
							&node->data, false);
				}
			}
		}
		if ((node->child != NULL) && (node->expanded))
			tree_handle_selection_area_node(tree, node->child, y,
							height, invert);
	}
}


/**
 * Updates the selected state for a region of nodes.
 *
 * \param tree	  the tree to update
 * \param y	  the minimum y of the selection rectangle
 * \param height  the height of the selection rectangle
 * \param invert  whether to invert the selected state
 */
static void tree_handle_selection_area(struct tree *tree, int y, int height,
		bool invert)
{
	assert(tree != NULL);
	assert(tree->root != NULL);

	if (tree->root->child == NULL)
		return;

	if (height < 0) {
		y += height;
		height = -height;
	}
	tree_handle_selection_area_node(tree, tree->root->child, y, height,
			invert);
}


/**
 * Clears the processing flag.
 *
 * \param node	  the node to process siblings and children of
 */
static void tree_clear_processing(struct node *node)
{
	for (; node != NULL; node = node->next) {
		node->processing = false;
		if (node->child != NULL)
			tree_clear_processing(node->child);
	}
}


/**
 * Sets the processing flag to the selection state.
 *
 * \param node	  the node to process siblings and children of
 */
static void tree_selected_to_processing(struct node *node)
{
	for (; node != NULL; node = node->next) {
		node->processing = node->selected;
		if ((node->child != NULL) && (node->expanded))
			tree_selected_to_processing(node->child);
	}
}


/**
 * Moves the first node in a tree with the processing flag set.
 *
 * \param tree	  the tree in which the move takes place
 * \param node	  the node to move siblings/children of
 * \param link	  the node to link before/as a child (folders) or before/after
 *		  (link)
 * \param before  whether to link siblings before or after the supplied node
 * \param first	  whether to always link after the supplied node (ie not
 *		  inside of folders)
 * \return	  the node moved
 */
static struct node *tree_move_processing_node(struct tree *tree,
		struct node *node, struct node *link, bool before, bool first)
{
	struct node *result;

	bool folder = link->folder;
	for (; node != NULL; node = node->next) {
		if (node->processing) {
			node->processing = false;
			tree_delink_node(tree, node);
			if (!first)
				link->folder = false;
			tree_link_node(tree, link, node, before);
			if (!first)
				link->folder = folder;
			return node;
		}
		if (node->child != NULL) {
			result = tree_move_processing_node(tree, node->child,
							   link, before, first);
			if (result != NULL)
				return result;
		}
	}
	return NULL;
}


/**
 * Moves nodes within a tree.
 *
 * \param tree	       the tree to process
 * \param destination  the node to link before/as a child (folders)
 *		       or before/after (link)
 * \param before       whether to link siblings before or after the supplied
 *		       node
 */
static void tree_move_selected_nodes(struct tree *tree,
		struct node *destination, bool before)
{
	struct node *link;
	struct node *test;
	bool error;

	tree_clear_processing(tree->root);
	tree_selected_to_processing(tree->root);

	/* the destination node cannot be a child of any node with
	   the processing flag set */
	error = destination->processing;
	for (test = destination; test != NULL; test = test->parent)
		error |= test->processing;
	if (error) {
		tree_clear_processing(tree->root);
		return;
	}
	if ((destination->folder) && (!destination->expanded) && (!before)) {
		tree_set_node_expanded(tree, destination, true, false, false);
	}
	link = tree_move_processing_node(tree, tree->root, destination, before,
					 true);
	while (link != NULL)
		link = tree_move_processing_node(tree, tree->root, link, false,
						 false);

	tree_clear_processing(tree->root);
	tree_recalculate_node_positions(tree, tree->root);
	if (tree->redraw)
		tree->callbacks->redraw_request(0, 0, tree->width, tree->height,
						tree->client_data);
}


/**
 * Handle the end of a drag operation
 *
 * \param tree	the tree on which the drag was performed
 * \param mouse	mouse state during drag end
 * \param x0	x coordinate of drag start
 * \param y0	y coordinate of drag start
 * \param x1	x coordinate of drag end
 * \param y1	y coordinate of drag end
 */
void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0,
		int x1, int y1)
{

	bool before;
	struct node *node;
	int x, y;

	if (tree->textarea_drag_start) {
		x = tree->editing->box.x;
		y = tree->editing->box.y;
		if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON)
			x += NODE_INSTEP;
		textarea_drag_end(tree->textarea, mouse, x1 - x, y1 - y);
	}

	tree->textarea_drag_start = false;

	switch (tree->drag) {
	case TREE_NO_DRAG:
	case TREE_TEXTAREA_DRAG:
	case TREE_UNKNOWN_DRAG:
		break;
	case TREE_SELECT_DRAG:
		tree_handle_selection_area(tree, y0, y1 - y0,
					   (mouse | BROWSER_MOUSE_HOLDING_2));
		break;
	case TREE_MOVE_DRAG:
		if (!(tree->flags & TREE_MOVABLE))
			return;
		node = tree_get_link_details(tree, x1, y1, &before);
		tree_move_selected_nodes(tree, node, before);
		break;
	}

	tree->drag = TREE_NO_DRAG;
}


/**
 * Key press handling for a tree.
 *
 * \param tree	The tree which got the keypress
 * \param key	The ucs4 character codepoint
 * \return	true if the keypress is dealt with, false otherwise.
 */
bool tree_keypress(struct tree *tree, uint32_t key)
{

	if (tree->editing != NULL)
		switch (key) {
		case KEY_ESCAPE:
			tree_stop_edit(tree, false);
			return true;
		case KEY_NL:
		case KEY_CR:
			tree_stop_edit(tree, true);
			return true;
		default:
			return textarea_keypress(tree->textarea, key);
		}

	return false;
}


/**
 * Alphabetical comparison function for nodes
 *
 * \param n1  first node to compare
 * \param n2  first node to compare
 * \return    0 if equal, greater then zero if n1 > n2,
 *	      less then zero if n2 < n1
 */
int tree_alphabetical_sort(struct node *n1, struct node *n2)
{
	return strcmp(n1->data.text, n2->data.text);
}


/**
 * Redraw requests from the textarea are piped through this because we have to
 * check the redraw flag of the tree before requesting a redraw and change the
 * position to tree origin relative.
 */
static void tree_textarea_redraw_request(void *data, int x, int y,
		int width, int height)
{
	struct tree *tree = data;
	x = x + tree->editing->box.x;
	y = y + tree->editing->box.y;
	if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON)
		x += NODE_INSTEP;

	if (tree->redraw)
		tree->callbacks->redraw_request(x, y,
						width, height,
						tree->client_data);
}


/**
 * Starts editing a node_element
 *
 * \param tree		The tree to which element belongs
 * \param element	The element to start being edited
 */
void tree_start_edit(struct tree *tree, struct node_element *element)
{
	struct node *parent;
	int width, height;

	assert(tree != NULL);
	assert(element != NULL);

	if (tree->editing != NULL)
		tree_stop_edit(tree, true);

	parent = element->parent;
	if (&parent->data == element)
		parent = parent->parent;
	for (; parent != NULL; parent = parent->parent) {
		if (!parent->expanded) {
			tree_set_node_expanded(tree, parent, true,
					       false, false);
		}
	}

	tree->editing = element;
	tree->callbacks->get_window_dimensions(&width, NULL, tree->client_data);
	width -= element->box.x;
	height = element->box.height;
	if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON)
		width -= NODE_INSTEP;

	tree->textarea = textarea_create(width, height, 0,
			&plot_fstyle, tree_textarea_redraw_request, tree);
	if (tree->textarea == NULL) {
		tree_stop_edit(tree, false);
		return;
	}
	textarea_set_text(tree->textarea, element->text);

	tree_handle_node_element_changed(tree, element, true);
	tree_recalculate_size(tree);
	tree->callbacks->scroll_visible(element->box.y, element->box.height,
					tree->client_data);
}


/**
 * Callback for fetchcache(). Should be removed once bitmaps get loaded directly
 * from disc
 */
static nserror tree_icon_callback(hlcache_handle *handle,
		const hlcache_event *event, void *pw)
{
	return NSERROR_OK;
}


/**
 * Tree utility function. Placed here so that this code doesn't have to be
 * copied by each user.
 *
 * \param name	the name of the loaded icon, if it's not a full path the icon is
 *		looked for in the directory specified by tree_icons_dir
 * \return the icon in form of a content or NULL on failure
 */
hlcache_handle *tree_load_icon(const char *name)
{
	char *url = NULL;
	const char *icon_url = NULL;
	int len;
	hlcache_handle *c;
	nserror err;
	nsurl *icon_nsurl;

	/** @todo something like bitmap_from_disc is needed here */

	if (!strncmp(name, "file://", 7)) {
		icon_url = name;
	} else {
		char *native_path;

		if (tree_icons_dir == NULL)
			return NULL;

		/* path + separator + leafname + '\0' */
		len = strlen(tree_icons_dir) + 1 + strlen(name) + 1;
		native_path = malloc(len);
		if (native_path == NULL) {
			LOG(("malloc failed"));
			warn_user("NoMemory", 0);
			return NULL;
		}

		/* Build native path */
		memcpy(native_path, tree_icons_dir,
		       strlen(tree_icons_dir) + 1);
		path_add_part(native_path, len, name);

		/* Convert native path to URL */
		url = path_to_url(native_path);

		free(native_path);
		icon_url = url;
	}

	err = nsurl_create(icon_url, &icon_nsurl);
	if (err != NSERROR_OK) {
		if (url != NULL)
			free(url);
		return NULL;
	}

	/* Fetch the icon */
	err = hlcache_handle_retrieve(icon_nsurl, 0, 0, 0,
				      tree_icon_callback, 0, 0, 
				      CONTENT_IMAGE, &c);

	nsurl_unref(icon_nsurl);

	/* If we built the URL here, free it */
	if (url != NULL)
		free(url);

	if (err != NSERROR_OK) {
		return NULL;
	}

	return c;
}
