1 /*
2  * Copyright 2012 - 2013 Michael Drake <tlsa@netsurf-browser.org>
3  *
4  * This file is part of NetSurf, http://www.netsurf-browser.org/
5  *
6  * NetSurf is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * NetSurf is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * \file
21  *
22  * Treeview handling implementation.
23  */
24 
25 #include "utils/config.h"
26 
27 #include <string.h>
28 
29 #include "utils/utils.h"
30 #include "utils/log.h"
31 #include "utils/nsurl.h"
32 #include "utils/nscolour.h"
33 #include "utils/nsoption.h"
34 #include "netsurf/bitmap.h"
35 #include "netsurf/content.h"
36 #include "netsurf/plotters.h"
37 #include "netsurf/clipboard.h"
38 #include "netsurf/layout.h"
39 #include "netsurf/keypress.h"
40 #include "netsurf/core_window.h"
41 #include "content/hlcache.h"
42 #include "css/utils.h"
43 
44 #include "desktop/knockout.h"
45 #include "desktop/textarea.h"
46 #include "desktop/treeview.h"
47 #include "desktop/cw_helper.h"
48 #include "desktop/gui_internal.h"
49 #include "desktop/system_colour.h"
50 
51 /**
52  * The maximum horizontal size a treeview can possibly be.
53  *
54  * \todo get rid of REDRAW_MAX -- need to be able to know window size
55  */
56 #define REDRAW_MAX 8000
57 
58 
59 /**
60  * Treeview handling global context
61  */
62 struct treeview_globals {
63 	unsigned initialised;
64 	int line_height;
65 	int furniture_width;
66 	int step_width;
67 	int window_padding;
68 	int icon_size;
69 	int icon_step;
70 	int move_offset;
71 } tree_g;
72 
73 
74 /**
75  * Section type of a treeview at a point
76  */
77 enum treeview_node_part {
78 	TV_NODE_PART_TOGGLE,	/**< Expansion toggle */
79 	TV_NODE_PART_ON_NODE,	/**< Node content (text, icon) */
80 	TV_NODE_PART_NONE	/**< Empty area */
81 };
82 
83 
84 /**
85  * Text within a treeview field or node
86  */
87 struct treeview_text {
88 	const char *data;	/**< Text string */
89 	uint32_t len;		/**< Length of string in bytes */
90 	int width;		/**< Width of text in px */
91 };
92 
93 
94 /**
95  * a treeview field
96  */
97 struct treeview_field {
98 	/** flags controlling how field is interpreted */
99 	enum treeview_field_flags flags;
100 
101 	lwc_string *field; /**< field contents */
102 	struct treeview_text value; /**< field text */
103 };
104 
105 
106 /**
107  * flags indicating render state of node.
108  */
109 enum treeview_node_flags {
110 	TV_NFLAGS_NONE     = 0,		/**< No node flags set */
111 	TV_NFLAGS_EXPANDED = (1 << 0),	/**< Whether node is expanded */
112 	TV_NFLAGS_SELECTED = (1 << 1),	/**< Whether node is selected */
113 	TV_NFLAGS_SPECIAL  = (1 << 2),	/**< Render as special node */
114 	TV_NFLAGS_MATCHED  = (1 << 3),	/**< Whether node matches search */
115 };
116 
117 
118 /**
119  * Treeview target position
120  */
121 enum treeview_target_pos {
122 	TV_TARGET_ABOVE,
123 	TV_TARGET_INSIDE,
124 	TV_TARGET_BELOW,
125 	TV_TARGET_NONE
126 };
127 
128 
129 /**
130  * Treeview node
131  */
132 struct treeview_node {
133 	enum treeview_node_flags flags;	/**< Node flags */
134 	enum treeview_node_type type;	/**< Node type */
135 
136 	int height;	/**< Includes height of any descendants (pixels) */
137 	int inset;	/**< Node's inset depending on tree depth (pixels) */
138 
139 	treeview_node *parent; /**< parent node */
140 	treeview_node *prev_sib; /**< previous sibling node */
141 	treeview_node *next_sib; /**< next sibling node */
142 	treeview_node *children; /**< first child node */
143 
144 	void *client_data;  /**< Passed to client on node event msg callback */
145 
146 	struct treeview_text text; /** Text to show for node (default field) */
147 };
148 
149 
150 /**
151  * Node entry
152  *
153  * node entry contains a base node at the beginning allowing for
154  * trivial containerof by cast and some number of fields.
155  */
156 struct treeview_node_entry {
157 	treeview_node base; /**< Entry class inherits node base class */
158 	struct treeview_field fields[FLEX_ARRAY_LEN_DECL];
159 };
160 
161 
162 /**
163  * A mouse position wrt treeview
164  */
165 struct treeview_pos {
166 	int x;		/**< Mouse X coordinate */
167 	int y;		/**< Mouse Y coordinate */
168 	int node_y;	/**< Top of node at y */
169 	int node_h;	/**< Height of node at y */
170 };
171 
172 
173 /**
174  * Treeview drag state
175  */
176 struct treeview_drag {
177 	enum {
178 		TV_DRAG_NONE,
179 		TV_DRAG_SELECTION,
180 		TV_DRAG_MOVE,
181 		TV_DRAG_TEXTAREA,
182 		TV_DRAG_SEARCH,
183 	} type;	/**< Drag type */
184 	treeview_node *start_node;	/**< Start node */
185 	bool selected;			/**< Start node is selected */
186 	enum treeview_node_part part;	/**< Node part at start */
187 	struct treeview_pos start;	/**< Start pos */
188 	struct treeview_pos prev;	/**< Previous pos */
189 };
190 
191 
192 /**
193  * Treeview node move details
194  */
195 struct treeview_move {
196 	treeview_node *root;		/**< Head of yanked node list */
197 	treeview_node *target;		/**< Move target */
198 	struct rect target_area;	/**< Pos/size of target indicator */
199 	enum treeview_target_pos target_pos;	/**< Pos wrt render node */
200 };
201 
202 
203 /**
204  * Treeview node edit details
205  */
206 struct treeview_edit {
207 	treeview_node *node;		/**< Node being edited, or NULL */
208 	struct textarea *textarea;	/**< Textarea for edit, or NULL */
209 	lwc_string *field;		/**< The field being edited, or NULL */
210 	int x;		/**< Textarea x position */
211 	int y;		/**< Textarea y position */
212 	int w;		/**< Textarea width */
213 	int h;		/**< Textarea height */
214 };
215 
216 
217 /**
218  * Treeview search box details
219  */
220 struct treeview_search {
221 	struct textarea *textarea;  /**< Search box. */
222 	bool active;                /**< Whether the search box has focus. */
223 	bool search;                /**< Whether we have a search term. */
224 	int height;                 /**< Current search display height. */
225 };
226 
227 
228 /**
229  * The treeview context
230  */
231 struct treeview {
232 	uint32_t view_width; /**< Viewport horizontal size */
233 
234 	treeview_flags flags; /**< Treeview behaviour settings */
235 
236 	treeview_node *root; /**< Root node */
237 
238 	struct treeview_field *fields;	/**< Array of fields */
239 	int n_fields; /**< fields[n_fields] is folder, lower are entry fields */
240 	int field_width; /**< Max width of shown field names */
241 
242 	struct treeview_drag drag; /**< Drag state */
243 	struct treeview_move move; /**< Move drag details */
244 	struct treeview_edit edit; /**< Edit details */
245 
246 	struct treeview_search search; /**< Treeview search box */
247 
248 	const struct treeview_callback_table *callbacks; /**< For node events */
249 
250 	const struct core_window_callback_table *cw_t; /**< Window cb table */
251 	struct core_window *cw_h; /**< Core window handle */
252 };
253 
254 
255 /**
256  * Treeview furniture states.
257  */
258 enum treeview_furniture_id {
259 	TREE_FURN_EXPAND = 0,
260 	TREE_FURN_CONTRACT,
261 	TREE_FURN_LAST
262 };
263 
264 
265 /**
266  * style for a node
267  */
268 struct treeview_node_style {
269 	plot_style_t bg;	  /**< Background */
270 	plot_font_style_t text;	  /**< Text */
271 	plot_font_style_t itext;  /**< Entry field text */
272 
273 	plot_style_t sbg;	  /**< Selected background */
274 	plot_font_style_t stext;  /**< Selected text */
275 	plot_font_style_t sitext; /**< Selected entry field text */
276 
277 	struct {
278 		int size;
279 		struct bitmap *bmp;
280 		struct bitmap *sel;
281 	} furn[TREE_FURN_LAST];
282 };
283 
284 
285 /**
286  * Plot style for odd rows
287  */
288 struct treeview_node_style plot_style_odd;
289 
290 
291 /**
292  * Plot style for even rows
293  */
294 struct treeview_node_style plot_style_even;
295 
296 
297 /**
298  * Treeview content resource data
299  */
300 struct treeview_resource {
301 	const char *url;
302 	struct hlcache_handle *c;
303 	int height;
304 	bool ready;
305 };
306 
307 
308 /**
309  * treeview resource indexes
310  */
311 enum treeview_resource_id {
312 	TREE_RES_ARROW = 0,
313 	TREE_RES_CONTENT,
314 	TREE_RES_FOLDER,
315 	TREE_RES_FOLDER_SPECIAL,
316 	TREE_RES_SEARCH,
317 	TREE_RES_LAST
318 };
319 
320 
321 /**
322  * Treeview content resources
323  */
324 static struct treeview_resource treeview_res[TREE_RES_LAST] = {
325 	{ "resource:icons/arrow-l.png", NULL, 0, false },
326 	{ "resource:icons/content.png", NULL, 0, false },
327 	{ "resource:icons/directory.png", NULL, 0, false },
328 	{ "resource:icons/directory2.png", NULL, 0, false },
329 	{ "resource:icons/search.png", NULL, 0, false }
330 };
331 
332 
333 /**
334  * Get the display height of the treeview data component of the display.
335  *
336  * \param[in] tree  Treeview to get the height of.
337  * \return the display height in pixels.
338  */
treeview__get_display_height(const treeview * tree)339 static inline int treeview__get_display_height(const treeview *tree)
340 {
341 	return (tree->search.search == false) ?
342 			tree->root->height :
343 			tree->search.height;
344 }
345 
346 
347 /**
348  * Corewindow callback wrapper: Request a redraw of the window
349  *
350  * \param[in] tree The treeview to request redraw on.
351  * \param[in] r rectangle to redraw
352  */
treeview__cw_invalidate_area(const struct treeview * tree,const struct rect * r)353 static inline void treeview__cw_invalidate_area(
354 		const struct treeview *tree,
355 		const struct rect *r)
356 {
357 	if (tree->cw_t != NULL) {
358 		tree->cw_t->invalidate(tree->cw_h, r);
359 	}
360 }
361 
362 
363 /**
364  * Corewindow callback wrapper: Request a full redraw of the window
365  *
366  * \param[in] tree The treeview to request redraw on.
367  */
treeview__cw_full_redraw(const struct treeview * tree)368 static inline void treeview__cw_full_redraw(
369 		const struct treeview *tree)
370 {
371 	if (tree->cw_t != NULL) {
372 		static const struct rect r = {
373 			.x0 = 0,
374 			.y0 = 0,
375 			.x1 = REDRAW_MAX,
376 			.y1 = REDRAW_MAX,
377 		};
378 		tree->cw_t->invalidate(tree->cw_h, &r);
379 	}
380 }
381 
382 
383 /**
384  * Get height used by treeview's search bar (or 0 if not present).
385  *
386  * \param tree    Treeview object to check.
387  * \return height used by search bar in pixels.
388  */
treeview__get_search_height(const treeview * tree)389 static inline unsigned treeview__get_search_height(
390 		const treeview *tree)
391 {
392 	return (tree->flags & TREEVIEW_SEARCHABLE) ?
393 			tree_g.line_height : 0;
394 }
395 
396 
397 /**
398  * Corewindow callback wrapper: Update the limits of the window
399  *
400  * \param[in] tree The treeview to update size for.
401  * \param[in] width the width in px, or negative if don't care
402  * \param[in] height the height in px, or negative if don't care
403  */
treeview__cw_update_size(const struct treeview * tree,int width,int height)404 static inline void treeview__cw_update_size(
405 	const struct treeview *tree,
406 	int width, int height)
407 {
408 	if (tree->cw_t != NULL) {
409 		tree->cw_t->update_size(tree->cw_h, width,
410 				height + treeview__get_search_height(tree));
411 	}
412 }
413 
414 
415 /**
416  * Corewindow callback_wrapper: Scroll to top of window.
417  *
418  * \param[in] tree  The treeview to scroll.
419  */
treeview__cw_scroll_top(const struct treeview * tree)420 static inline void treeview__cw_scroll_top(
421 		const struct treeview *tree)
422 {
423 	struct rect r = {
424 		.x0 = 0,
425 		.y0 = 0,
426 		.x1 = tree_g.window_padding,
427 		.y1 = tree_g.line_height,
428 	};
429 
430 	cw_helper_scroll_visible(tree->cw_t, tree->cw_h, &r);
431 }
432 
433 
434 /**
435  * Corewindow callback wrapper: Get window viewport dimensions
436  *
437  * \param[in] tree The treeview to get dimensions for.
438  * \param[out] width to be set to viewport width in px
439  * \param[out] height to be set to viewport height in px
440  */
treeview__cw_get_window_dimensions(const struct treeview * tree,int * width,int * height)441 static inline void treeview__cw_get_window_dimensions(
442 	const struct treeview *tree,
443 	int *width, int *height)
444 {
445 	if (tree->cw_t != NULL) {
446 		tree->cw_t->get_window_dimensions(tree->cw_h, width, height);
447 	}
448 }
449 
450 
451 /**
452  * Corewindow callback wrapper: Inform corewindow owner of drag status
453  *
454  * \param[in] tree The treeview to report status on.
455  * \param[in] ds the current drag status
456  */
treeview__cw_drag_status(const struct treeview * tree,core_window_drag_status ds)457 static inline void treeview__cw_drag_status(
458 	const struct treeview *tree,
459 	core_window_drag_status ds)
460 {
461 	if (tree->cw_t != NULL) {
462 		tree->cw_t->drag_status(tree->cw_h, ds);
463 	}
464 }
465 
466 
467 /**
468  * Helper function to access the given field of a node
469  *
470  * \param tree Treeview that node belongs to
471  * \param n Node to get field from
472  * \param i Index of field of interest
473  * \return text entry for field or NULL.
474  */
475 static inline struct treeview_text *
treeview_get_text_for_field(treeview * tree,treeview_node * n,int i)476 treeview_get_text_for_field(treeview *tree, treeview_node *n, int i)
477 {
478 	if (i == 0) {
479 		return &n->text;
480 
481 	} else if (i < tree->n_fields && n->type == TREE_NODE_ENTRY) {
482 		struct treeview_node_entry *e = (struct treeview_node_entry *)n;
483 		return &e->fields[i - 1].value;
484 	}
485 
486 	assert(0 && "Bad field index for node");
487 	return NULL;
488 }
489 
490 
491 /**
492  * Find the next node in depth first tree order
493  *
494  * \param node Start node
495  * \param full Iff true, visit children of collapsed nodes
496  * \return next node, or NULL if \a node is last node
497  */
treeview_node_next(treeview_node * node,bool full)498 static inline treeview_node * treeview_node_next(treeview_node *node, bool full)
499 {
500 	assert(node != NULL);
501 
502 	if ((full || (node->flags & TV_NFLAGS_EXPANDED)) &&
503 	    node->children != NULL) {
504 		/* Next node is child */
505 		node = node->children;
506 	} else {
507 		/* No children.  As long as we're not at the root,
508 		 * go to next sibling if present, or nearest ancestor
509 		 * with a next sibling. */
510 
511 		while (node->parent != NULL && node->next_sib == NULL) {
512 			node = node->parent;
513 		}
514 
515 		if (node->type == TREE_NODE_ROOT) {
516 			node = NULL;
517 
518 		} else {
519 			node = node->next_sib;
520 		}
521 	}
522 
523 	return node;
524 }
525 
526 
527 /**
528  * Find node at given y-position
529  *
530  * \param tree Treeview object to delete node from
531  * \param target_y Target y-position
532  * \return node at y_target
533  */
treeview_y_node(treeview * tree,int target_y)534 static treeview_node * treeview_y_node(treeview *tree, int target_y)
535 {
536 	int y = treeview__get_search_height(tree);
537 	treeview_node *n;
538 
539 	assert(tree != NULL);
540 	assert(tree->root != NULL);
541 
542 	n = treeview_node_next(tree->root, false);
543 
544 	while (n != NULL) {
545 		int h = (n->type == TREE_NODE_ENTRY) ?
546 			n->height : tree_g.line_height;
547 		if (target_y >= y && target_y < y + h)
548 			return n;
549 		y += h;
550 
551 		n = treeview_node_next(n, false);
552 	}
553 
554 	return NULL;
555 }
556 
557 
558 /**
559  * Find y position of the top of a node
560  *
561  * \param tree Treeview object to delete node from
562  * \param node Node to get position of
563  * \return node's y position
564  */
treeview_node_y(const treeview * tree,const treeview_node * node)565 static int treeview_node_y(
566 		const treeview *tree,
567 		const treeview_node *node)
568 {
569 	treeview_node *n;
570 	int y = treeview__get_search_height(tree);
571 
572 	assert(tree != NULL);
573 	assert(tree->root != NULL);
574 
575 	n = treeview_node_next(tree->root, false);
576 
577 	while (n != NULL && n != node) {
578 		y += (n->type == TREE_NODE_ENTRY) ?
579 			n->height : tree_g.line_height;
580 
581 		n = treeview_node_next(n, false);
582 	}
583 
584 	return y;
585 }
586 
587 
588 /**
589  * Corewindow callback_wrapper: Scroll to make node visible
590  *
591  * \param[in] tree  The treeview to scroll.
592  * \param[in] node  The treeview node to scroll to visibility.
593  */
treeview__cw_scroll_to_node(const struct treeview * tree,const struct treeview_node * node)594 static inline void treeview__cw_scroll_to_node(
595 		const struct treeview *tree,
596 		const struct treeview_node *node)
597 {
598 	struct rect r = {
599 		.x0 = 0,
600 		.y0 = treeview_node_y(tree, node),
601 		.x1 = 1,
602 		.y1 = ((node->type == TREE_NODE_ENTRY) ?
603 		       node->height : tree_g.line_height),
604 	};
605 
606 	r.y1 += r.y0; /* Apply the Y offset to the second Y coordinate */
607 
608 	cw_helper_scroll_visible(tree->cw_t, tree->cw_h, &r);
609 }
610 
611 
612 /**
613  * Redraw tree from given node to the bottom.
614  *
615  * \param[in] tree  Tree to redraw from node in.
616  * \param[in] node  Node to redraw from.
617  */
treeview__redraw_from_node(const treeview * tree,const treeview_node * node)618 static void treeview__redraw_from_node(
619 		const treeview *tree,
620 		const treeview_node *node)
621 {
622 	struct rect r = {
623 		.x0 = 0,
624 		.y0 = treeview_node_y(tree, node),
625 		.x1 = REDRAW_MAX,
626 		.y1 = treeview__get_display_height(tree) +
627 				treeview__get_search_height(tree),
628 	};
629 
630 	assert(tree != NULL);
631 
632 	treeview__cw_invalidate_area(tree, &r);
633 }
634 
635 
636 /**
637  * The treeview walk mode.  Controls which nodes are visited in a walk.
638  */
639 enum treeview_walk_mode {
640 	/**
641 	 * Walk to all nodes in the (sub)tree.
642 	 */
643 	TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
644 
645 	/**
646 	 * Walk to expanded nodes in the (sub)tree only.  Children of
647 	 * collapsed nodes are not visited.
648 	 */
649 	TREEVIEW_WALK_MODE_LOGICAL_EXPANDED,
650 
651 	/**
652 	 * Walk displayed nodes.  This differs from the
653 	 * `TREEVIEW_WALK_MODE_LOGICAL_EXPANDED` mode when there is
654 	 * an active search filter display.
655 	 */
656 	TREEVIEW_WALK_MODE_DISPLAY,
657 };
658 
659 
660 /**
661  * Walk a treeview subtree, calling a callback at each node (depth first)
662  *
663  * \param tree		Treeview being walked.
664  * \param root		Root to walk tree from (doesn't get a callback call)
665  * \param mode		The treeview walk mode to use.
666  * \param callback_bwd	Function to call on each node in backwards order
667  * \param callback_fwd	Function to call on each node in forwards order
668  * \param ctx		Context to pass to callback
669  * \return NSERROR_OK on success, or appropriate error otherwise
670  *
671  * \note Any node deletion must happen in callback_bwd.
672  */
treeview_walk_internal(treeview * tree,treeview_node * root,enum treeview_walk_mode mode,nserror (* callback_bwd)(treeview_node * n,void * ctx,bool * end),nserror (* callback_fwd)(treeview_node * n,void * ctx,bool * skip_children,bool * end),void * ctx)673 static nserror treeview_walk_internal(
674 		treeview *tree,
675 		treeview_node *root,
676 		enum treeview_walk_mode mode,
677 		nserror (*callback_bwd)(
678 				treeview_node *n,
679 				void *ctx,
680 				bool *end),
681 		nserror (*callback_fwd)(
682 				treeview_node *n,
683 				void *ctx,
684 				bool *skip_children,
685 				bool *end),
686 		void *ctx)
687 {
688 	treeview_node *node, *child, *parent, *next_sibling;
689 	bool walking_search = (mode == TREEVIEW_WALK_MODE_DISPLAY &&
690 			tree->search.search == true);
691 	bool skip_children = false;
692 	bool abort = false;
693 	bool full = false;
694 	nserror err;
695 	bool entry;
696 
697 	assert(root != NULL);
698 
699 	if (mode == TREEVIEW_WALK_MODE_LOGICAL_COMPLETE || walking_search) {
700 		/* We need to visit children of collapsed folders. */
701 		full = true;
702 	}
703 
704 	node = root;
705 	parent = node->parent;
706 	next_sibling = node->next_sib;
707 	child = (full || (node->flags & TV_NFLAGS_EXPANDED)) ?
708 		node->children : NULL;
709 
710 	while (node != NULL) {
711 
712 		if (child != NULL && !skip_children) {
713 			/* Down to children */
714 			node = child;
715 		} else {
716 			/* No children.  As long as we're not at the root,
717 			 * go to next sibling if present, or nearest ancestor
718 			 * with a next sibling. */
719 
720 			while (node != root && next_sibling == NULL) {
721 				entry = (node->type == TREE_NODE_ENTRY);
722 				if (callback_bwd != NULL &&
723 						(entry || !walking_search)) {
724 					/* Backwards callback */
725 					err = callback_bwd(node, ctx, &abort);
726 
727 					if (err != NSERROR_OK) {
728 						return err;
729 
730 					} else if (abort) {
731 						/* callback requested early
732 						 * termination */
733 						return NSERROR_OK;
734 					}
735 				}
736 				node = parent;
737 				parent = node->parent;
738 				next_sibling = node->next_sib;
739 			}
740 
741 			if (node == root)
742 				break;
743 
744 			if (callback_bwd != NULL) {
745 				/* Backwards callback */
746 				err = callback_bwd(node, ctx, &abort);
747 
748 				if (err != NSERROR_OK) {
749 					return err;
750 
751 				} else if (abort) {
752 					/* callback requested early
753 					 * termination */
754 					return NSERROR_OK;
755 				}
756 			}
757 			node = next_sibling;
758 		}
759 
760 		assert(node != NULL);
761 		assert(node != root);
762 
763 		entry = (node->type == TREE_NODE_ENTRY);
764 
765 		parent = node->parent;
766 		next_sibling = node->next_sib;
767 		child = (full || (node->flags & TV_NFLAGS_EXPANDED)) ?
768 			node->children : NULL;
769 
770 		if (walking_search && (!entry ||
771 				!(node->flags & TV_NFLAGS_MATCHED))) {
772 			continue;
773 		}
774 
775 		if (callback_fwd != NULL) {
776 			/* Forwards callback */
777 			err = callback_fwd(node, ctx, &skip_children, &abort);
778 
779 			if (err != NSERROR_OK) {
780 				return err;
781 
782 			} else if (abort) {
783 				/* callback requested early termination */
784 				return NSERROR_OK;
785 			}
786 		}
787 	}
788 	return NSERROR_OK;
789 }
790 
791 
792 /**
793  * Data used when doing a treeview walk for search.
794  */
795 struct treeview_search_walk_data {
796 	treeview *tree;          /**< The treeview to search. */
797 	const char *text;        /**< The string being searched for. */
798 	const unsigned int len;  /**< Length of string being searched for. */
799 	int window_height;       /**< Accumulate height for matching entries. */
800 };
801 
802 
803 /**
804  * Treewalk node callback for handling search.
805  *
806  * \param[in]     n              Current node.
807  * \param[in]     ctx            Treeview search context.
808  * \param[in,out] skip_children  Flag to allow children to be skipped.
809  * \param[in,out] end            Flag to allow iteration to be finished early.
810  * \return NSERROR_OK on success else error code.
811  */
treeview__search_walk_cb(treeview_node * n,void * ctx,bool * skip_children,bool * end)812 static nserror treeview__search_walk_cb(
813 		treeview_node *n,
814 		void *ctx,
815 		bool *skip_children,
816 		bool *end)
817 {
818 	struct treeview_search_walk_data *sw = ctx;
819 
820 	if (n->type != TREE_NODE_ENTRY) {
821 		return NSERROR_OK;
822 	}
823 
824 	if (sw->len == 0) {
825 		n->flags &= ~TV_NFLAGS_MATCHED;
826 	} else {
827 		struct treeview_node_entry *entry =
828 				(struct treeview_node_entry *)n;
829 		bool matched = false;
830 
831 		for (int i = 0; i < sw->tree->n_fields; i++) {
832 			struct treeview_field *ef = &(sw->tree->fields[i + 1]);
833 			if (ef->flags & TREE_FLAG_SEARCHABLE) {
834 				if (strcasestr(entry->fields[i].value.data,
835 						sw->text) != NULL) {
836 					matched = true;
837 					break;
838 				}
839 			}
840 		}
841 
842 		if (!matched && strcasestr(n->text.data, sw->text) != NULL) {
843 			matched = true;
844 		}
845 
846 		if (matched) {
847 			n->flags |= TV_NFLAGS_MATCHED;
848 			sw->window_height += n->height;
849 		} else {
850 			n->flags &= ~TV_NFLAGS_MATCHED;
851 		}
852 	}
853 
854 	return NSERROR_OK;
855 }
856 
857 
858 /**
859  * Search treeview for text.
860  *
861  * \param[in] tree  Treeview to search.
862  * \param[in] text  UTF-8 string to search for.  (NULL-terminated.)
863  * \param[in] len   Byte length of UTF-8 string.
864  * \return NSERROR_OK on success, appropriate error otherwise.
865  */
treeview__search(treeview * tree,const char * text,unsigned int len)866 static nserror treeview__search(
867 		treeview *tree,
868 		const char *text,
869 		unsigned int len)
870 {
871 	nserror err;
872 	uint32_t height;
873 	uint32_t prev_height = treeview__get_display_height(tree);
874 	int search_height = treeview__get_search_height(tree);
875 	struct treeview_search_walk_data sw = {
876 		.len = len,
877 		.text = text,
878 		.tree = tree,
879 		.window_height = 0,
880 	};
881 	struct rect r = {
882 		.x0 = 0,
883 		.y0 = search_height,
884 		.x1 = REDRAW_MAX,
885 	};
886 
887 	assert(text[len] == '\0');
888 
889 	if (tree->root == NULL) {
890 		return NSERROR_OK;
891 	}
892 
893 	err = treeview_walk_internal(tree, tree->root,
894 			TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, NULL,
895 			treeview__search_walk_cb, &sw);
896 	if (err != NSERROR_OK) {
897 		return err;
898 	}
899 
900 	if (len > 0) {
901 		tree->search.height = sw.window_height;
902 		tree->search.search = true;
903 		height = sw.window_height;
904 	} else {
905 		tree->search.search = false;
906 		height = tree->root->height;
907 	}
908 
909 	r.y1 = ((height > prev_height) ? height : prev_height) + search_height;
910 	treeview__cw_invalidate_area(tree, &r);
911 	treeview__cw_update_size(tree, -1, height);
912 	treeview__cw_scroll_top(tree);
913 
914 	return NSERROR_OK;
915 }
916 
917 
918 /**
919  * Cancel a treeview search, optionally droping focus from search widget.
920  *
921  * \param[in] tree        Treeview to cancel search in.
922  * \param[in] drop_focus  Iff true, drop input focus from search widget.
923  */
treeview__search_cancel(treeview * tree,bool drop_focus)924 static void treeview__search_cancel(treeview *tree, bool drop_focus)
925 {
926 	struct rect r = {
927 		.x0 = tree_g.window_padding + tree_g.icon_size,
928 		.x1 = 600,
929 		.y0 = 0,
930 		.y1 = tree_g.line_height,
931 	};
932 
933 	tree->search.search = false;
934 	if (tree->search.active == false) {
935 		return;
936 	}
937 
938 	if (textarea_get_text(tree->search.textarea, NULL, 0) == 1) {
939 		// If there's no text in the search box, we drop focus on a
940 		// cancel.  Note '1' because it includes the trailing \0
941 		drop_focus = true;
942 	}
943 
944 	if (drop_focus) {
945 		tree->search.active = false;
946 		textarea_set_caret(tree->search.textarea, -1);
947 	} else {
948 		textarea_set_caret(tree->search.textarea, 0);
949 	}
950 
951 	textarea_set_text(tree->search.textarea, "");
952 	treeview__cw_invalidate_area(tree, &r);
953 }
954 
955 
956 /**
957  * Callback for textarea_create, in desktop/treeview.h
958  *
959  * \param data treeview context
960  * \param msg textarea message
961  */
treeview_textarea_search_callback(void * data,struct textarea_msg * msg)962 static void treeview_textarea_search_callback(void *data,
963 		struct textarea_msg *msg)
964 {
965 	treeview *tree = data;
966 	struct rect *r;
967 
968 	if (tree->search.active == false || tree->root == NULL) {
969 		return;
970 	}
971 
972 	switch (msg->type) {
973 	case TEXTAREA_MSG_DRAG_REPORT:
974 		if (msg->data.drag == TEXTAREA_DRAG_NONE) {
975 			/* Textarea drag finished */
976 			tree->drag.type = TV_DRAG_NONE;
977 		} else {
978 			/* Textarea drag started */
979 			tree->drag.type = TV_DRAG_SEARCH;
980 		}
981 		treeview__cw_drag_status(tree, tree->drag.type);
982 		break;
983 
984 	case TEXTAREA_MSG_REDRAW_REQUEST:
985 		r = &msg->data.redraw;
986 		r->x0 += tree_g.window_padding + tree_g.icon_size;
987 		r->y0 += 0;
988 		r->x1 += 600;
989 		r->y1 += tree_g.line_height;
990 
991 		/* Redraw the textarea */
992 		treeview__cw_invalidate_area(tree, r);
993 		break;
994 
995 	case TEXTAREA_MSG_TEXT_MODIFIED:
996 		/* Textarea length includes trailing NULL, so subtract it. */
997 		treeview__search(tree,
998 				msg->data.modified.text,
999 				msg->data.modified.len - 1);
1000 		break;
1001 
1002 	default:
1003 		break;
1004 	}
1005 }
1006 
1007 
1008 /**
1009  * Update the layout for any active search.
1010  *
1011  * \param[in] tree  The tree to update.
1012  */
treeview__search_update_display(treeview * tree)1013 static void treeview__search_update_display(
1014 		treeview *tree)
1015 {
1016 	const char *string;
1017 	unsigned int len;
1018 
1019 	if (tree->search.search == false) {
1020 		/* No active search to update view for. */
1021 		return;
1022 	}
1023 
1024 	string = textarea_data(tree->search.textarea, &len);
1025 	if (string == NULL || len == 0) {
1026 		return;
1027 	}
1028 
1029 	treeview__search(tree, string, len - 1);
1030 }
1031 
1032 
1033 /**
1034  * Create treeview's root node
1035  *
1036  * \param[out] root Returns root node
1037  * \return NSERROR_OK on success, appropriate error otherwise
1038  */
treeview_create_node_root(treeview_node ** root)1039 static nserror treeview_create_node_root(treeview_node **root)
1040 {
1041 	treeview_node *n;
1042 
1043 	n = malloc(sizeof(struct treeview_node));
1044 	if (n == NULL) {
1045 		return NSERROR_NOMEM;
1046 	}
1047 
1048 	n->flags = TV_NFLAGS_EXPANDED;
1049 	n->type = TREE_NODE_ROOT;
1050 
1051 	n->height = 0;
1052 	n->inset = tree_g.window_padding - tree_g.step_width;
1053 
1054 	n->text.data = NULL;
1055 	n->text.len = 0;
1056 	n->text.width = 0;
1057 
1058 	n->parent = NULL;
1059 	n->next_sib = NULL;
1060 	n->prev_sib = NULL;
1061 	n->children = NULL;
1062 
1063 	n->client_data = NULL;
1064 
1065 	*root = n;
1066 
1067 	return NSERROR_OK;
1068 }
1069 
1070 
1071 /**
1072  * Set a node's inset from its parent
1073  *
1074  * This may be used as treeview walk callback
1075  *
1076  * \param[in] n node to set inset on
1077  * \param[in] ctx context unused
1078  * \param[out] skip_children set to false so child nodes are not skipped.
1079  * \param[out] end unused flag so treewalk in not terminated early.
1080  */
1081 static nserror
treeview_set_inset_from_parent(treeview_node * n,void * ctx,bool * skip_children,bool * end)1082 treeview_set_inset_from_parent(treeview_node *n,
1083 			       void *ctx,
1084 			       bool *skip_children,
1085 			       bool *end)
1086 {
1087 	if (n->parent != NULL)
1088 		n->inset = n->parent->inset + tree_g.step_width;
1089 
1090 	*skip_children = false;
1091 	return NSERROR_OK;
1092 }
1093 
1094 
1095 /**
1096  * Insert a treeview node into a treeview
1097  *
1098  * \param tree  the treeview to insert node into.
1099  * \param a     parentless node to insert
1100  * \param b     tree node to insert a as a relation of
1101  * \param rel The relationship between \a a and \a b
1102  */
1103 static inline void
treeview_insert_node(treeview * tree,treeview_node * a,treeview_node * b,enum treeview_relationship rel)1104 treeview_insert_node(
1105 		treeview *tree,
1106 		treeview_node *a,
1107 		treeview_node *b,
1108 		enum treeview_relationship rel)
1109 {
1110 	assert(a != NULL);
1111 	assert(a->parent == NULL);
1112 	assert(b != NULL);
1113 
1114 	switch (rel) {
1115 	case TREE_REL_FIRST_CHILD:
1116 		assert(b->type != TREE_NODE_ENTRY);
1117 		a->parent = b;
1118 		a->next_sib = b->children;
1119 		if (a->next_sib)
1120 			a->next_sib->prev_sib = a;
1121 		b->children = a;
1122 		break;
1123 
1124 	case TREE_REL_NEXT_SIBLING:
1125 		assert(b->type != TREE_NODE_ROOT);
1126 		a->prev_sib = b;
1127 		a->next_sib = b->next_sib;
1128 		a->parent = b->parent;
1129 		b->next_sib = a;
1130 		if (a->next_sib)
1131 			a->next_sib->prev_sib = a;
1132 		break;
1133 
1134 	default:
1135 		assert(0);
1136 		break;
1137 	}
1138 
1139 	assert(a->parent != NULL);
1140 
1141 	a->inset = a->parent->inset + tree_g.step_width;
1142 	if (a->children != NULL) {
1143 		treeview_walk_internal(tree, a,
1144 				TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, NULL,
1145 				treeview_set_inset_from_parent, NULL);
1146 	}
1147 
1148 	if (a->parent->flags & TV_NFLAGS_EXPANDED) {
1149 		int height = a->height;
1150 		/* Parent is expanded, so inserted node will be visible and
1151 		 * affect layout */
1152 		if (a->text.width == 0) {
1153 			guit->layout->width(&plot_style_odd.text,
1154 					    a->text.data,
1155 					    a->text.len,
1156 					    &(a->text.width));
1157 		}
1158 
1159 		do {
1160 			a->parent->height += height;
1161 			a = a->parent;
1162 		} while (a->parent != NULL);
1163 	}
1164 }
1165 
1166 
1167 /* Exported interface, documented in treeview.h */
1168 nserror
treeview_create_node_folder(treeview * tree,treeview_node ** folder,treeview_node * relation,enum treeview_relationship rel,const struct treeview_field_data * field,void * data,treeview_node_options_flags flags)1169 treeview_create_node_folder(treeview *tree,
1170 			    treeview_node **folder,
1171 			    treeview_node *relation,
1172 			    enum treeview_relationship rel,
1173 			    const struct treeview_field_data *field,
1174 			    void *data,
1175 			    treeview_node_options_flags flags)
1176 {
1177 	treeview_node *n;
1178 
1179 	assert(data != NULL);
1180 	assert(tree != NULL);
1181 	assert(tree->root != NULL);
1182 
1183 	if (relation == NULL) {
1184 		relation = tree->root;
1185 		rel = TREE_REL_FIRST_CHILD;
1186 	}
1187 
1188 	n = malloc(sizeof(struct treeview_node));
1189 	if (n == NULL) {
1190 		return NSERROR_NOMEM;
1191 	}
1192 
1193 	n->flags = (flags & TREE_OPTION_SPECIAL_DIR) ?
1194 		TV_NFLAGS_SPECIAL : TV_NFLAGS_NONE;
1195 	n->type = TREE_NODE_FOLDER;
1196 
1197 	n->height = tree_g.line_height;
1198 
1199 	n->text.data = field->value;
1200 	n->text.len = field->value_len;
1201 	n->text.width = 0;
1202 
1203 	n->parent = NULL;
1204 	n->next_sib = NULL;
1205 	n->prev_sib = NULL;
1206 	n->children = NULL;
1207 
1208 	n->client_data = data;
1209 
1210 	treeview_insert_node(tree, n, relation, rel);
1211 
1212 	if (n->parent->flags & TV_NFLAGS_EXPANDED) {
1213 		/* Inform front end of change in dimensions */
1214 		if (!(flags & TREE_OPTION_SUPPRESS_RESIZE))
1215 			treeview__cw_update_size(tree, -1,
1216 						 tree->root->height);
1217 
1218 		/* Redraw */
1219 		if (!(flags & TREE_OPTION_SUPPRESS_REDRAW)) {
1220 			struct rect r;
1221 			r.x0 = 0;
1222 			r.y0 = treeview_node_y(tree, n);
1223 			r.x1 = REDRAW_MAX;
1224 			r.y1 = tree->root->height;
1225 			treeview__cw_invalidate_area(tree, &r);
1226 		}
1227 	}
1228 
1229 	*folder = n;
1230 
1231 	return NSERROR_OK;
1232 }
1233 
1234 
1235 /* Exported interface, documented in treeview.h */
1236 nserror
treeview_update_node_folder(treeview * tree,treeview_node * folder,const struct treeview_field_data * field,void * data)1237 treeview_update_node_folder(treeview *tree,
1238 			    treeview_node *folder,
1239 			    const struct treeview_field_data *field,
1240 			    void *data)
1241 {
1242 	bool match;
1243 
1244 	assert(data != NULL);
1245 	assert(tree != NULL);
1246 	assert(folder != NULL);
1247 	assert(data == folder->client_data);
1248 	assert(folder->parent != NULL);
1249 
1250 	assert(field != NULL);
1251 	assert(lwc_string_isequal(tree->fields[tree->n_fields].field,
1252 				  field->field, &match) == lwc_error_ok &&
1253 	       match == true);
1254 	folder->text.data = field->value;
1255 	folder->text.len = field->value_len;
1256 	folder->text.width = 0;
1257 
1258 	if (folder->parent->flags & TV_NFLAGS_EXPANDED) {
1259 		/* Text will be seen, get its width */
1260 		guit->layout->width(&plot_style_odd.text,
1261 				    folder->text.data,
1262 				    folder->text.len,
1263 				    &(folder->text.width));
1264 	} else {
1265 		/* Just invalidate the width, since it's not needed now */
1266 		folder->text.width = 0;
1267 	}
1268 
1269 	/* Redraw */
1270 	if (folder->parent->flags & TV_NFLAGS_EXPANDED) {
1271 		struct rect r;
1272 		r.x0 = 0;
1273 		r.y0 = treeview_node_y(tree, folder);
1274 		r.x1 = REDRAW_MAX;
1275 		r.y1 = r.y0 + tree_g.line_height;
1276 		treeview__cw_invalidate_area(tree, &r);
1277 	}
1278 
1279 	return NSERROR_OK;
1280 }
1281 
1282 
1283 /* Exported interface, documented in treeview.h */
1284 nserror
treeview_update_node_entry(treeview * tree,treeview_node * entry,const struct treeview_field_data fields[],void * data)1285 treeview_update_node_entry(treeview *tree,
1286 			   treeview_node *entry,
1287 			   const struct treeview_field_data fields[],
1288 			   void *data)
1289 {
1290 	bool match;
1291 	struct treeview_node_entry *e = (struct treeview_node_entry *)entry;
1292 	int i;
1293 
1294 	assert(data != NULL);
1295 	assert(tree != NULL);
1296 	assert(entry != NULL);
1297 	assert(data == entry->client_data);
1298 	assert(entry->parent != NULL);
1299 
1300 	assert(fields != NULL);
1301 	assert(fields[0].field != NULL);
1302 	assert(lwc_string_isequal(tree->fields[0].field,
1303 				  fields[0].field, &match) == lwc_error_ok &&
1304 	       match == true);
1305 	entry->text.data = fields[0].value;
1306 	entry->text.len = fields[0].value_len;
1307 	entry->text.width = 0;
1308 
1309 	if (entry->parent->flags & TV_NFLAGS_EXPANDED) {
1310 		/* Text will be seen, get its width */
1311 		guit->layout->width(&plot_style_odd.text,
1312 				    entry->text.data,
1313 				    entry->text.len,
1314 				    &(entry->text.width));
1315 	} else {
1316 		/* Just invalidate the width, since it's not needed now */
1317 		entry->text.width = 0;
1318 	}
1319 
1320 	for (i = 1; i < tree->n_fields; i++) {
1321 		assert(fields[i].field != NULL);
1322 		assert(lwc_string_isequal(tree->fields[i].field,
1323 					  fields[i].field, &match) == lwc_error_ok &&
1324 		       match == true);
1325 
1326 		e->fields[i - 1].value.data = fields[i].value;
1327 		e->fields[i - 1].value.len = fields[i].value_len;
1328 
1329 		if (entry->flags & TV_NFLAGS_EXPANDED) {
1330 			/* Text will be seen, get its width */
1331 			guit->layout->width(&plot_style_odd.text,
1332 					    e->fields[i - 1].value.data,
1333 					    e->fields[i - 1].value.len,
1334 					    &(e->fields[i - 1].value.width));
1335 		} else {
1336 			/* Invalidate the width, since it's not needed yet */
1337 			e->fields[i - 1].value.width = 0;
1338 		}
1339 	}
1340 
1341 	treeview__search_update_display(tree);
1342 
1343 	/* Redraw */
1344 	if (entry->parent->flags & TV_NFLAGS_EXPANDED) {
1345 		struct rect r;
1346 		r.x0 = 0;
1347 		r.y0 = treeview_node_y(tree, entry);
1348 		r.x1 = REDRAW_MAX;
1349 		r.y1 = r.y0 + entry->height;
1350 		treeview__cw_invalidate_area(tree, &r);
1351 	}
1352 
1353 	return NSERROR_OK;
1354 }
1355 
1356 
1357 /* Exported interface, documented in treeview.h */
1358 nserror
treeview_create_node_entry(treeview * tree,treeview_node ** entry,treeview_node * relation,enum treeview_relationship rel,const struct treeview_field_data fields[],void * data,treeview_node_options_flags flags)1359 treeview_create_node_entry(treeview *tree,
1360 			   treeview_node **entry,
1361 			   treeview_node *relation,
1362 			   enum treeview_relationship rel,
1363 			   const struct treeview_field_data fields[],
1364 			   void *data,
1365 			   treeview_node_options_flags flags)
1366 {
1367 	bool match;
1368 	struct treeview_node_entry *e;
1369 	treeview_node *n;
1370 	int i;
1371 
1372 	assert(data != NULL);
1373 	assert(tree != NULL);
1374 	assert(tree->root != NULL);
1375 
1376 	if (relation == NULL) {
1377 		relation = tree->root;
1378 		rel = TREE_REL_FIRST_CHILD;
1379 	}
1380 
1381 	e = malloc(sizeof(struct treeview_node_entry) +
1382 			(tree->n_fields - 1) * sizeof(struct treeview_field));
1383 	if (e == NULL) {
1384 		return NSERROR_NOMEM;
1385 	}
1386 
1387 
1388 	n = (treeview_node *) e;
1389 
1390 	n->flags = TV_NFLAGS_NONE;
1391 	n->type = TREE_NODE_ENTRY;
1392 
1393 	n->height = tree_g.line_height;
1394 
1395 	assert(fields != NULL);
1396 	assert(fields[0].field != NULL);
1397 	assert(lwc_string_isequal(tree->fields[0].field,
1398 			fields[0].field, &match) == lwc_error_ok &&
1399 			match == true);
1400 	n->text.data = fields[0].value;
1401 	n->text.len = fields[0].value_len;
1402 	n->text.width = 0;
1403 
1404 	n->parent = NULL;
1405 	n->next_sib = NULL;
1406 	n->prev_sib = NULL;
1407 	n->children = NULL;
1408 
1409 	n->client_data = data;
1410 
1411 	for (i = 1; i < tree->n_fields; i++) {
1412 		assert(fields[i].field != NULL);
1413 		assert(lwc_string_isequal(tree->fields[i].field,
1414 				fields[i].field, &match) == lwc_error_ok &&
1415 				match == true);
1416 
1417 		e->fields[i - 1].value.data = fields[i].value;
1418 		e->fields[i - 1].value.len = fields[i].value_len;
1419 		e->fields[i - 1].value.width = 0;
1420 	}
1421 
1422 	treeview_insert_node(tree, n, relation, rel);
1423 
1424 	if (n->parent->flags & TV_NFLAGS_EXPANDED) {
1425 		/* Inform front end of change in dimensions */
1426 		if (!(flags & TREE_OPTION_SUPPRESS_RESIZE))
1427 			treeview__cw_update_size(tree, -1,
1428 						 tree->root->height);
1429 
1430 		/* Redraw */
1431 		if (!(flags & TREE_OPTION_SUPPRESS_REDRAW)) {
1432 			struct rect r;
1433 			r.x0 = 0;
1434 			r.y0 = treeview_node_y(tree, n);
1435 			r.x1 = REDRAW_MAX;
1436 			r.y1 = tree->root->height;
1437 			treeview__cw_invalidate_area(tree, &r);
1438 		}
1439 	}
1440 
1441 	treeview__search_update_display(tree);
1442 
1443 	*entry = n;
1444 
1445 	return NSERROR_OK;
1446 }
1447 
1448 
1449 /**
1450  * Treewalk iterator context
1451  */
1452 struct treeview_walk_ctx {
1453 	treeview_walk_cb enter_cb;
1454 	treeview_walk_cb leave_cb;
1455 	void *ctx;
1456 	enum treeview_node_type type;
1457 };
1458 
1459 
1460 /**
1461  * Treewalk node enter callback.
1462  *
1463  * \param n current node
1464  * \param ctx treewalk context
1465  * \param skip_children set if child nodes should be skipped
1466  * \param end set if iteration should end early
1467  */
1468 static nserror
treeview_walk_fwd_cb(treeview_node * n,void * ctx,bool * skip_children,bool * end)1469 treeview_walk_fwd_cb(treeview_node *n,
1470 		     void *ctx,
1471 		     bool *skip_children,
1472 		     bool *end)
1473 {
1474 	struct treeview_walk_ctx *tw = ctx;
1475 
1476 	if (n->type & tw->type) {
1477 		return tw->enter_cb(tw->ctx, n->client_data, n->type, end);
1478 	}
1479 
1480 	return NSERROR_OK;
1481 }
1482 
1483 
1484 /**
1485  * Treewalk node leave callback.
1486  *
1487  * \param n current node
1488  * \param ctx treewalk context
1489  * \param end set if iteration should end early
1490  */
treeview_walk_bwd_cb(treeview_node * n,void * ctx,bool * end)1491 static nserror treeview_walk_bwd_cb(treeview_node *n, void *ctx, bool *end)
1492 {
1493 	struct treeview_walk_ctx *tw = ctx;
1494 
1495 	if (n->type & tw->type) {
1496 		return tw->leave_cb(tw->ctx, n->client_data, n->type, end);
1497 	}
1498 
1499 	return NSERROR_OK;
1500 }
1501 
1502 
1503 /* Exported interface, documented in treeview.h */
1504 nserror
treeview_walk(treeview * tree,treeview_node * root,treeview_walk_cb enter_cb,treeview_walk_cb leave_cb,void * ctx,enum treeview_node_type type)1505 treeview_walk(treeview *tree,
1506 	      treeview_node *root,
1507 	      treeview_walk_cb enter_cb,
1508 	      treeview_walk_cb leave_cb,
1509 	      void *ctx,
1510 	      enum treeview_node_type type)
1511 {
1512 	struct treeview_walk_ctx tw = {
1513 		.enter_cb = enter_cb,
1514 		.leave_cb = leave_cb,
1515 		.ctx = ctx,
1516 		.type = type
1517 	};
1518 
1519 	assert(tree != NULL);
1520 	assert(tree->root != NULL);
1521 
1522 	if (root == NULL)
1523 		root = tree->root;
1524 
1525 	return treeview_walk_internal(tree, root,
1526 			TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
1527 			(leave_cb != NULL) ? treeview_walk_bwd_cb : NULL,
1528 			(enter_cb != NULL) ? treeview_walk_fwd_cb : NULL,
1529 			&tw);
1530 }
1531 
1532 
1533 /**
1534  * Unlink a treeview node
1535  *
1536  * \param n Node to unlink
1537  * \return true iff ancestor heights need to be reduced
1538  */
treeview_unlink_node(treeview_node * n)1539 static inline bool treeview_unlink_node(treeview_node *n)
1540 {
1541 	/* Unlink node from tree */
1542 	if (n->parent != NULL && n->parent->children == n) {
1543 		/* Node is a first child */
1544 		n->parent->children = n->next_sib;
1545 
1546 	} else if (n->prev_sib != NULL) {
1547 		/* Node is not first child */
1548 		n->prev_sib->next_sib = n->next_sib;
1549 	}
1550 
1551 	if (n->next_sib != NULL) {
1552 		/* Always need to do this */
1553 		n->next_sib->prev_sib = n->prev_sib;
1554 	}
1555 
1556 	/* Reduce ancestor heights */
1557 	if ((n->parent != NULL) &&
1558 	    (n->parent->flags & TV_NFLAGS_EXPANDED)) {
1559 		return true;
1560 	}
1561 
1562 	return false;
1563 }
1564 
1565 
1566 /**
1567  * Cancel the editing of a treeview node
1568  *
1569  * \param tree Treeview object to cancel node editing in
1570  * \param redraw Set true iff redraw of removed textarea area required
1571  */
treeview_edit_cancel(treeview * tree,bool redraw)1572 static void treeview_edit_cancel(treeview *tree, bool redraw)
1573 {
1574 	struct rect r;
1575 
1576 	if (tree->edit.textarea == NULL)
1577 		return;
1578 
1579 	textarea_destroy(tree->edit.textarea);
1580 
1581 	tree->edit.textarea = NULL;
1582 	tree->edit.node = NULL;
1583 
1584 	if (tree->drag.type == TV_DRAG_TEXTAREA)
1585 		tree->drag.type = TV_DRAG_NONE;
1586 
1587 	if (redraw) {
1588 		r.x0 = tree->edit.x;
1589 		r.y0 = tree->edit.y;
1590 		r.x1 = tree->edit.x + tree->edit.w;
1591 		r.y1 = tree->edit.y + tree->edit.h;
1592 		treeview__cw_invalidate_area(tree, &r);
1593 	}
1594 }
1595 
1596 
1597 /**
1598  * Complete a treeview edit
1599  *
1600  * Complete edit by informing the client with a change request msg
1601  *
1602  * \param tree Treeview object to complete edit in
1603  */
treeview_edit_done(treeview * tree)1604 static void treeview_edit_done(treeview *tree)
1605 {
1606 	int len, error;
1607 	char* new_text;
1608 	treeview_node *n = tree->edit.node;
1609 	struct treeview_node_msg msg;
1610 	msg.msg = TREE_MSG_NODE_EDIT;
1611 
1612 	if (tree->edit.textarea == NULL) {
1613 		return;
1614 	}
1615 
1616 	assert(n != NULL);
1617 
1618 	/* Get new text length */
1619 	len = textarea_get_text(tree->edit.textarea, NULL, 0);
1620 
1621 	new_text = malloc(len);
1622 	if (new_text == NULL) {
1623 		/* TODO: don't just silently ignore */
1624 		return;
1625 	}
1626 
1627 	/* Get the new text from textarea */
1628 	error = textarea_get_text(tree->edit.textarea, new_text, len);
1629 	if (error == -1) {
1630 		/* TODO: don't just silently ignore */
1631 		free(new_text);
1632 		return;
1633 	}
1634 
1635 	/* Inform the treeview client with change request message */
1636 	msg.data.node_edit.field = tree->edit.field;
1637 	msg.data.node_edit.text = new_text;
1638 
1639 	switch (n->type) {
1640 	case TREE_NODE_ENTRY:
1641 		tree->callbacks->entry(msg, n->client_data);
1642 		break;
1643 	case TREE_NODE_FOLDER:
1644 		tree->callbacks->folder(msg, n->client_data);
1645 		break;
1646 	case TREE_NODE_ROOT:
1647 		break;
1648 	default:
1649 		break;
1650 	}
1651 
1652 	/* Finished with the new text */
1653 	free(new_text);
1654 
1655 	/* Finally, destroy the treeview, and redraw */
1656 	treeview_edit_cancel(tree, true);
1657 }
1658 
1659 
1660 /**
1661  * context for treeview node deletion iterator
1662  */
1663 struct treeview_node_delete {
1664 	treeview *tree;
1665 	int h_reduction;
1666 	bool user_interaction;
1667 };
1668 
1669 
1670 /**
1671  * Treewalk node callback deleting nodes.
1672  */
1673 static nserror
treeview_delete_node_walk_cb(treeview_node * n,void * ctx,bool * end)1674 treeview_delete_node_walk_cb(treeview_node *n, void *ctx, bool *end)
1675 {
1676 	struct treeview_node_delete *nd = (struct treeview_node_delete *)ctx;
1677 	struct treeview_node_msg msg;
1678 
1679 	msg.msg = TREE_MSG_NODE_DELETE;
1680 	msg.data.delete.user = nd->user_interaction;
1681 
1682 	assert(n->children == NULL);
1683 
1684 	if (treeview_unlink_node(n))
1685 		nd->h_reduction += (n->type == TREE_NODE_ENTRY) ?
1686 			n->height : tree_g.line_height;
1687 
1688 	/* Handle any special treatment */
1689 	switch (n->type) {
1690 	case TREE_NODE_ENTRY:
1691 		nd->tree->callbacks->entry(msg, n->client_data);
1692 		break;
1693 
1694 	case TREE_NODE_FOLDER:
1695 		nd->tree->callbacks->folder(msg, n->client_data);
1696 		break;
1697 
1698 	case TREE_NODE_ROOT:
1699 		break;
1700 
1701 	default:
1702 		return NSERROR_BAD_PARAMETER;
1703 	}
1704 
1705 	/* Cancel any edit of this node */
1706 	if (nd->tree->edit.textarea != NULL &&
1707 	    nd->tree->edit.node == n) {
1708 		treeview_edit_cancel(nd->tree, false);
1709 	}
1710 
1711 	/* Free the node */
1712 	free(n);
1713 
1714 	return NSERROR_OK;
1715 }
1716 
1717 
1718 /**
1719  * Delete a treeview node
1720  *
1721  * Will emit folder or entry deletion msg callback.
1722  *
1723  * \note this can be called from inside a treeview_walk fwd callback.
1724  * For example walking the tree and calling this for any node that's selected.
1725  *
1726  * This function does not delete empty nodes, so if TREEVIEW_DEL_EMPTY_DIRS is
1727  * set, caller must also call treeview_delete_empty.
1728  *
1729  * \param tree		Treeview object to delete node from
1730  * \param n		Node to delete
1731  * \param interaction	Delete is result of user interaction with treeview
1732  * \param flags		Treeview node options flags
1733  * \return NSERROR_OK on success, appropriate error otherwise
1734  */
1735 static nserror
treeview_delete_node_internal(treeview * tree,treeview_node * n,bool interaction,treeview_node_options_flags flags)1736 treeview_delete_node_internal(treeview *tree,
1737 			      treeview_node *n,
1738 			      bool interaction,
1739 			      treeview_node_options_flags flags)
1740 {
1741 	nserror err;
1742 	treeview_node *p = n->parent;
1743 	struct treeview_node_delete nd = {
1744 		.tree = tree,
1745 		.h_reduction = 0,
1746 		.user_interaction = interaction
1747 	};
1748 
1749 	if (interaction && (tree->flags & TREEVIEW_NO_DELETES)) {
1750 		return NSERROR_OK;
1751 	}
1752 
1753 	/* Delete any children first */
1754 	err = treeview_walk_internal(tree, n,
1755 			TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
1756 			treeview_delete_node_walk_cb, NULL, &nd);
1757 	if (err != NSERROR_OK) {
1758 		return err;
1759 	}
1760 
1761 	/* Now delete node */
1762 	if (n == tree->root)
1763 		tree->root = NULL;
1764 	err = treeview_delete_node_walk_cb(n, &nd, false);
1765 	if (err != NSERROR_OK) {
1766 		return err;
1767 	}
1768 
1769 	n = p;
1770 	/* Reduce ancestor heights */
1771 	while (n != NULL && n->flags & TV_NFLAGS_EXPANDED) {
1772 		n->height -= nd.h_reduction;
1773 		n = n->parent;
1774 	}
1775 
1776 	/* Inform front end of change in dimensions */
1777 	if (tree->root != NULL && p != NULL && p->flags & TV_NFLAGS_EXPANDED &&
1778 	    nd.h_reduction > 0 &&
1779 	    !(flags & TREE_OPTION_SUPPRESS_RESIZE)) {
1780 		treeview__cw_update_size(tree, -1,
1781 					 tree->root->height);
1782 	}
1783 
1784 	treeview__search_update_display(tree);
1785 
1786 	return NSERROR_OK;
1787 }
1788 
1789 
1790 /**
1791  * Delete any empty treeview folder nodes
1792  *
1793  * \param tree Treeview object to delete empty nodes from
1794  * \param interaction Delete is result of user interaction with treeview
1795  * \return NSERROR_OK on success, appropriate error otherwise
1796  *
1797  * Note this must not be called within a treeview_walk.  It may delete the
1798  * walker's 'current' node, making it impossible to move on without invalid
1799  * reads.
1800  */
treeview_delete_empty_nodes(treeview * tree,bool interaction)1801 static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction)
1802 {
1803 	treeview_node *node, *child, *parent, *next_sibling, *p;
1804 	bool abort = false;
1805 	nserror err;
1806 	struct treeview_node_delete nd = {
1807 		.tree = tree,
1808 		.h_reduction = 0,
1809 		.user_interaction = interaction
1810 	};
1811 
1812 	assert(tree != NULL);
1813 	assert(tree->root != NULL);
1814 
1815 	node = tree->root;
1816 	parent = node->parent;
1817 	next_sibling = node->next_sib;
1818 	child = (node->flags & TV_NFLAGS_EXPANDED) ? node->children : NULL;
1819 
1820 	while (node != NULL) {
1821 
1822 		if (child != NULL) {
1823 			/* Down to children */
1824 			node = child;
1825 		} else {
1826 			/* No children.  As long as we're not at the root,
1827 			 * go to next sibling if present, or nearest ancestor
1828 			 * with a next sibling. */
1829 
1830 			while (node->parent != NULL &&
1831 			       next_sibling == NULL) {
1832 				if (node->type == TREE_NODE_FOLDER &&
1833 				    node->children == NULL) {
1834 					/* Delete node */
1835 					p = node->parent;
1836 					err = treeview_delete_node_walk_cb(
1837 						node, &nd, &abort);
1838 					if (err != NSERROR_OK) {
1839 						return err;
1840 					}
1841 
1842 					/* Reduce ancestor heights */
1843 					while (p != NULL &&
1844 					       p->flags &
1845 					       TV_NFLAGS_EXPANDED) {
1846 						p->height -= nd.h_reduction;
1847 						p = p->parent;
1848 					}
1849 					nd.h_reduction = 0;
1850 				}
1851 				node = parent;
1852 				parent = node->parent;
1853 				next_sibling = node->next_sib;
1854 			}
1855 
1856 			if (node->parent == NULL)
1857 				break;
1858 
1859 			if (node->type == TREE_NODE_FOLDER &&
1860 			    node->children == NULL) {
1861 				/* Delete node */
1862 				p = node->parent;
1863 				err = treeview_delete_node_walk_cb(
1864 					node, &nd, &abort);
1865 				if (err != NSERROR_OK) {
1866 					return err;
1867 				}
1868 
1869 				/* Reduce ancestor heights */
1870 				while (p != NULL &&
1871 				       p->flags & TV_NFLAGS_EXPANDED) {
1872 					p->height -= nd.h_reduction;
1873 					p = p->parent;
1874 				}
1875 				nd.h_reduction = 0;
1876 			}
1877 			node = next_sibling;
1878 		}
1879 
1880 		assert(node != NULL);
1881 		assert(node->parent != NULL);
1882 
1883 		parent = node->parent;
1884 		next_sibling = node->next_sib;
1885 		child = (node->flags & TV_NFLAGS_EXPANDED) ?
1886 			node->children : NULL;
1887 	}
1888 
1889 	return NSERROR_OK;
1890 }
1891 
1892 
1893 /* Exported interface, documented in treeview.h */
1894 nserror
treeview_delete_node(treeview * tree,treeview_node * n,treeview_node_options_flags flags)1895 treeview_delete_node(treeview *tree,
1896 		     treeview_node *n,
1897 		     treeview_node_options_flags flags)
1898 {
1899 	nserror err;
1900 	struct rect r;
1901 	bool visible;
1902 
1903 	assert(tree != NULL);
1904 	assert(n != NULL);
1905 	assert(n->parent != NULL);
1906 
1907 	visible = n->parent->flags & TV_NFLAGS_EXPANDED;
1908 
1909 	r.y0 = treeview_node_y(tree, n);
1910 	r.y1 = tree->root->height;
1911 
1912 	err = treeview_delete_node_internal(tree, n, false, flags);
1913 	if (err != NSERROR_OK)
1914 		return err;
1915 
1916 	if (tree->flags & TREEVIEW_DEL_EMPTY_DIRS) {
1917 		int h = tree->root->height;
1918 		/* Delete any empty nodes */
1919 		err = treeview_delete_empty_nodes(tree, false);
1920 		if (err != NSERROR_OK)
1921 			return err;
1922 
1923 		/* Inform front end of change in dimensions */
1924 		if (tree->root->height != h) {
1925 			r.y0 = 0;
1926 			if (!(flags & TREE_OPTION_SUPPRESS_RESIZE)) {
1927 				treeview__cw_update_size(tree, -1,
1928 							 tree->root->height);
1929 			}
1930 		}
1931 	}
1932 
1933 	/* Redraw */
1934 	if (visible && !(flags & TREE_OPTION_SUPPRESS_REDRAW)) {
1935 		r.x0 = 0;
1936 		r.x1 = REDRAW_MAX;
1937 		treeview__cw_invalidate_area(tree, &r);
1938 	}
1939 
1940 	return NSERROR_OK;
1941 }
1942 
1943 
1944 /**
1945  * Helper to create a textarea.
1946  *
1947  * \param[in] tree         The treeview we're creating the textarea for.
1948  * \param[in] width        The width of the textarea.
1949  * \param[in] height       The height of the textarea.
1950  * \param[in] border       The border colour to use.
1951  * \param[in] background   The background colour to use.
1952  * \param[in] foreground   The foreground colour to use.
1953  * \param[in] text         The text style to use for the text area.
1954  * \param[in] ta_callback  The textarea callback function to give the textarea.
1955  * \return the textarea pointer on success, or NULL on failure.
1956  */
treeview__create_textarea(treeview * tree,int width,int height,colour border,colour background,colour foreground,plot_font_style_t text,textarea_client_callback ta_callback)1957 static struct textarea *treeview__create_textarea(
1958 		treeview *tree,
1959 		int width,
1960 		int height,
1961 		colour border,
1962 		colour background,
1963 		colour foreground,
1964 		plot_font_style_t text,
1965 		textarea_client_callback ta_callback)
1966 {
1967 	/* Configure the textarea */
1968 	textarea_flags ta_flags = TEXTAREA_INTERNAL_CARET;
1969 	textarea_setup ta_setup = {
1970 		.text = text,
1971 		.width = width,
1972 		.height = height,
1973 		.pad_top = 0,
1974 		.pad_left = 2,
1975 		.pad_right = 2,
1976 		.pad_bottom = 0,
1977 		.border_width = 1,
1978 		.border_col = border,
1979 		.selected_bg = foreground,
1980 		.selected_text = background,
1981 	};
1982 
1983 	ta_setup.text.foreground = foreground;
1984 	ta_setup.text.background = background;
1985 
1986 	/* Create text area */
1987 	return textarea_create(ta_flags, &ta_setup, ta_callback, tree);
1988 }
1989 
1990 
1991 /* Exported interface, documented in treeview.h */
1992 nserror
treeview_create(treeview ** tree,const struct treeview_callback_table * callbacks,int n_fields,struct treeview_field_desc fields[],const struct core_window_callback_table * cw_t,struct core_window * cw,treeview_flags flags)1993 treeview_create(treeview **tree,
1994 		const struct treeview_callback_table *callbacks,
1995 		int n_fields,
1996 		struct treeview_field_desc fields[],
1997 		const struct core_window_callback_table *cw_t,
1998 		struct core_window *cw,
1999 		treeview_flags flags)
2000 {
2001 	nserror error;
2002 	int i;
2003 
2004 	assert((cw_t == NULL && cw == NULL) || (cw_t != NULL && cw != NULL));
2005 	assert(callbacks != NULL);
2006 
2007 	assert(fields != NULL);
2008 	assert(fields[0].flags & TREE_FLAG_DEFAULT);
2009 	assert(fields[n_fields - 1].flags & TREE_FLAG_DEFAULT);
2010 	assert(n_fields >= 2);
2011 
2012 	*tree = malloc(sizeof(struct treeview));
2013 	if (*tree == NULL) {
2014 		return NSERROR_NOMEM;
2015 	}
2016 
2017 	(*tree)->fields = malloc(sizeof(struct treeview_field) * n_fields);
2018 	if ((*tree)->fields == NULL) {
2019 		free(*tree);
2020 		return NSERROR_NOMEM;
2021 	}
2022 
2023 	error = treeview_create_node_root(&((*tree)->root));
2024 	if (error != NSERROR_OK) {
2025 		free((*tree)->fields);
2026 		free(*tree);
2027 		return error;
2028 	}
2029 
2030 	(*tree)->field_width = 0;
2031 	for (i = 0; i < n_fields; i++) {
2032 		struct treeview_field *f = &((*tree)->fields[i]);
2033 
2034 		f->flags = fields[i].flags;
2035 		f->field = lwc_string_ref(fields[i].field);
2036 		f->value.data = lwc_string_data(fields[i].field);
2037 		f->value.len = lwc_string_length(fields[i].field);
2038 
2039 		guit->layout->width(&plot_style_odd.text, f->value.data,
2040 				    f->value.len, &(f->value.width));
2041 
2042 		if (f->flags & TREE_FLAG_SHOW_NAME)
2043 			if ((*tree)->field_width < f->value.width)
2044 				(*tree)->field_width = f->value.width;
2045 	}
2046 
2047 	(*tree)->field_width += tree_g.step_width;
2048 
2049 	(*tree)->callbacks = callbacks;
2050 	(*tree)->n_fields = n_fields - 1;
2051 
2052 	(*tree)->drag.type = TV_DRAG_NONE;
2053 	(*tree)->drag.start_node = NULL;
2054 	(*tree)->drag.start.x = 0;
2055 	(*tree)->drag.start.y = 0;
2056 	(*tree)->drag.start.node_y = 0;
2057 	(*tree)->drag.start.node_h = 0;
2058 	(*tree)->drag.prev.x = 0;
2059 	(*tree)->drag.prev.y = 0;
2060 	(*tree)->drag.prev.node_y = 0;
2061 	(*tree)->drag.prev.node_h = 0;
2062 
2063 	(*tree)->move.root = NULL;
2064 	(*tree)->move.target = NULL;
2065 	(*tree)->move.target_pos = TV_TARGET_NONE;
2066 
2067 	(*tree)->edit.textarea = NULL;
2068 	(*tree)->edit.node = NULL;
2069 
2070 	if (flags & TREEVIEW_SEARCHABLE) {
2071 		(*tree)->search.textarea = treeview__create_textarea(
2072 				*tree, 600, tree_g.line_height,
2073 				nscolours[NSCOLOUR_TEXT_INPUT_BG],
2074 				nscolours[NSCOLOUR_TEXT_INPUT_BG],
2075 				nscolours[NSCOLOUR_TEXT_INPUT_FG],
2076 				plot_style_odd.text,
2077 				treeview_textarea_search_callback);
2078 		if ((*tree)->search.textarea == NULL) {
2079 			treeview_destroy(*tree);
2080 			return NSERROR_NOMEM;
2081 		}
2082 	} else {
2083 		(*tree)->search.textarea = NULL;
2084 	}
2085 	(*tree)->search.active = false;
2086 	(*tree)->search.search = false;
2087 
2088 	(*tree)->flags = flags;
2089 
2090 	(*tree)->cw_t = cw_t;
2091 	(*tree)->cw_h = cw;
2092 
2093 	return NSERROR_OK;
2094 }
2095 
2096 
2097 /* Exported interface, documented in treeview.h */
2098 nserror
treeview_cw_attach(treeview * tree,const struct core_window_callback_table * cw_t,struct core_window * cw)2099 treeview_cw_attach(treeview *tree,
2100 		   const struct core_window_callback_table *cw_t,
2101 		   struct core_window *cw)
2102 {
2103 	assert(cw_t != NULL);
2104 	assert(cw != NULL);
2105 
2106 	if (tree->cw_t != NULL || tree->cw_h != NULL) {
2107 		NSLOG(netsurf, INFO, "Treeview already attached.");
2108 		return NSERROR_UNKNOWN;
2109 	}
2110 	tree->cw_t = cw_t;
2111 	tree->cw_h = cw;
2112 
2113 	return NSERROR_OK;
2114 }
2115 
2116 
2117 /* Exported interface, documented in treeview.h */
treeview_cw_detach(treeview * tree)2118 nserror treeview_cw_detach(treeview *tree)
2119 {
2120 	tree->cw_t = NULL;
2121 	tree->cw_h = NULL;
2122 
2123 	treeview__search_cancel(tree, true);
2124 
2125 	return NSERROR_OK;
2126 }
2127 
2128 
2129 /* Exported interface, documented in treeview.h */
treeview_destroy(treeview * tree)2130 nserror treeview_destroy(treeview *tree)
2131 {
2132 	int f;
2133 
2134 	assert(tree != NULL);
2135 
2136 	if (tree->search.textarea != NULL) {
2137 		tree->search.active = false;
2138 		tree->search.search = false;
2139 		textarea_destroy(tree->search.textarea);
2140 	}
2141 
2142 	/* Destroy nodes */
2143 	treeview_delete_node_internal(tree, tree->root, false,
2144 				      TREE_OPTION_SUPPRESS_RESIZE |
2145 				      TREE_OPTION_SUPPRESS_REDRAW);
2146 
2147 	/* Destroy feilds */
2148 	for (f = 0; f <= tree->n_fields; f++) {
2149 		lwc_string_unref(tree->fields[f].field);
2150 	}
2151 	free(tree->fields);
2152 
2153 	/* Free treeview */
2154 	free(tree);
2155 
2156 	return NSERROR_OK;
2157 }
2158 
2159 
2160 /**
2161  * Expand a treeview's nodes
2162  *
2163  * \param tree Treeview object to expand nodes in
2164  * \param node The node to expand.
2165  * \return NSERROR_OK on success, appropriate error otherwise.
2166  */
2167 static nserror
treeview_node_expand_internal(treeview * tree,treeview_node * node)2168 treeview_node_expand_internal(treeview *tree, treeview_node *node)
2169 {
2170 	treeview_node *child;
2171 	struct treeview_node_entry *e;
2172 	int additional_height_folders = 0;
2173 	int additional_height_entries = 0;
2174 	int i;
2175 
2176 	assert(tree != NULL);
2177 	assert(node != NULL);
2178 
2179 	if (node->flags & TV_NFLAGS_EXPANDED) {
2180 		/* What madness is this? */
2181 		NSLOG(netsurf, INFO, "Tried to expand an expanded node.");
2182 		return NSERROR_OK;
2183 	}
2184 
2185 	switch (node->type) {
2186 	case TREE_NODE_FOLDER:
2187 		child = node->children;
2188 		if (child == NULL) {
2189 			/* Allow expansion of empty folders */
2190 			break;
2191 		}
2192 
2193 		do {
2194 			if (child->text.width == 0) {
2195 				guit->layout->width(&plot_style_odd.text,
2196 						    child->text.data,
2197 						    child->text.len,
2198 						    &(child->text.width));
2199 			}
2200 
2201 			additional_height_folders += child->height;
2202 
2203 			child = child->next_sib;
2204 		} while (child != NULL);
2205 
2206 		break;
2207 
2208 	case TREE_NODE_ENTRY:
2209 		assert(node->children == NULL);
2210 
2211 		e = (struct treeview_node_entry *)node;
2212 
2213 		for (i = 0; i < tree->n_fields - 1; i++) {
2214 
2215 			if (e->fields[i].value.width == 0) {
2216 				guit->layout->width(&plot_style_odd.text,
2217 						    e->fields[i].value.data,
2218 						    e->fields[i].value.len,
2219 						    &(e->fields[i].value.width));
2220 			}
2221 
2222 			/* Add height for field */
2223 			additional_height_entries += tree_g.line_height;
2224 		}
2225 
2226 		break;
2227 
2228 	case TREE_NODE_ROOT:
2229 	case TREE_NODE_NONE:
2230 		assert(node->type != TREE_NODE_ROOT);
2231 		assert(node->type != TREE_NODE_NONE);
2232 		break;
2233 	}
2234 
2235 	/* Update the node */
2236 	node->flags |= TV_NFLAGS_EXPANDED;
2237 
2238 	/* And node heights */
2239 	for (struct treeview_node *n = node;
2240 			(n != NULL) && (n->flags & TV_NFLAGS_EXPANDED);
2241 			n = n->parent) {
2242 		n->height += additional_height_entries +
2243 				additional_height_folders;
2244 	}
2245 
2246 	if (tree->search.search &&
2247 			node->type == TREE_NODE_ENTRY &&
2248 			node->flags & TV_NFLAGS_MATCHED) {
2249 		tree->search.height += additional_height_entries;
2250 	}
2251 
2252 	/* Inform front end of change in dimensions */
2253 	if (additional_height_entries + additional_height_folders != 0) {
2254 		treeview__cw_update_size(tree, -1,
2255 				treeview__get_display_height(tree));
2256 	}
2257 
2258 	return NSERROR_OK;
2259 }
2260 
2261 
2262 /* Exported interface, documented in treeview.h */
treeview_node_expand(treeview * tree,treeview_node * node)2263 nserror treeview_node_expand(treeview *tree, treeview_node *node)
2264 {
2265 	nserror res;
2266 
2267 	res = treeview_node_expand_internal(tree, node);
2268 	NSLOG(netsurf, INFO, "Expanding!");
2269 	if (res == NSERROR_OK) {
2270 		/* expansion was successful, attempt redraw */
2271 		treeview__redraw_from_node(tree, node);
2272 		NSLOG(netsurf, INFO, "Expanded!");
2273 	}
2274 
2275 	return res;
2276 }
2277 
2278 
2279 /**
2280  * context for treeview contraction callback
2281  */
2282 struct treeview_contract_data {
2283 	treeview *tree;
2284 	bool only_entries;
2285 };
2286 
2287 
2288 /**
2289  * Treewalk node callback for handling node contraction.
2290  *
2291  * \param n node
2292  * \param ctx contract iterator context
2293  * \param end flag to end iteration now
2294  * \return NSERROR_OK on success else appropriate error code
2295  */
treeview_node_contract_cb(treeview_node * n,void * ctx,bool * end)2296 static nserror treeview_node_contract_cb(treeview_node *n, void *ctx, bool *end)
2297 {
2298 	struct treeview_contract_data *data = ctx;
2299 	int h_reduction_folder = 0;
2300 	int h_reduction_entry = 0;
2301 
2302 	assert(n != NULL);
2303 	assert(n->type != TREE_NODE_ROOT);
2304 
2305 	n->flags &= ~TV_NFLAGS_SELECTED;
2306 
2307 	if ((n->flags & TV_NFLAGS_EXPANDED) == false ||
2308 	    (n->type == TREE_NODE_FOLDER && data->only_entries)) {
2309 		/* Nothing to do. */
2310 		return NSERROR_OK;
2311 	}
2312 
2313 
2314 	switch (n->type) {
2315 	case TREE_NODE_FOLDER:
2316 		h_reduction_folder = n->height - tree_g.line_height;
2317 		break;
2318 
2319 	case TREE_NODE_ENTRY:
2320 		h_reduction_entry = n->height - tree_g.line_height;
2321 		break;
2322 
2323 	default:
2324 		break;
2325 	}
2326 
2327 
2328 	assert(h_reduction_folder + h_reduction_entry >= 0);
2329 	for (struct treeview_node *node = n;
2330 			(node != NULL) && (node->flags & TV_NFLAGS_EXPANDED);
2331 			node = node->parent) {
2332 		node->height -= h_reduction_folder + h_reduction_entry;
2333 	}
2334 
2335 	if (data->tree->search.search) {
2336 		data->tree->search.height -= h_reduction_entry;
2337 	}
2338 
2339 	n->flags ^= TV_NFLAGS_EXPANDED;
2340 
2341 	return NSERROR_OK;
2342 }
2343 
2344 
2345 /**
2346  * Contract a treeview node
2347  *
2348  * \param tree Treeview object to contract node in
2349  * \param node Node to contract
2350  * \return NSERROR_OK on success, appropriate error otherwise
2351  */
2352 static nserror
treeview_node_contract_internal(treeview * tree,treeview_node * node)2353 treeview_node_contract_internal(treeview *tree, treeview_node *node)
2354 {
2355 	struct treeview_contract_data data;
2356 	bool selected;
2357 	assert(node != NULL);
2358 
2359 	if ((node->flags & TV_NFLAGS_EXPANDED) == false) {
2360 		/* What madness is this? */
2361 		NSLOG(netsurf, INFO, "Tried to contract a contracted node.");
2362 		return NSERROR_OK;
2363 	}
2364 
2365 	data.tree = tree;
2366 	data.only_entries = false;
2367 	selected = node->flags & TV_NFLAGS_SELECTED;
2368 
2369 	/* Contract children. */
2370 	treeview_walk_internal(tree, node, TREEVIEW_WALK_MODE_LOGICAL_EXPANDED,
2371 			treeview_node_contract_cb, NULL, &data);
2372 
2373 	/* Contract node */
2374 	treeview_node_contract_cb(node, &data, false);
2375 
2376 	if (selected)
2377 		node->flags |= TV_NFLAGS_SELECTED;
2378 
2379 	/* Inform front end of change in dimensions */
2380 	treeview__cw_update_size(tree, -1, treeview__get_display_height(tree));
2381 
2382 	return NSERROR_OK;
2383 }
2384 
2385 
2386 /* Exported interface, documented in treeview.h */
treeview_node_contract(treeview * tree,treeview_node * node)2387 nserror treeview_node_contract(treeview *tree, treeview_node *node)
2388 {
2389 	nserror res;
2390 
2391 	assert(tree != NULL);
2392 
2393 	res = treeview_node_contract_internal(tree, node);
2394 	NSLOG(netsurf, INFO, "Contracting!");
2395 	if (res == NSERROR_OK) {
2396 		/* successful contraction, request redraw */
2397 		treeview__redraw_from_node(tree, node);
2398 		NSLOG(netsurf, INFO, "Contracted!");
2399 	}
2400 
2401 	return res;
2402 }
2403 
2404 
2405 /* Exported interface, documented in treeview.h */
treeview_contract(treeview * tree,bool all)2406 nserror treeview_contract(treeview *tree, bool all)
2407 {
2408 	int search_height = treeview__get_search_height(tree);
2409 	struct treeview_contract_data data;
2410 	bool selected;
2411 	treeview_node *n;
2412 	struct rect r;
2413 
2414 	assert(tree != NULL);
2415 	assert(tree->root != NULL);
2416 
2417 	r.x0 = 0;
2418 	r.y0 = 0;
2419 	r.x1 = REDRAW_MAX;
2420 	r.y1 = tree->root->height + search_height;
2421 
2422 	data.tree = tree;
2423 	data.only_entries = !all;
2424 
2425 	for (n = tree->root->children; n != NULL; n = n->next_sib) {
2426 		if ((n->flags & TV_NFLAGS_EXPANDED) == false) {
2427 			continue;
2428 		}
2429 
2430 		selected = n->flags & TV_NFLAGS_SELECTED;
2431 
2432 		/* Contract children. */
2433 		treeview_walk_internal(tree, n,
2434 				TREEVIEW_WALK_MODE_LOGICAL_EXPANDED,
2435 				treeview_node_contract_cb, NULL, &data);
2436 
2437 		/* Contract node */
2438 		treeview_node_contract_cb(n, &data, false);
2439 
2440 		if (selected)
2441 			n->flags |= TV_NFLAGS_SELECTED;
2442 	}
2443 
2444 	/* Inform front end of change in dimensions */
2445 	treeview__cw_update_size(tree, -1, tree->root->height);
2446 
2447 	/* Redraw */
2448 	treeview__cw_invalidate_area(tree, &r);
2449 
2450 	return NSERROR_OK;
2451 }
2452 
2453 
2454 /**
2455  * context data for treeview expansion
2456  */
2457 struct treeview_expand_data {
2458 	treeview *tree;
2459 	bool only_folders;
2460 };
2461 
2462 
2463 /**
2464  * Treewalk node callback for handling recursive node expansion.
2465  *
2466  * \param n current node
2467  * \param ctx node expansion context
2468  * \param skip_children flag to allow children to be skipped
2469  * \param end flag to allow iteration to be finished early.
2470  * \return NSERROR_OK on success else error code.
2471  */
2472 static nserror
treeview_expand_cb(treeview_node * n,void * ctx,bool * skip_children,bool * end)2473 treeview_expand_cb(treeview_node *n,
2474 		   void *ctx,
2475 		   bool *skip_children,
2476 		   bool *end)
2477 {
2478 	struct treeview_expand_data *data = ctx;
2479 	nserror err;
2480 
2481 	assert(n != NULL);
2482 	assert(n->type != TREE_NODE_ROOT);
2483 
2484 	if (n->flags & TV_NFLAGS_EXPANDED ||
2485 	    (data->only_folders && n->type != TREE_NODE_FOLDER)) {
2486 		/* Nothing to do. */
2487 		return NSERROR_OK;
2488 	}
2489 
2490 	err = treeview_node_expand_internal(data->tree, n);
2491 
2492 	return err;
2493 }
2494 
2495 
2496 /* Exported interface, documented in treeview.h */
treeview_expand(treeview * tree,bool only_folders)2497 nserror treeview_expand(treeview *tree, bool only_folders)
2498 {
2499 	struct treeview_expand_data data;
2500 	nserror res;
2501 	struct rect r;
2502 
2503 	assert(tree != NULL);
2504 	assert(tree->root != NULL);
2505 
2506 	data.tree = tree;
2507 	data.only_folders = only_folders;
2508 
2509 	res = treeview_walk_internal(tree, tree->root,
2510 			TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
2511 			NULL, treeview_expand_cb, &data);
2512 	if (res == NSERROR_OK) {
2513 		/* expansion succeeded, schedule redraw */
2514 
2515 		r.x0 = 0;
2516 		r.y0 = 0;
2517 		r.x1 = REDRAW_MAX;
2518 		r.y1 = tree->root->height;
2519 
2520 		treeview__cw_invalidate_area(tree, &r);
2521 	}
2522 	return res;
2523 }
2524 
2525 
2526 /**
2527  * Draw a treeview normally, in tree mode.
2528  *
2529  * \param[in]     tree      The treeview we're rendering.
2530  * \param[in]     x         X coordinate we're rendering the treeview at.
2531  * \param[in]     y         Y coordinate we're rendering the treeview at.
2532  * \param[in,out] render_y  Current vertical position in tree, updated on exit.
2533  * \param[in]     r         Clip rectangle.
2534  * \param[in]     data      Redraw data for rendering contents.
2535  * \param[in]     ctx       Current render context.
2536  */
treeview_redraw_tree(treeview * tree,const int x,const int y,int * render_y_in_out,const struct rect * r,struct content_redraw_data * data,const struct redraw_context * ctx)2537 static void treeview_redraw_tree(
2538 		treeview *tree,
2539 		const int x,
2540 		const int y,
2541 		int *render_y_in_out,
2542 		const struct rect *r,
2543 		struct content_redraw_data *data,
2544 		const struct redraw_context *ctx)
2545 {
2546 	struct treeview_node_style *style = &plot_style_odd;
2547 	enum treeview_resource_id res = TREE_RES_CONTENT;
2548 	int baseline = (tree_g.line_height * 3 + 2) / 4;
2549 	plot_font_style_t *infotext_style;
2550 	treeview_node *root = tree->root;
2551 	treeview_node *node = tree->root;
2552 	int render_y = *render_y_in_out;
2553 	plot_font_style_t *text_style;
2554 	plot_style_t *bg_style;
2555 	int sel_min, sel_max;
2556 	uint32_t count = 0;
2557 	struct rect rect;
2558 	int inset;
2559 	int x0;
2560 
2561 	if (tree->drag.start.y > tree->drag.prev.y) {
2562 		sel_min = tree->drag.prev.y;
2563 		sel_max = tree->drag.start.y;
2564 	} else {
2565 		sel_min = tree->drag.start.y;
2566 		sel_max = tree->drag.prev.y;
2567 	}
2568 
2569 	while (node != NULL) {
2570 		struct treeview_node_entry *entry;
2571 		struct bitmap *furniture;
2572 		bool invert_selection;
2573 		treeview_node *next;
2574 		int height;
2575 		int i;
2576 
2577 		next = (node->flags & TV_NFLAGS_EXPANDED) ?
2578 			node->children : NULL;
2579 
2580 		if (next != NULL) {
2581 			/* down to children */
2582 			node = next;
2583 		} else {
2584 			/* No children.  As long as we're not at the root,
2585 			 * go to next sibling if present, or nearest ancestor
2586 			 * with a next sibling. */
2587 
2588 			while (node != root &&
2589 			       node->next_sib == NULL) {
2590 				node = node->parent;
2591 			}
2592 
2593 			if (node == root)
2594 				break;
2595 
2596 			node = node->next_sib;
2597 		}
2598 
2599 		assert(node != NULL);
2600 		assert(node != root);
2601 		assert(node->type == TREE_NODE_FOLDER ||
2602 		       node->type == TREE_NODE_ENTRY);
2603 
2604 		count++;
2605 		inset = x + node->inset;
2606 		height = (node->type == TREE_NODE_ENTRY) ? node->height :
2607 			tree_g.line_height;
2608 
2609 		if ((render_y + height) < r->y0) {
2610 			/* This node's line is above clip region */
2611 			render_y += height;
2612 			continue;
2613 		}
2614 
2615 		style = (count & 0x1) ? &plot_style_odd : &plot_style_even;
2616 		if (tree->drag.type == TV_DRAG_SELECTION &&
2617 		    (render_y + height >= sel_min &&
2618 		     render_y < sel_max)) {
2619 			invert_selection = true;
2620 		} else {
2621 			invert_selection = false;
2622 		}
2623 		if ((node->flags & TV_NFLAGS_SELECTED && !invert_selection) ||
2624 		    (!(node->flags & TV_NFLAGS_SELECTED) &&
2625 		     invert_selection)) {
2626 			bg_style = &style->sbg;
2627 			text_style = &style->stext;
2628 			infotext_style = &style->sitext;
2629 			furniture = (node->flags & TV_NFLAGS_EXPANDED) ?
2630 				style->furn[TREE_FURN_CONTRACT].sel :
2631 				style->furn[TREE_FURN_EXPAND].sel;
2632 		} else {
2633 			bg_style = &style->bg;
2634 			text_style = &style->text;
2635 			infotext_style = &style->itext;
2636 			furniture = (node->flags & TV_NFLAGS_EXPANDED) ?
2637 				style->furn[TREE_FURN_CONTRACT].bmp :
2638 				style->furn[TREE_FURN_EXPAND].bmp;
2639 		}
2640 
2641 		/* Render background */
2642 		rect.x0 = r->x0;
2643 		rect.y0 = render_y;
2644 		rect.x1 = r->x1;
2645 		rect.y1 = render_y + height;
2646 		ctx->plot->rectangle(ctx, bg_style, &rect);
2647 
2648 		/* Render toggle */
2649 		ctx->plot->bitmap(ctx,
2650 				furniture,
2651 				inset,
2652 				render_y + tree_g.line_height / 4,
2653 				style->furn[TREE_FURN_EXPAND].size,
2654 				style->furn[TREE_FURN_EXPAND].size,
2655 				bg_style->fill_colour,
2656 				BITMAPF_NONE);
2657 
2658 		/* Render icon */
2659 		if (node->type == TREE_NODE_ENTRY) {
2660 			res = TREE_RES_CONTENT;
2661 		} else if (node->flags & TV_NFLAGS_SPECIAL) {
2662 			res = TREE_RES_FOLDER_SPECIAL;
2663 		} else {
2664 			res = TREE_RES_FOLDER;
2665 		}
2666 
2667 		if (treeview_res[res].ready) {
2668 			/* Icon resource is available */
2669 			data->x = inset + tree_g.step_width;
2670 			data->y = render_y + ((tree_g.line_height -
2671 					      treeview_res[res].height + 1) / 2);
2672 			data->background_colour = bg_style->fill_colour;
2673 
2674 			content_redraw(treeview_res[res].c, data, r, ctx);
2675 		}
2676 
2677 		/* Render text */
2678 		x0 = inset + tree_g.step_width + tree_g.icon_step;
2679 		ctx->plot->text(ctx,
2680 				text_style,
2681 				x0, render_y + baseline,
2682 				node->text.data,
2683 				node->text.len);
2684 
2685 		/* Rendered the node */
2686 		render_y += tree_g.line_height;
2687 		if (render_y > r->y1) {
2688 			/* Passed the bottom of what's in the clip region.
2689 			 * Done. */
2690 			break;
2691 		}
2692 
2693 
2694 		if (node->type != TREE_NODE_ENTRY ||
2695 		    !(node->flags & TV_NFLAGS_EXPANDED))
2696 			/* Done everything for this node */
2697 			continue;
2698 
2699 		/* Render expanded entry fields */
2700 		entry = (struct treeview_node_entry *)node;
2701 		for (i = 0; i < tree->n_fields - 1; i++) {
2702 			struct treeview_field *ef = &(tree->fields[i + 1]);
2703 
2704 			if (ef->flags & TREE_FLAG_SHOW_NAME) {
2705 				int max_width = tree->field_width;
2706 
2707 				ctx->plot->text(ctx,
2708 						infotext_style,
2709 						x0 + max_width - ef->value.width - tree_g.step_width,
2710 						render_y + baseline,
2711 						ef->value.data,
2712 						ef->value.len);
2713 
2714 				ctx->plot->text(ctx,
2715 						infotext_style,
2716 						x0 + max_width,
2717 						render_y + baseline,
2718 						entry->fields[i].value.data,
2719 						entry->fields[i].value.len);
2720 			} else {
2721 				ctx->plot->text(ctx,
2722 						infotext_style,
2723 						x0, render_y + baseline,
2724 						entry->fields[i].value.data,
2725 						entry->fields[i].value.len);
2726 			}
2727 
2728 			/* Rendered the expanded entry field */
2729 			render_y += tree_g.line_height;
2730 		}
2731 
2732 		/* Finished rendering expanded entry */
2733 
2734 		if (render_y > r->y1) {
2735 			/* Passed the bottom of what's in the clip region.
2736 			 * Done. */
2737 			break;
2738 		}
2739 	}
2740 
2741 	*render_y_in_out = render_y;
2742 }
2743 
2744 
2745 /**
2746  * Draw a treeview normally, in tree mode.
2747  *
2748  * \param[in]     tree      The treeview we're rendering.
2749  * \param[in]     x         X coordinate we're rendering the treeview at.
2750  * \param[in]     y         Y coordinate we're rendering the treeview at.
2751  * \param[in,out] render_y  Current vertical position in tree, updated on exit.
2752  * \param[in]     r         Clip rectangle.
2753  * \param[in]     data      Redraw data for rendering contents.
2754  * \param[in]     ctx       Current render context.
2755  */
treeview_redraw_search(treeview * tree,const int x,const int y,int * render_y_in_out,const struct rect * r,struct content_redraw_data * data,const struct redraw_context * ctx)2756 static void treeview_redraw_search(
2757 		treeview *tree,
2758 		const int x,
2759 		const int y,
2760 		int *render_y_in_out,
2761 		const struct rect *r,
2762 		struct content_redraw_data *data,
2763 		const struct redraw_context *ctx)
2764 {
2765 	struct treeview_node_style *style = &plot_style_odd;
2766 	enum treeview_resource_id res = TREE_RES_CONTENT;
2767 	int baseline = (tree_g.line_height * 3 + 2) / 4;
2768 	plot_font_style_t *infotext_style;
2769 	treeview_node *root = tree->root;
2770 	treeview_node *node = tree->root;
2771 	int render_y = *render_y_in_out;
2772 	plot_font_style_t *text_style;
2773 	plot_style_t *bg_style;
2774 	int sel_min, sel_max;
2775 	uint32_t count = 0;
2776 	struct rect rect;
2777 	int inset;
2778 	int x0;
2779 
2780 	if (tree->drag.start.y > tree->drag.prev.y) {
2781 		sel_min = tree->drag.prev.y;
2782 		sel_max = tree->drag.start.y;
2783 	} else {
2784 		sel_min = tree->drag.start.y;
2785 		sel_max = tree->drag.prev.y;
2786 	}
2787 
2788 	while (node != NULL) {
2789 		struct treeview_node_entry *entry;
2790 		struct bitmap *furniture;
2791 		bool invert_selection;
2792 		treeview_node *next;
2793 		int height;
2794 		int i;
2795 
2796 		next = node->children;
2797 
2798 		if (next != NULL) {
2799 			/* down to children */
2800 			node = next;
2801 		} else {
2802 			/* No children.  As long as we're not at the root,
2803 			 * go to next sibling if present, or nearest ancestor
2804 			 * with a next sibling. */
2805 
2806 			while (node != root &&
2807 			       node->next_sib == NULL) {
2808 				node = node->parent;
2809 			}
2810 
2811 			if (node == root)
2812 				break;
2813 
2814 			node = node->next_sib;
2815 		}
2816 
2817 		assert(node != NULL);
2818 		assert(node != root);
2819 		assert(node->type == TREE_NODE_FOLDER ||
2820 		       node->type == TREE_NODE_ENTRY);
2821 
2822 		if (node->type == TREE_NODE_FOLDER ||
2823 				!(node->flags & TV_NFLAGS_MATCHED)) {
2824 			continue;
2825 		}
2826 
2827 		count++;
2828 		inset = x + tree_g.window_padding;
2829 		height = node->height;
2830 
2831 		if ((render_y + height) < r->y0) {
2832 			/* This node's line is above clip region */
2833 			render_y += height;
2834 			continue;
2835 		}
2836 
2837 		style = (count & 0x1) ? &plot_style_odd : &plot_style_even;
2838 		if (tree->drag.type == TV_DRAG_SELECTION &&
2839 		    (render_y + height >= sel_min &&
2840 		     render_y < sel_max)) {
2841 			invert_selection = true;
2842 		} else {
2843 			invert_selection = false;
2844 		}
2845 		if ((node->flags & TV_NFLAGS_SELECTED && !invert_selection) ||
2846 		    (!(node->flags & TV_NFLAGS_SELECTED) &&
2847 		     invert_selection)) {
2848 			bg_style = &style->sbg;
2849 			text_style = &style->stext;
2850 			infotext_style = &style->sitext;
2851 			furniture = (node->flags & TV_NFLAGS_EXPANDED) ?
2852 				style->furn[TREE_FURN_CONTRACT].sel :
2853 				style->furn[TREE_FURN_EXPAND].sel;
2854 		} else {
2855 			bg_style = &style->bg;
2856 			text_style = &style->text;
2857 			infotext_style = &style->itext;
2858 			furniture = (node->flags & TV_NFLAGS_EXPANDED) ?
2859 				style->furn[TREE_FURN_CONTRACT].bmp :
2860 				style->furn[TREE_FURN_EXPAND].bmp;
2861 		}
2862 
2863 		/* Render background */
2864 		rect.x0 = r->x0;
2865 		rect.y0 = render_y;
2866 		rect.x1 = r->x1;
2867 		rect.y1 = render_y + height;
2868 		ctx->plot->rectangle(ctx, bg_style, &rect);
2869 
2870 		/* Render toggle */
2871 		ctx->plot->bitmap(ctx,
2872 				furniture,
2873 				inset,
2874 				render_y + tree_g.line_height / 4,
2875 				style->furn[TREE_FURN_EXPAND].size,
2876 				style->furn[TREE_FURN_EXPAND].size,
2877 				bg_style->fill_colour,
2878 				BITMAPF_NONE);
2879 
2880 		/* Render icon */
2881 		if (node->type == TREE_NODE_ENTRY) {
2882 			res = TREE_RES_CONTENT;
2883 		} else if (node->flags & TV_NFLAGS_SPECIAL) {
2884 			res = TREE_RES_FOLDER_SPECIAL;
2885 		} else {
2886 			res = TREE_RES_FOLDER;
2887 		}
2888 
2889 		if (treeview_res[res].ready) {
2890 			/* Icon resource is available */
2891 			data->x = inset + tree_g.step_width;
2892 			data->y = render_y + ((tree_g.line_height -
2893 					      treeview_res[res].height + 1) / 2);
2894 			data->background_colour = bg_style->fill_colour;
2895 
2896 			content_redraw(treeview_res[res].c, data, r, ctx);
2897 		}
2898 
2899 		/* Render text */
2900 		x0 = inset + tree_g.step_width + tree_g.icon_step;
2901 		ctx->plot->text(ctx,
2902 				text_style,
2903 				x0, render_y + baseline,
2904 				node->text.data,
2905 				node->text.len);
2906 
2907 		/* Rendered the node */
2908 		render_y += tree_g.line_height;
2909 		if (render_y > r->y1) {
2910 			/* Passed the bottom of what's in the clip region.
2911 			 * Done. */
2912 			break;
2913 		}
2914 
2915 
2916 		if (node->type != TREE_NODE_ENTRY ||
2917 		    !(node->flags & TV_NFLAGS_EXPANDED))
2918 			/* Done everything for this node */
2919 			continue;
2920 
2921 		/* Render expanded entry fields */
2922 		entry = (struct treeview_node_entry *)node;
2923 		for (i = 0; i < tree->n_fields - 1; i++) {
2924 			struct treeview_field *ef = &(tree->fields[i + 1]);
2925 
2926 			if (ef->flags & TREE_FLAG_SHOW_NAME) {
2927 				int max_width = tree->field_width;
2928 
2929 				ctx->plot->text(ctx,
2930 						infotext_style,
2931 						x0 + max_width - ef->value.width - tree_g.step_width,
2932 						render_y + baseline,
2933 						ef->value.data,
2934 						ef->value.len);
2935 
2936 				ctx->plot->text(ctx,
2937 						infotext_style,
2938 						x0 + max_width,
2939 						render_y + baseline,
2940 						entry->fields[i].value.data,
2941 						entry->fields[i].value.len);
2942 			} else {
2943 				ctx->plot->text(ctx,
2944 						infotext_style,
2945 						x0, render_y + baseline,
2946 						entry->fields[i].value.data,
2947 						entry->fields[i].value.len);
2948 			}
2949 
2950 			/* Rendered the expanded entry field */
2951 			render_y += tree_g.line_height;
2952 		}
2953 
2954 		/* Finished rendering expanded entry */
2955 
2956 		if (render_y > r->y1) {
2957 			/* Passed the bottom of what's in the clip region.
2958 			 * Done. */
2959 			break;
2960 		}
2961 	}
2962 
2963 	*render_y_in_out = render_y;
2964 }
2965 
2966 
2967 /* Exported interface, documented in treeview.h */
2968 void
treeview_redraw(treeview * tree,const int x,const int y,struct rect * clip,const struct redraw_context * ctx)2969 treeview_redraw(treeview *tree,
2970 		const int x,
2971 		const int y,
2972 		struct rect *clip,
2973 		const struct redraw_context *ctx)
2974 {
2975 	struct redraw_context new_ctx = *ctx;
2976 	struct content_redraw_data data;
2977 	struct rect r;
2978 	struct rect rect;
2979 	int render_y = y;
2980 
2981 	assert(tree != NULL);
2982 	assert(tree->root != NULL);
2983 	assert(tree->root->flags & TV_NFLAGS_EXPANDED);
2984 
2985 	/* Start knockout rendering if it's available for this plotter */
2986 	if (ctx->plot->option_knockout) {
2987 		knockout_plot_start(ctx, &new_ctx);
2988 	}
2989 
2990 	/* Set up clip rectangle */
2991 	r.x0 = clip->x0 + x;
2992 	r.y0 = clip->y0 + y;
2993 	r.x1 = clip->x1 + x;
2994 	r.y1 = clip->y1 + y;
2995 	new_ctx.plot->clip(&new_ctx, &r);
2996 
2997 	/* Setup common content redraw data */
2998 	data.width = tree_g.icon_size;
2999 	data.height = tree_g.icon_size;
3000 	data.scale = 1;
3001 	data.repeat_x = false;
3002 	data.repeat_y = false;
3003 
3004 	if (tree->flags & TREEVIEW_SEARCHABLE) {
3005 		if (render_y < r.y1) {
3006 			enum treeview_resource_id icon = TREE_RES_SEARCH;
3007 
3008 			/* Fill the blank area at the bottom */
3009 			rect.x0 = r.x0;
3010 			rect.y0 = render_y;
3011 			rect.x1 = r.x1;
3012 			rect.y1 = render_y + tree_g.line_height;
3013 			new_ctx.plot->rectangle(&new_ctx, &plot_style_even.bg,
3014 					&rect);
3015 
3016 			if (treeview_res[icon].ready) {
3017 				/* Icon resource is available */
3018 				data.x = tree_g.window_padding;
3019 				data.y = render_y + ((tree_g.line_height -
3020 						treeview_res[icon].height + 1) /
3021 						2);
3022 				data.background_colour = plot_style_even.bg.
3023 						fill_colour;
3024 
3025 				content_redraw(treeview_res[icon].c,
3026 						&data, &r, &new_ctx);
3027 			}
3028 
3029 			textarea_redraw(tree->search.textarea,
3030 					x + tree_g.window_padding +
3031 							tree_g.icon_step, y,
3032 					plot_style_even.bg.fill_colour, 1.0,
3033 					&r, &new_ctx);
3034 		}
3035 		render_y += tree_g.line_height;
3036 	}
3037 
3038 	/* Render the treeview data */
3039 	if (tree->search.search == true) {
3040 		treeview_redraw_search(tree, x, y,
3041 				&render_y, &r, &data, &new_ctx);
3042 	} else {
3043 		treeview_redraw_tree(tree, x, y,
3044 				&render_y, &r, &data, &new_ctx);
3045 	}
3046 
3047 	if (render_y < r.y1) {
3048 		/* Fill the blank area at the bottom */
3049 		rect.x0 = r.x0;
3050 		rect.y0 = render_y;
3051 		rect.x1 = r.x1;
3052 		rect.y1 = r.y1;
3053 		new_ctx.plot->rectangle(&new_ctx, &plot_style_even.bg, &rect);
3054 	}
3055 
3056 	/* All normal treeview rendering is done; render any overlays */
3057 	if ((tree->move.target_pos != TV_TARGET_NONE) &&
3058 	    (treeview_res[TREE_RES_ARROW].ready)) {
3059 		/* Got a MOVE drag; render move indicator arrow */
3060 		data.x = tree->move.target_area.x0 + x;
3061 		data.y = tree->move.target_area.y0 + y;
3062 		data.background_colour = plot_style_even.bg.fill_colour;
3063 
3064 		content_redraw(treeview_res[TREE_RES_ARROW].c, &data, &r, &new_ctx);
3065 
3066 	} else if (tree->edit.textarea != NULL) {
3067 		/* Edit in progress; render textarea */
3068 		textarea_redraw(tree->edit.textarea,
3069 				tree->edit.x + x, tree->edit.y + y,
3070 				plot_style_even.bg.fill_colour, 1.0,
3071 				&r, &new_ctx);
3072 	}
3073 
3074 	/* Rendering complete */
3075 	if (ctx->plot->option_knockout) {
3076 		knockout_plot_end(ctx);
3077 	}
3078 }
3079 
3080 
3081 /**
3082  * context for treeview selection
3083  */
3084 struct treeview_selection_walk_data {
3085 	enum {
3086 		TREEVIEW_WALK_HAS_SELECTION,
3087 		TREEVIEW_WALK_GET_FIRST_SELECTED,
3088 		TREEVIEW_WALK_CLEAR_SELECTION,
3089 		TREEVIEW_WALK_SELECT_ALL,
3090 		TREEVIEW_WALK_COMMIT_SELECT_DRAG,
3091 		TREEVIEW_WALK_DELETE_SELECTION,
3092 		TREEVIEW_WALK_PROPAGATE_SELECTION,
3093 		TREEVIEW_WALK_YANK_SELECTION,
3094 		TREEVIEW_WALK_COPY_SELECTION
3095 	} purpose;
3096 	union {
3097 		bool has_selection;
3098 		struct {
3099 			bool required;
3100 			struct rect *rect;
3101 		} redraw;
3102 		struct {
3103 			int sel_min;
3104 			int sel_max;
3105 		} drag;
3106 		struct {
3107 			treeview_node *prev;
3108 			treeview_node *fixed;
3109 		} yank;
3110 		struct {
3111 			treeview_node *n;
3112 		} first;
3113 		struct {
3114 			char *text;
3115 			uint32_t len;
3116 		} copy;
3117 	} data;
3118 	int current_y;
3119 	treeview *tree;
3120 };
3121 
3122 
3123 /**
3124  * Treewalk node callback for handling selection related actions.
3125  *
3126  * \param n current node
3127  * \param ctx node selection context
3128  * \param skip_children flag to allow children to be skipped
3129  * \param end flag to allow iteration to be finished early.
3130  * \return NSERROR_OK on success else error code.
3131  */
3132 static nserror
treeview_node_selection_walk_cb(treeview_node * n,void * ctx,bool * skip_children,bool * end)3133 treeview_node_selection_walk_cb(treeview_node *n,
3134 				void *ctx,
3135 				bool *skip_children,
3136 				bool *end)
3137 {
3138 	struct treeview_selection_walk_data *sw = ctx;
3139 	int height;
3140 	bool changed = false;
3141 	nserror err;
3142 
3143 	height = (n->type == TREE_NODE_ENTRY) ? n->height : tree_g.line_height;
3144 	sw->current_y += height;
3145 
3146 	switch (sw->purpose) {
3147 	case TREEVIEW_WALK_HAS_SELECTION:
3148 		if (n->flags & TV_NFLAGS_SELECTED) {
3149 			sw->data.has_selection = true;
3150 			*end = true; /* Can abort tree walk */
3151 			return NSERROR_OK;
3152 		}
3153 		break;
3154 
3155 	case TREEVIEW_WALK_GET_FIRST_SELECTED:
3156 		if (n->flags & TV_NFLAGS_SELECTED) {
3157 			sw->data.first.n = n;
3158 			*end = true; /* Can abort tree walk */
3159 			return NSERROR_OK;
3160 		}
3161 		break;
3162 
3163 	case TREEVIEW_WALK_DELETE_SELECTION:
3164 		if (n->flags & TV_NFLAGS_SELECTED) {
3165 			err = treeview_delete_node_internal(sw->tree, n, true,
3166 							    TREE_OPTION_NONE);
3167 			if (err != NSERROR_OK) {
3168 				return err;
3169 			}
3170 			*skip_children = true;
3171 			changed = true;
3172 		}
3173 		break;
3174 
3175 	case TREEVIEW_WALK_PROPAGATE_SELECTION:
3176 		if (n->parent != NULL &&
3177 		    n->parent->flags & TV_NFLAGS_SELECTED &&
3178 		    !(n->flags & TV_NFLAGS_SELECTED)) {
3179 			n->flags ^= TV_NFLAGS_SELECTED;
3180 			changed = true;
3181 		}
3182 		break;
3183 
3184 	case TREEVIEW_WALK_CLEAR_SELECTION:
3185 		if (n->flags & TV_NFLAGS_SELECTED) {
3186 			n->flags ^= TV_NFLAGS_SELECTED;
3187 			changed = true;
3188 		}
3189 		break;
3190 
3191 	case TREEVIEW_WALK_SELECT_ALL:
3192 		if (!(n->flags & TV_NFLAGS_SELECTED)) {
3193 			n->flags ^= TV_NFLAGS_SELECTED;
3194 			changed = true;
3195 		}
3196 		break;
3197 
3198 	case TREEVIEW_WALK_COMMIT_SELECT_DRAG:
3199 		if (sw->current_y >= sw->data.drag.sel_min &&
3200 		    sw->current_y - height <
3201 		    sw->data.drag.sel_max) {
3202 			n->flags ^= TV_NFLAGS_SELECTED;
3203 		}
3204 		return NSERROR_OK;
3205 
3206 	case TREEVIEW_WALK_YANK_SELECTION:
3207 		if (n->flags & TV_NFLAGS_SELECTED) {
3208 			treeview_node *p = n->parent;
3209 			int h = 0;
3210 
3211 			if (n == sw->data.yank.fixed) {
3212 				break;
3213 			}
3214 
3215 			if (treeview_unlink_node(n))
3216 				h = n->height;
3217 
3218 			/* Reduce ancestor heights */
3219 			while (p != NULL && p->flags & TV_NFLAGS_EXPANDED) {
3220 				p->height -= h;
3221 				p = p->parent;
3222 			}
3223 			if (sw->data.yank.prev == NULL) {
3224 				sw->tree->move.root = n;
3225 				n->parent = NULL;
3226 				n->prev_sib = NULL;
3227 				n->next_sib = NULL;
3228 			} else {
3229 				n->parent = NULL;
3230 				n->prev_sib = sw->data.yank.prev;
3231 				n->next_sib = NULL;
3232 				sw->data.yank.prev->next_sib = n;
3233 			}
3234 			sw->data.yank.prev = n;
3235 
3236 			*skip_children = true;
3237 		}
3238 		break;
3239 
3240 	case TREEVIEW_WALK_COPY_SELECTION:
3241 		if (n->flags & TV_NFLAGS_SELECTED &&
3242 		    n->type == TREE_NODE_ENTRY) {
3243 			int i;
3244 			char *temp;
3245 			uint32_t len;
3246 			const char *text;
3247 			struct treeview_field *ef;
3248 			struct treeview_text *val;
3249 
3250 			for (i = 0; i < sw->tree->n_fields; i++) {
3251 				ef = &(sw->tree->fields[i]);
3252 
3253 				if (!(ef->flags & TREE_FLAG_COPY_TEXT)) {
3254 					continue;
3255 				}
3256 				val = treeview_get_text_for_field(sw->tree,
3257 								  n, i);
3258 				text = val->data;
3259 				len  = val->len;
3260 
3261 				temp = realloc(sw->data.copy.text,
3262 					       sw->data.copy.len + len + 1);
3263 				if (temp == NULL) {
3264 					free(sw->data.copy.text);
3265 					sw->data.copy.text = NULL;
3266 					sw->data.copy.len = 0;
3267 					return NSERROR_NOMEM;
3268 				}
3269 
3270 				if (sw->data.copy.len != 0) {
3271 					temp[sw->data.copy.len - 1] = '\n';
3272 				}
3273 				memcpy(temp + sw->data.copy.len, text, len);
3274 				temp[sw->data.copy.len + len] = '\0';
3275 				sw->data.copy.len += len + 1;
3276 				sw->data.copy.text = temp;
3277 			}
3278 		}
3279 		break;
3280 	}
3281 
3282 	if (changed) {
3283 		if (sw->data.redraw.required == false) {
3284 			sw->data.redraw.required = true;
3285 			sw->data.redraw.rect->y0 = sw->current_y - height;
3286 		}
3287 
3288 		if (sw->current_y > sw->data.redraw.rect->y1) {
3289 			sw->data.redraw.rect->y1 = sw->current_y;
3290 		}
3291 	}
3292 
3293 	return NSERROR_OK;
3294 }
3295 
3296 
3297 /* Exported interface, documented in treeview.h */
treeview_has_selection(treeview * tree)3298 bool treeview_has_selection(treeview *tree)
3299 {
3300 	struct treeview_selection_walk_data sw;
3301 
3302 	sw.purpose = TREEVIEW_WALK_HAS_SELECTION;
3303 	sw.data.has_selection = false;
3304 
3305 	treeview_walk_internal(tree, tree->root,
3306 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3307 			treeview_node_selection_walk_cb, &sw);
3308 
3309 	return sw.data.has_selection;
3310 }
3311 
3312 
3313 /**
3314  * Get first selected node (in any)
3315  *
3316  * \param tree Treeview object in which to create folder
3317  * \return the first selected treeview node, or NULL
3318  */
treeview_get_first_selected(treeview * tree)3319 static treeview_node * treeview_get_first_selected(treeview *tree)
3320 {
3321 	struct treeview_selection_walk_data sw;
3322 
3323 	sw.purpose = TREEVIEW_WALK_GET_FIRST_SELECTED;
3324 	sw.data.first.n = NULL;
3325 
3326 	treeview_walk_internal(tree, tree->root,
3327 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3328 			treeview_node_selection_walk_cb, &sw);
3329 
3330 	return sw.data.first.n;
3331 }
3332 
3333 
3334 /* Exported interface, documented in treeview.h */
treeview_get_selection(treeview * tree,void ** node_data)3335 enum treeview_node_type treeview_get_selection(treeview *tree,
3336 					       void **node_data)
3337 {
3338 	treeview_node *n;
3339 
3340 	assert(tree != NULL);
3341 
3342 	n = treeview_get_first_selected(tree);
3343 
3344 	if (n != NULL && n->type & (TREE_NODE_ENTRY | TREE_NODE_FOLDER)) {
3345 		*node_data = n->client_data;
3346 		return n->type;
3347 	}
3348 
3349 	*node_data = NULL;
3350 	return TREE_NODE_NONE;
3351 }
3352 
3353 
3354 /**
3355  * Clear any selection in a treeview
3356  *
3357  * \param tree Treeview object to clear selection in
3358  * \param rect Redraw rectangle (if redraw required)
3359  * \return true iff redraw required
3360  */
treeview_clear_selection(treeview * tree,struct rect * rect)3361 static bool treeview_clear_selection(treeview *tree, struct rect *rect)
3362 {
3363 	struct treeview_selection_walk_data sw;
3364 
3365 	rect->x0 = 0;
3366 	rect->y0 = 0;
3367 	rect->x1 = REDRAW_MAX;
3368 	rect->y1 = 0;
3369 
3370 	sw.purpose = TREEVIEW_WALK_CLEAR_SELECTION;
3371 	sw.data.redraw.required = false;
3372 	sw.data.redraw.rect = rect;
3373 	sw.current_y = treeview__get_search_height(tree);
3374 
3375 	treeview_walk_internal(tree, tree->root,
3376 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3377 			treeview_node_selection_walk_cb, &sw);
3378 
3379 	return sw.data.redraw.required;
3380 }
3381 
3382 
3383 /**
3384  * Select all in a treeview
3385  *
3386  * \param tree Treeview object to select all in
3387  * \param rect Redraw rectangle (if redraw required)
3388  * \return true iff redraw required
3389  */
treeview_select_all(treeview * tree,struct rect * rect)3390 static bool treeview_select_all(treeview *tree, struct rect *rect)
3391 {
3392 	struct treeview_selection_walk_data sw;
3393 
3394 	rect->x0 = 0;
3395 	rect->y0 = 0;
3396 	rect->x1 = REDRAW_MAX;
3397 	rect->y1 = 0;
3398 
3399 	sw.purpose = TREEVIEW_WALK_SELECT_ALL;
3400 	sw.data.redraw.required = false;
3401 	sw.data.redraw.rect = rect;
3402 	sw.current_y = treeview__get_search_height(tree);
3403 
3404 	treeview_walk_internal(tree, tree->root,
3405 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3406 			treeview_node_selection_walk_cb, &sw);
3407 
3408 	return sw.data.redraw.required;
3409 }
3410 
3411 
3412 /**
3413  * Commit a current selection drag, modifying the node's selection state.
3414  *
3415  * \param tree Treeview object to commit drag selection in
3416  */
treeview_commit_selection_drag(treeview * tree)3417 static void treeview_commit_selection_drag(treeview *tree)
3418 {
3419 	struct treeview_selection_walk_data sw;
3420 
3421 	sw.purpose = TREEVIEW_WALK_COMMIT_SELECT_DRAG;
3422 	sw.current_y = treeview__get_search_height(tree);
3423 
3424 	if (tree->drag.start.y > tree->drag.prev.y) {
3425 		sw.data.drag.sel_min = tree->drag.prev.y;
3426 		sw.data.drag.sel_max = tree->drag.start.y;
3427 	} else {
3428 		sw.data.drag.sel_min = tree->drag.start.y;
3429 		sw.data.drag.sel_max = tree->drag.prev.y;
3430 	}
3431 
3432 	treeview_walk_internal(tree, tree->root,
3433 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3434 			treeview_node_selection_walk_cb, &sw);
3435 }
3436 
3437 
3438 /**
3439  * Yank a selection to the node move list.
3440  *
3441  * \param tree   Treeview object to yank selection from
3442  * \param fixed  Treeview node that should not be yanked
3443  */
treeview_move_yank_selection(treeview * tree,treeview_node * fixed)3444 static void treeview_move_yank_selection(treeview *tree, treeview_node *fixed)
3445 {
3446 	struct treeview_selection_walk_data sw;
3447 
3448 	sw.purpose = TREEVIEW_WALK_YANK_SELECTION;
3449 	sw.data.yank.fixed = fixed;
3450 	sw.data.yank.prev = NULL;
3451 	sw.tree = tree;
3452 
3453 	treeview_walk_internal(tree, tree->root,
3454 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3455 			treeview_node_selection_walk_cb, &sw);
3456 }
3457 
3458 
3459 /**
3460  * Copy a selection to the clipboard.
3461  *
3462  * \param tree Treeview object to yank selection from
3463  */
treeview_copy_selection(treeview * tree)3464 static void treeview_copy_selection(treeview *tree)
3465 {
3466 	struct treeview_selection_walk_data sw;
3467 	nserror err;
3468 
3469 	sw.purpose = TREEVIEW_WALK_COPY_SELECTION;
3470 	sw.data.copy.text = NULL;
3471 	sw.data.copy.len = 0;
3472 	sw.tree = tree;
3473 
3474 	err = treeview_walk_internal(tree, tree->root,
3475 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3476 			treeview_node_selection_walk_cb, &sw);
3477 	if (err != NSERROR_OK) {
3478 		return;
3479 	}
3480 
3481 	if (sw.data.copy.text != NULL) {
3482 		guit->clipboard->set(sw.data.copy.text,
3483 				     sw.data.copy.len - 1, NULL, 0);
3484 		free(sw.data.copy.text);
3485 	}
3486 }
3487 
3488 
3489 /**
3490  * Delete a selection.
3491  *
3492  * \param tree Treeview object to delete selected nodes from
3493  * \param rect Updated to redraw rectangle
3494  * \return true iff redraw required.
3495  */
treeview_delete_selection(treeview * tree,struct rect * rect)3496 static bool treeview_delete_selection(treeview *tree, struct rect *rect)
3497 {
3498 	struct treeview_selection_walk_data sw;
3499 
3500 	assert(tree != NULL);
3501 	assert(tree->root != NULL);
3502 
3503 	rect->x0 = 0;
3504 	rect->y0 = 0;
3505 	rect->x1 = REDRAW_MAX;
3506 	rect->y1 = treeview__get_display_height(tree);
3507 
3508 	sw.purpose = TREEVIEW_WALK_DELETE_SELECTION;
3509 	sw.data.redraw.required = false;
3510 	sw.data.redraw.rect = rect;
3511 	sw.current_y = treeview__get_search_height(tree);
3512 	sw.tree = tree;
3513 
3514 	treeview_walk_internal(tree, tree->root,
3515 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3516 			treeview_node_selection_walk_cb, &sw);
3517 
3518 	return sw.data.redraw.required;
3519 }
3520 
3521 
3522 /**
3523  * Propagate selection to visible descendants of selected nodes.
3524  *
3525  * \param tree Treeview object to propagate selection in
3526  * \param rect Redraw rectangle (if redraw required)
3527  * \return true iff redraw required
3528  */
treeview_propagate_selection(treeview * tree,struct rect * rect)3529 static bool treeview_propagate_selection(treeview *tree, struct rect *rect)
3530 {
3531 	struct treeview_selection_walk_data sw;
3532 
3533 	assert(tree != NULL);
3534 	assert(tree->root != NULL);
3535 
3536 	rect->x0 = 0;
3537 	rect->y0 = 0;
3538 	rect->x1 = REDRAW_MAX;
3539 	rect->y1 = 0;
3540 
3541 	sw.purpose = TREEVIEW_WALK_PROPAGATE_SELECTION;
3542 	sw.data.redraw.required = false;
3543 	sw.data.redraw.rect = rect;
3544 	sw.current_y = treeview__get_search_height(tree);
3545 	sw.tree = tree;
3546 
3547 	treeview_walk_internal(tree, tree->root,
3548 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3549 			treeview_node_selection_walk_cb, &sw);
3550 
3551 	return sw.data.redraw.required;
3552 }
3553 
3554 
3555 /**
3556  * Move a selection according to the current move drag.
3557  *
3558  * \param tree Treeview object to move selected nodes in
3559  * \param rect Redraw rectangle
3560  * \return NSERROR_OK on success else appropriate error code
3561  */
treeview_move_selection(treeview * tree,struct rect * rect)3562 static nserror treeview_move_selection(treeview *tree, struct rect *rect)
3563 {
3564 	treeview_node *node, *next, *parent;
3565 	treeview_node *relation;
3566 	enum treeview_relationship relationship;
3567 	int height;
3568 
3569 	assert(tree != NULL);
3570 	assert(tree->root != NULL);
3571 	assert(tree->root->children != NULL);
3572 	assert(tree->move.target_pos != TV_TARGET_NONE);
3573 
3574 	height = tree->root->height;
3575 
3576 	/* Identify target location */
3577 	switch (tree->move.target_pos) {
3578 	case TV_TARGET_ABOVE:
3579 		if (tree->move.target == NULL) {
3580 			/* Target: After last child of root */
3581 			relation = tree->root->children;
3582 			while (relation->next_sib != NULL) {
3583 				relation = relation->next_sib;
3584 			}
3585 			relationship = TREE_REL_NEXT_SIBLING;
3586 
3587 		} else if (tree->move.target->prev_sib != NULL) {
3588 			/* Target: After previous sibling */
3589 			relation = tree->move.target->prev_sib;
3590 			relationship = TREE_REL_NEXT_SIBLING;
3591 
3592 		} else {
3593 			/* Target: Target: First child of parent */
3594 			assert(tree->move.target->parent != NULL);
3595 			relation = tree->move.target->parent;
3596 			relationship = TREE_REL_FIRST_CHILD;
3597 		}
3598 		break;
3599 
3600 	case TV_TARGET_INSIDE:
3601 		assert(tree->move.target != NULL);
3602 		relation = tree->move.target;
3603 		relationship = TREE_REL_FIRST_CHILD;
3604 		break;
3605 
3606 	case TV_TARGET_BELOW:
3607 		assert(tree->move.target != NULL);
3608 		relation = tree->move.target;
3609 		relationship = TREE_REL_NEXT_SIBLING;
3610 		break;
3611 
3612 	default:
3613 		NSLOG(netsurf, INFO, "Bad drop target for move.");
3614 		return NSERROR_BAD_PARAMETER;
3615 	}
3616 
3617 	if (relationship == TREE_REL_FIRST_CHILD) {
3618 		parent = relation;
3619 	} else {
3620 		parent = relation->parent;
3621 	}
3622 
3623 	/* Move all selected nodes from treeview to tree->move.root */
3624 	treeview_move_yank_selection(tree, relation);
3625 
3626 	/* Move all nodes on tree->move.root to target location */
3627 	for (node = tree->move.root; node != NULL; node = next) {
3628 		next = node->next_sib;
3629 
3630 		if (node == relation) {
3631 			continue;
3632 		}
3633 
3634 		if (!(parent->flags & TV_NFLAGS_EXPANDED)) {
3635 			if (node->flags & TV_NFLAGS_EXPANDED)
3636 				treeview_node_contract_internal(tree, node);
3637 			node->flags &= ~TV_NFLAGS_SELECTED;
3638 		}
3639 
3640 		treeview_insert_node(tree, node, relation, relationship);
3641 
3642 		relation = node;
3643 		relationship = TREE_REL_NEXT_SIBLING;
3644 	}
3645 	tree->move.root = NULL;
3646 
3647 	/* Tell window, if height has changed */
3648 	if (height != tree->root->height)
3649 		treeview__cw_update_size(tree, -1, tree->root->height);
3650 
3651 	/* TODO: Deal with redraw area properly */
3652 	rect->x0 = 0;
3653 	rect->y0 = 0;
3654 	rect->x1 = REDRAW_MAX;
3655 	rect->y1 = REDRAW_MAX;
3656 
3657 	return NSERROR_OK;
3658 }
3659 
3660 
3661 /**
3662  * context for treeview launch action
3663  */
3664 struct treeview_launch_walk_data {
3665 	int selected_depth;
3666 	treeview *tree;
3667 };
3668 
3669 
3670 /**
3671  * Treewalk node walk backward callback for tracking folder selection.
3672  */
3673 static nserror
treeview_node_launch_walk_bwd_cb(treeview_node * n,void * ctx,bool * end)3674 treeview_node_launch_walk_bwd_cb(treeview_node *n, void *ctx, bool *end)
3675 {
3676 	struct treeview_launch_walk_data *lw = ctx;
3677 
3678 	if (n->type == TREE_NODE_FOLDER && n->flags == TV_NFLAGS_SELECTED) {
3679 		lw->selected_depth--;
3680 	}
3681 
3682 	return NSERROR_OK;
3683 }
3684 
3685 
3686 /**
3687  * Treewalk node walk forward callback for launching nodes.
3688  *
3689  * \param n current node
3690  * \param ctx node launch context
3691  * \param skip_children flag to allow children to be skipped
3692  * \param end flag to allow iteration to be finished early.
3693  * \return NSERROR_OK on success else error code.
3694  */
3695 static nserror
treeview_node_launch_walk_fwd_cb(treeview_node * n,void * ctx,bool * skip_children,bool * end)3696 treeview_node_launch_walk_fwd_cb(treeview_node *n,
3697 				 void *ctx,
3698 				 bool *skip_children,
3699 				 bool *end)
3700 {
3701 	struct treeview_launch_walk_data *lw = ctx;
3702 	nserror ret = NSERROR_OK;
3703 
3704 	if (n->type == TREE_NODE_FOLDER && n->flags & TV_NFLAGS_SELECTED) {
3705 		lw->selected_depth++;
3706 
3707 	} else if (n->type == TREE_NODE_ENTRY &&
3708 		   (n->flags & TV_NFLAGS_SELECTED ||
3709 		    lw->selected_depth > 0)) {
3710 		struct treeview_node_msg msg;
3711 		msg.msg = TREE_MSG_NODE_LAUNCH;
3712 		msg.data.node_launch.mouse = BROWSER_MOUSE_HOVER;
3713 		ret = lw->tree->callbacks->entry(msg, n->client_data);
3714 	}
3715 
3716 	return ret;
3717 }
3718 
3719 
3720 /**
3721  * Launch a selection.
3722  *
3723  * \note Selected entries are launched. Entries that are descendants
3724  *        of selected folders are also launched.
3725  *
3726  * \param tree Treeview object to launch selected nodes in
3727  * \return NSERROR_OK on success, appropriate error otherwise
3728  */
treeview_launch_selection(treeview * tree)3729 static nserror treeview_launch_selection(treeview *tree)
3730 {
3731 	struct treeview_launch_walk_data lw;
3732 
3733 	assert(tree != NULL);
3734 	assert(tree->root != NULL);
3735 
3736 	lw.selected_depth = 0;
3737 	lw.tree = tree;
3738 
3739 	return treeview_walk_internal(tree, tree->root,
3740 			TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
3741 			treeview_node_launch_walk_bwd_cb,
3742 			treeview_node_launch_walk_fwd_cb, &lw);
3743 }
3744 
3745 
3746 /* Exported interface, documented in treeview.h */
3747 nserror
treeview_get_relation(treeview * tree,treeview_node ** relation,enum treeview_relationship * rel,bool at_y,int y)3748 treeview_get_relation(treeview *tree,
3749 		      treeview_node **relation,
3750 		      enum treeview_relationship *rel,
3751 		      bool at_y,
3752 		      int y)
3753 {
3754 	treeview_node *n;
3755 
3756 	assert(tree != NULL);
3757 
3758 	if (at_y) {
3759 		n = treeview_y_node(tree, y);
3760 
3761 	} else {
3762 		n = treeview_get_first_selected(tree);
3763 	}
3764 
3765 	if (n != NULL && n->parent != NULL) {
3766 		if (n == n->parent->children) {
3767 			/* First child */
3768 			*relation = n->parent;
3769 			*rel = TREE_REL_FIRST_CHILD;
3770 		} else {
3771 			/* Not first child */
3772 			*relation = n->prev_sib;
3773 			*rel = TREE_REL_NEXT_SIBLING;
3774 		}
3775 	} else {
3776 		if (tree->root->children == NULL) {
3777 			/* First child of root */
3778 			*relation = tree->root;
3779 			*rel = TREE_REL_FIRST_CHILD;
3780 		} else {
3781 			/* Last child of root */
3782 			n = tree->root->children;
3783 			while (n->next_sib != NULL)
3784 				n = n->next_sib;
3785 			*relation = n;
3786 			*rel = TREE_REL_NEXT_SIBLING;
3787 		}
3788 	}
3789 
3790 	return NSERROR_OK;
3791 }
3792 
3793 
3794 /**
3795  * context for treeview keyboard action
3796  */
3797 struct treeview_nav_state {
3798 	treeview *tree;
3799 	treeview_node *prev;
3800 	treeview_node *curr;
3801 	treeview_node *next;
3802 	treeview_node *last;
3803 	int n_selected;
3804 	int prev_n_selected;
3805 };
3806 
3807 
3808 /**
3809  * Treewalk node callback for handling mouse action.
3810  *
3811  * \param node current node
3812  * \param ctx node context
3813  * \param skip_children flag to allow children to be skipped
3814  * \param end flag to allow iteration to be finished early.
3815  * \return NSERROR_OK on success else error code.
3816  */
3817 static nserror
treeview_node_nav_cb(treeview_node * node,void * ctx,bool * skip_children,bool * end)3818 treeview_node_nav_cb(treeview_node *node,
3819 		     void *ctx,
3820 		     bool *skip_children,
3821 		     bool *end)
3822 {
3823 	struct treeview_nav_state *ns = ctx;
3824 
3825 	if (node == ns->tree->root)
3826 		return NSERROR_OK;
3827 
3828 	if (node->flags & TV_NFLAGS_SELECTED) {
3829 		ns->n_selected++;
3830 		if (ns->curr == NULL) {
3831 			ns->curr = node;
3832 		}
3833 
3834 	} else {
3835 		if (ns->n_selected == 0) {
3836 			ns->prev = node;
3837 
3838 		} else if (ns->prev_n_selected < ns->n_selected) {
3839 			ns->next = node;
3840 			ns->prev_n_selected = ns->n_selected;
3841 		}
3842 	}
3843 	ns->last = node;
3844 
3845 	return NSERROR_OK;
3846 }
3847 
3848 
3849 /**
3850  * Handle keyboard navigation.
3851  *
3852  * \note Selected entries are launched.
3853  *       Entries that are descendants of selected folders are also launched.
3854  *
3855  * \param tree Treeview object to launch selected nodes in
3856  * \param key The ucs4 character codepoint
3857  * \param rect Updated to redraw rectangle
3858  * \return true if treeview needs redraw, false otherwise
3859  */
3860 static bool
treeview_keyboard_navigation(treeview * tree,uint32_t key,struct rect * rect)3861 treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
3862 {
3863 	struct treeview_nav_state ns = {
3864 		.tree = tree,
3865 		.prev = NULL,
3866 		.curr = NULL,
3867 		.next = NULL,
3868 		.last = NULL,
3869 		.n_selected = 0,
3870 		.prev_n_selected = 0
3871 	};
3872 	int search_height = treeview__get_search_height(tree);
3873 	int h = treeview__get_display_height(tree) + search_height;
3874 	bool redraw = false;
3875 	struct treeview_node *scroll_to_node = NULL;
3876 
3877 	/* Fill out the nav. state struct, by examining the current selection
3878 	 * state */
3879 	treeview_walk_internal(tree, tree->root,
3880 			TREEVIEW_WALK_MODE_DISPLAY, NULL,
3881 			treeview_node_nav_cb, &ns);
3882 
3883 	scroll_to_node = ns.curr;
3884 
3885 	if (tree->search.search == false) {
3886 		if (ns.next == NULL)
3887 			ns.next = tree->root->children;
3888 		if (ns.prev == NULL)
3889 			ns.prev = ns.last;
3890 	}
3891 
3892 	/* Clear any existing selection */
3893 	redraw = treeview_clear_selection(tree, rect);
3894 
3895 	switch (key) {
3896 	case NS_KEY_LEFT:
3897 		if (tree->search.search == true) {
3898 			break;
3899 		}
3900 		if (ns.curr != NULL &&
3901 		    ns.curr->parent != NULL &&
3902 		    ns.curr->parent->type != TREE_NODE_ROOT) {
3903 			/* Step to parent */
3904 			ns.curr->parent->flags |= TV_NFLAGS_SELECTED;
3905 			scroll_to_node = ns.curr->parent;
3906 
3907 		} else if (ns.curr != NULL && tree->root->children != NULL) {
3908 			/* Select first node in tree */
3909 			tree->root->children->flags |= TV_NFLAGS_SELECTED;
3910 			scroll_to_node = tree->root->children;
3911 		}
3912 		break;
3913 
3914 	case NS_KEY_RIGHT:
3915 		if (ns.curr != NULL) {
3916 			if (!(ns.curr->flags & TV_NFLAGS_EXPANDED)) {
3917 				/* Toggle node to expanded */
3918 				treeview_node_expand_internal(tree, ns.curr);
3919 				if (ns.curr->children != NULL) {
3920 					/* Step to first child */
3921 					ns.curr->children->flags |=
3922 						TV_NFLAGS_SELECTED;
3923 					scroll_to_node = ns.curr->children;
3924 				} else {
3925 					/* Retain current node selection */
3926 					ns.curr->flags |= TV_NFLAGS_SELECTED;
3927 				}
3928 			} else {
3929 				/* Toggle node to contracted */
3930 				treeview_node_contract_internal(tree, ns.curr);
3931 				/* Retain current node selection */
3932 				ns.curr->flags |= TV_NFLAGS_SELECTED;
3933 			}
3934 
3935 		} else if (ns.curr != NULL) {
3936 			/* Retain current node selection */
3937 			ns.curr->flags |= TV_NFLAGS_SELECTED;
3938 		}
3939 		break;
3940 
3941 	case NS_KEY_UP:
3942 		if (ns.prev != NULL) {
3943 			/* Step to previous node */
3944 			ns.prev->flags |= TV_NFLAGS_SELECTED;
3945 			scroll_to_node = ns.prev;
3946 		}
3947 		break;
3948 
3949 	case NS_KEY_DOWN:
3950 		if (ns.next != NULL) {
3951 			/* Step to next node */
3952 			ns.next->flags |= TV_NFLAGS_SELECTED;
3953 			scroll_to_node = ns.next;
3954 		}
3955 		break;
3956 
3957 	default:
3958 		break;
3959 	}
3960 
3961 	treeview__cw_scroll_to_node(tree, scroll_to_node);
3962 
3963 	/* TODO: Deal with redraw area properly */
3964 	rect->x0 = 0;
3965 	rect->y0 = 0;
3966 	rect->x1 = REDRAW_MAX;
3967 	if (treeview__get_display_height(tree) + search_height > h)
3968 		rect->y1 = treeview__get_display_height(tree) + search_height;
3969 	else
3970 		rect->y1 = h;
3971 	redraw = true;
3972 
3973 	return redraw;
3974 }
3975 
3976 
3977 /* Exported interface, documented in treeview.h */
treeview_keypress(treeview * tree,uint32_t key)3978 bool treeview_keypress(treeview *tree, uint32_t key)
3979 {
3980 	struct rect r;	/**< Redraw rectangle */
3981 	bool redraw = false;
3982 
3983 	assert(tree != NULL);
3984 
3985 	/* Pass to any textarea, if editing in progress */
3986 	if (tree->edit.textarea != NULL) {
3987 		switch (key) {
3988 		case NS_KEY_ESCAPE:
3989 			treeview_edit_cancel(tree, true);
3990 			return true;
3991 		case NS_KEY_NL:
3992 		case NS_KEY_CR:
3993 			treeview_edit_done(tree);
3994 			return true;
3995 		default:
3996 			return textarea_keypress(tree->edit.textarea, key);
3997 		}
3998 	} else if (tree->search.active == true) {
3999 		switch (key) {
4000 		case NS_KEY_ESCAPE:
4001 			treeview__search_cancel(tree, false);
4002 			return true;
4003 		case NS_KEY_NL:
4004 		case NS_KEY_CR:
4005 			return true;
4006 		default:
4007 			return textarea_keypress(tree->search.textarea, key);
4008 		}
4009 	}
4010 
4011 	/* Keypress to be handled by treeview */
4012 	switch (key) {
4013 	case NS_KEY_SELECT_ALL:
4014 		redraw = treeview_select_all(tree, &r);
4015 		break;
4016 	case NS_KEY_COPY_SELECTION:
4017 		treeview_copy_selection(tree);
4018 		break;
4019 	case NS_KEY_DELETE_LEFT:
4020 	case NS_KEY_DELETE_RIGHT:
4021 		redraw = treeview_delete_selection(tree, &r);
4022 
4023 		if (tree->flags & TREEVIEW_DEL_EMPTY_DIRS) {
4024 			int h = tree->root->height;
4025 			/* Delete any empty nodes */
4026 			treeview_delete_empty_nodes(tree, false);
4027 
4028 			/* Inform front end of change in dimensions */
4029 			if (tree->root->height != h) {
4030 				r.y0 = 0;
4031 				treeview__cw_update_size(tree, -1,
4032 							 tree->root->height);
4033 			}
4034 		}
4035 		break;
4036 	case NS_KEY_CR:
4037 	case NS_KEY_NL:
4038 		treeview_launch_selection(tree);
4039 		break;
4040 	case NS_KEY_ESCAPE:
4041 	case NS_KEY_CLEAR_SELECTION:
4042 		redraw = treeview_clear_selection(tree, &r);
4043 		break;
4044 	case NS_KEY_LEFT:
4045 	case NS_KEY_RIGHT:
4046 	case NS_KEY_UP:
4047 	case NS_KEY_DOWN:
4048 		redraw = treeview_keyboard_navigation(tree, key, &r);
4049 		break;
4050 	default:
4051 		return false;
4052 	}
4053 
4054 	if (redraw) {
4055 		treeview__cw_invalidate_area(tree, &r);
4056 	}
4057 
4058 	return true;
4059 }
4060 
4061 
4062 /**
4063  * Set the drag&drop drop indicator
4064  *
4065  * \param tree		Treeview object to set node indicator in
4066  * \param need_redraw	True iff we already have a redraw region
4067  * \param target	The treeview node with mouse pointer over it
4068  * \param node_height	The height of node
4069  * \param node_y	The Y coord of the top of target node
4070  * \param mouse_y	Y coord of mouse position
4071  * \param rect		Redraw rectangle (if redraw required)
4072  * \return true iff redraw required
4073  */
4074 static bool
treeview_set_move_indicator(treeview * tree,bool need_redraw,treeview_node * target,int node_height,int node_y,int mouse_y,struct rect * rect)4075 treeview_set_move_indicator(treeview *tree,
4076 			    bool need_redraw,
4077 			    treeview_node *target,
4078 			    int node_height,
4079 			    int node_y,
4080 			    int mouse_y,
4081 			    struct rect *rect)
4082 {
4083 	treeview_node *orig = target;
4084 	enum treeview_target_pos target_pos;
4085 	int mouse_pos = mouse_y - node_y;
4086 	int x;
4087 
4088 	assert(tree != NULL);
4089 	assert(tree->root != NULL);
4090 	assert(tree->root->children != NULL);
4091 	assert(target != NULL);
4092 
4093 	if (target->flags & TV_NFLAGS_SELECTED) {
4094 		/* Find top selected ancestor */
4095 		while (target->parent &&
4096 		       target->parent->flags & TV_NFLAGS_SELECTED) {
4097 			target = target->parent;
4098 		}
4099 
4100 		/* Find top adjacent selected sibling */
4101 		while (target->prev_sib &&
4102 		       target->prev_sib->flags & TV_NFLAGS_SELECTED) {
4103 			target = target->prev_sib;
4104 		}
4105 		target_pos = TV_TARGET_ABOVE;
4106 
4107 	} else switch (target->type) {
4108 		case TREE_NODE_FOLDER:
4109 			if (mouse_pos <= node_height / 4) {
4110 				target_pos = TV_TARGET_ABOVE;
4111 			} else if (mouse_pos <= (3 * node_height) / 4 ||
4112 				   target->flags & TV_NFLAGS_EXPANDED) {
4113 				target_pos = TV_TARGET_INSIDE;
4114 			} else {
4115 				target_pos = TV_TARGET_BELOW;
4116 			}
4117 			break;
4118 
4119 		case TREE_NODE_ENTRY:
4120 			if (mouse_pos <= node_height / 2) {
4121 				target_pos = TV_TARGET_ABOVE;
4122 			} else {
4123 				target_pos = TV_TARGET_BELOW;
4124 			}
4125 			break;
4126 
4127 		default:
4128 			assert(target->type != TREE_NODE_ROOT);
4129 			return false;
4130 		}
4131 
4132 	if (target_pos == tree->move.target_pos &&
4133 	    target == tree->move.target) {
4134 		/* No change */
4135 		return need_redraw;
4136 	}
4137 
4138 	if (tree->move.target_pos != TV_TARGET_NONE) {
4139 		/* Need to clear old indicator position */
4140 		if (need_redraw) {
4141 			if (rect->x0 > tree->move.target_area.x0)
4142 				rect->x0 = tree->move.target_area.x0;
4143 			if (tree->move.target_area.x1 > rect->x1)
4144 				rect->x1 = tree->move.target_area.x1;
4145 			if (rect->y0 > tree->move.target_area.y0)
4146 				rect->y0 = tree->move.target_area.y0;
4147 			if (tree->move.target_area.y1 > rect->y1)
4148 				rect->y1 = tree->move.target_area.y1;
4149 		} else {
4150 			*rect = tree->move.target_area;
4151 			need_redraw = true;
4152 		}
4153 	}
4154 
4155 	/* Offset for ABOVE / BELOW */
4156 	if (target_pos == TV_TARGET_ABOVE) {
4157 		if (target != orig) {
4158 			node_y = treeview_node_y(tree, target);
4159 		}
4160 		node_y -= (tree_g.line_height + 1) / 2;
4161 	} else if (target_pos == TV_TARGET_BELOW) {
4162 		node_y += node_height - (tree_g.line_height + 1) / 2;
4163 	}
4164 
4165 	/* Oftsets are all relative to centred (INSIDE) */
4166 	node_y += (tree_g.line_height -
4167 		   treeview_res[TREE_RES_ARROW].height + 1) / 2;
4168 
4169 	x = target->inset + tree_g.move_offset;
4170 
4171 	/* Update target details */
4172 	tree->move.target = target;
4173 	tree->move.target_pos = target_pos;
4174 	tree->move.target_area.x0 = x;
4175 	tree->move.target_area.y0 = node_y;
4176 	tree->move.target_area.x1 = tree_g.icon_size + x;
4177 	tree->move.target_area.y1 = tree_g.icon_size + node_y;
4178 
4179 	if (target_pos != TV_TARGET_NONE) {
4180 		/* Need to draw new indicator position */
4181 		if (need_redraw) {
4182 			if (rect->x0 > tree->move.target_area.x0)
4183 				rect->x0 = tree->move.target_area.x0;
4184 			if (tree->move.target_area.x1 > rect->x1)
4185 				rect->x1 = tree->move.target_area.x1;
4186 			if (rect->y0 > tree->move.target_area.y0)
4187 				rect->y0 = tree->move.target_area.y0;
4188 			if (tree->move.target_area.y1 > rect->y1)
4189 				rect->y1 = tree->move.target_area.y1;
4190 		} else {
4191 			*rect = tree->move.target_area;
4192 			need_redraw = true;
4193 		}
4194 	}
4195 
4196 	return need_redraw;
4197 }
4198 
4199 
4200 /**
4201  * Callback for textarea_create, in desktop/treeview.h
4202  *
4203  * \param data treeview context
4204  * \param msg textarea message
4205  */
treeview_textarea_callback(void * data,struct textarea_msg * msg)4206 static void treeview_textarea_callback(void *data, struct textarea_msg *msg)
4207 {
4208 	treeview *tree = data;
4209 	struct rect *r;
4210 
4211 	switch (msg->type) {
4212 	case TEXTAREA_MSG_DRAG_REPORT:
4213 		if (msg->data.drag == TEXTAREA_DRAG_NONE) {
4214 			/* Textarea drag finished */
4215 			tree->drag.type = TV_DRAG_NONE;
4216 		} else {
4217 			/* Textarea drag started */
4218 			tree->drag.type = TV_DRAG_TEXTAREA;
4219 		}
4220 		treeview__cw_drag_status(tree, tree->drag.type);
4221 		break;
4222 
4223 	case TEXTAREA_MSG_REDRAW_REQUEST:
4224 		r = &msg->data.redraw;
4225 		r->x0 += tree->edit.x;
4226 		r->y0 += tree->edit.y;
4227 		r->x1 += tree->edit.x;
4228 		r->y1 += tree->edit.y;
4229 
4230 		/* Redraw the textarea */
4231 		treeview__cw_invalidate_area(tree, r);
4232 		break;
4233 
4234 	default:
4235 		break;
4236 	}
4237 }
4238 
4239 
4240 /**
4241  * Start edit of node field, at given y-coord, if editable
4242  *
4243  * \param tree Treeview object to consider editing in
4244  * \param n The treeview node to try editing
4245  * \param node_y The Y coord of the top of n
4246  * \param mouse_x X coord of mouse position
4247  * \param mouse_y Y coord of mouse position
4248  * \param rect Redraw rectangle (if redraw required)
4249  * \return true iff redraw required
4250  */
4251 static bool
treeview_edit_node_at_point(treeview * tree,treeview_node * n,int node_y,int mouse_x,int mouse_y,struct rect * rect)4252 treeview_edit_node_at_point(treeview *tree,
4253 			    treeview_node *n,
4254 			    int node_y,
4255 			    int mouse_x,
4256 			    int mouse_y,
4257 			    struct rect *rect)
4258 {
4259 	struct treeview_text *field_data = NULL;
4260 	struct treeview_field *ef, *field_desc = NULL;
4261 	int pos = node_y + tree_g.line_height;
4262 	int field_y = node_y;
4263 	int field_x;
4264 	int width, height;
4265 	bool success;
4266 
4267 	/* If the main field is editable, make field_data point to it */
4268 	if (n->type == TREE_NODE_ENTRY)
4269 		ef = &(tree->fields[0]);
4270 	else
4271 		ef = &(tree->fields[tree->n_fields]);
4272 	if (ef->flags & TREE_FLAG_ALLOW_EDIT) {
4273 		field_data = &n->text;
4274 		field_desc = ef;
4275 		field_y = node_y;
4276 	}
4277 
4278 	/* Check for editable entry fields */
4279 	if (n->type == TREE_NODE_ENTRY && n->height != tree_g.line_height) {
4280 		struct treeview_node_entry *e = (struct treeview_node_entry *)n;
4281 		int i;
4282 
4283 		for (i = 0; i < tree->n_fields - 1; i++) {
4284 			if (mouse_y <= pos)
4285 				continue;
4286 
4287 			ef = &(tree->fields[i + 1]);
4288 			pos += tree_g.line_height;
4289 			if (mouse_y <= pos && (ef->flags &
4290 					       TREE_FLAG_ALLOW_EDIT)) {
4291 				field_data = &e->fields[i].value;
4292 				field_desc = ef;
4293 				field_y = pos - tree_g.line_height;
4294 			}
4295 		}
4296 	}
4297 
4298 	if (field_data == NULL || field_desc == NULL) {
4299 		/* No editable field */
4300 		return false;
4301 	}
4302 
4303 	/* Get window width/height */
4304 	treeview__cw_get_window_dimensions(tree, &width, &height);
4305 
4306 	/* Calculate textarea width/height */
4307 	field_x = n->inset + tree_g.step_width + tree_g.icon_step - 3;
4308 	width -= field_x;
4309 	height = tree_g.line_height;
4310 
4311 	/* Create text area */
4312 	tree->edit.textarea = treeview__create_textarea(tree, width, height,
4313 			0x000000, 0xffffff, 0x000000, plot_style_odd.text,
4314 			treeview_textarea_callback);
4315 	if (tree->edit.textarea == NULL) {
4316 		return false;
4317 	}
4318 
4319 	success = textarea_set_text(tree->edit.textarea, field_data->data);
4320 	if (!success) {
4321 		textarea_destroy(tree->edit.textarea);
4322 		return false;
4323 	}
4324 
4325 	tree->edit.node = n;
4326 	tree->edit.field = field_desc->field;
4327 
4328 	/* Position the caret */
4329 	mouse_x -= field_x;
4330 	if (mouse_x < 0)
4331 		mouse_x = 0;
4332 	else if (mouse_x >= width)
4333 		mouse_x = width - 1;
4334 
4335 	textarea_mouse_action(tree->edit.textarea,
4336 			      BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1,
4337 			      mouse_x, tree_g.line_height / 2);
4338 
4339 	/* Position the textarea */
4340 	tree->edit.x = field_x;
4341 	tree->edit.y = field_y;
4342 	tree->edit.w = width;
4343 	tree->edit.h = height;
4344 
4345 	/* Setup redraw rectangle */
4346 	if (rect->x0 > field_x)
4347 		rect->x0 = field_x;
4348 	if (rect->y0 > field_y)
4349 		rect->y0 = field_y;
4350 	if (rect->x1 < field_x + width)
4351 		rect->x1 = field_x + width;
4352 	if (rect->y1 < field_y + height)
4353 		rect->y1 = field_y + height;
4354 
4355 	return true;
4356 }
4357 
4358 
4359 /* Exported interface, documented in treeview.h */
treeview_edit_selection(treeview * tree)4360 void treeview_edit_selection(treeview *tree)
4361 {
4362 	struct rect rect;
4363 	treeview_node *n;
4364 	bool redraw;
4365 	int y;
4366 
4367 	assert(tree != NULL);
4368 	assert(tree->root != NULL);
4369 
4370 	/* Get first selected node */
4371 	n = treeview_get_first_selected(tree);
4372 
4373 	if (n == NULL)
4374 		return;
4375 
4376 	/* Get node's y-position */
4377 	y = treeview_node_y(tree, n);
4378 
4379 	/* Edit node at y */
4380 	redraw = treeview_edit_node_at_point(tree, n, y,
4381 					     0, y + tree_g.line_height / 2, &rect);
4382 
4383 	if (redraw == false)
4384 		return;
4385 
4386 	/* Redraw */
4387 	rect.x0 = 0;
4388 	rect.y0 = y;
4389 	rect.x1 = REDRAW_MAX;
4390 	rect.y1 = y + tree_g.line_height;
4391 	treeview__cw_invalidate_area(tree, &rect);
4392 }
4393 
4394 
4395 /**
4396  * context for treeview mouse handling
4397  */
4398 struct treeview_mouse_action {
4399 	treeview *tree;
4400 	browser_mouse_state mouse;
4401 	int x;
4402 	int y;
4403 	int current_y;	/* Y coordinate value of top of current node */
4404 	int search_height;
4405 };
4406 
4407 
4408 /**
4409  * Treewalk node callback for handling mouse action.
4410  *
4411  * \param node current node
4412  * \param ctx node context
4413  * \param skip_children flag to allow children to be skipped
4414  * \param end flag to allow iteration to be finished early.
4415  * \return NSERROR_OK on success else error code.
4416  */
4417 static nserror
treeview_node_mouse_action_cb(treeview_node * node,void * ctx,bool * skip_children,bool * end)4418 treeview_node_mouse_action_cb(treeview_node *node,
4419 			      void *ctx,
4420 			      bool *skip_children,
4421 			      bool *end)
4422 {
4423 	struct treeview_mouse_action *ma = ctx;
4424 	struct rect r;
4425 	bool redraw = false;
4426 	bool click;
4427 	int height;
4428 	enum {
4429 		TV_NODE_ACTION_NONE		= 0,
4430 		TV_NODE_ACTION_SELECTION	= (1 << 0)
4431 	} action = TV_NODE_ACTION_NONE;
4432 	enum treeview_node_part part = TV_NODE_PART_NONE;
4433 	nserror err;
4434 
4435 	r.x0 = 0;
4436 	r.x1 = REDRAW_MAX;
4437 
4438 	height = (node->type == TREE_NODE_ENTRY) ? node->height :
4439 		tree_g.line_height;
4440 
4441 	/* Skip line if we've not reached mouse y */
4442 	if (ma->y > ma->current_y + height) {
4443 		ma->current_y += height;
4444 		return NSERROR_OK; /* Don't want to abort tree walk */
4445 	}
4446 
4447 	/* Find where the mouse is */
4448 	if (ma->y <= ma->current_y + tree_g.line_height) {
4449 		int inset = node->inset;
4450 		if (ma->tree->search.search == true) {
4451 			inset = tree_g.window_padding;
4452 		}
4453 		if (ma->x >= inset - 1 &&
4454 				ma->x < inset + tree_g.step_width) {
4455 			/* Over expansion toggle */
4456 			part = TV_NODE_PART_TOGGLE;
4457 
4458 		} else if (ma->x >= inset + tree_g.step_width &&
4459 				ma->x < inset + tree_g.step_width +
4460 				tree_g.icon_step + node->text.width) {
4461 			/* On node */
4462 			part = TV_NODE_PART_ON_NODE;
4463 		}
4464 	} else if (node->type == TREE_NODE_ENTRY &&
4465 		   height > tree_g.line_height) {
4466 		/* Expanded entries */
4467 		int x = node->inset + tree_g.step_width + tree_g.icon_step;
4468 		int y = ma->current_y + tree_g.line_height;
4469 		int i;
4470 		struct treeview_node_entry *entry =
4471 			(struct treeview_node_entry *)node;
4472 		for (i = 0; i < ma->tree->n_fields - 1; i++) {
4473 			struct treeview_field *ef = &(ma->tree->fields[i + 1]);
4474 
4475 			if (ma->y > y + tree_g.line_height) {
4476 				y += tree_g.line_height;
4477 				continue;
4478 			}
4479 
4480 			if (ef->flags & TREE_FLAG_SHOW_NAME) {
4481 				int max_width = ma->tree->field_width;
4482 
4483 				if (ma->x >= x + max_width - ef->value.width -
4484 				    tree_g.step_width &&
4485 				    ma->x < x + max_width -
4486 				    tree_g.step_width) {
4487 					/* On a field name */
4488 					part = TV_NODE_PART_ON_NODE;
4489 
4490 				} else if (ma->x >= x + max_width &&
4491 					   ma->x < x + max_width +
4492 					   entry->fields[i].value.width) {
4493 					/* On a field value */
4494 					part = TV_NODE_PART_ON_NODE;
4495 				}
4496 			} else {
4497 				if (ma->x >= x && ma->x < x +
4498 				    entry->fields[i].value.width) {
4499 					/* On a field value */
4500 					part = TV_NODE_PART_ON_NODE;
4501 				}
4502 			}
4503 
4504 			break;
4505 		}
4506 	}
4507 
4508 	/* Record what position / part a drag started on */
4509 	if (ma->mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) &&
4510 	    ma->tree->drag.type == TV_DRAG_NONE) {
4511 		ma->tree->drag.selected = node->flags & TV_NFLAGS_SELECTED;
4512 		ma->tree->drag.start_node = node;
4513 		ma->tree->drag.part = part;
4514 		ma->tree->drag.start.x = ma->x;
4515 		ma->tree->drag.start.y = ma->y;
4516 		ma->tree->drag.start.node_y = ma->current_y;
4517 		ma->tree->drag.start.node_h = height;
4518 
4519 		ma->tree->drag.prev.x = ma->x;
4520 		ma->tree->drag.prev.y = ma->y;
4521 		ma->tree->drag.prev.node_y = ma->current_y;
4522 		ma->tree->drag.prev.node_h = height;
4523 	}
4524 
4525 	/* Handle drag start */
4526 	if (ma->tree->drag.type == TV_DRAG_NONE) {
4527 		if (ma->mouse & BROWSER_MOUSE_DRAG_1 &&
4528 		    ma->tree->drag.selected == false &&
4529 		    ma->tree->drag.part == TV_NODE_PART_NONE) {
4530 			ma->tree->drag.type = TV_DRAG_SELECTION;
4531 			treeview__cw_drag_status(ma->tree,
4532 						 CORE_WINDOW_DRAG_SELECTION);
4533 
4534 		} else if (ma->tree->search.search == false &&
4535 			   !(ma->tree->flags & TREEVIEW_NO_MOVES) &&
4536 			   ma->mouse & BROWSER_MOUSE_DRAG_1 &&
4537 			   (ma->tree->drag.selected == true ||
4538 			    ma->tree->drag.part == TV_NODE_PART_ON_NODE)) {
4539 			ma->tree->drag.type = TV_DRAG_MOVE;
4540 			treeview__cw_drag_status(ma->tree,
4541 						 CORE_WINDOW_DRAG_MOVE);
4542 			redraw |= treeview_propagate_selection(ma->tree, &r);
4543 
4544 		} else if (ma->mouse & BROWSER_MOUSE_DRAG_2) {
4545 			ma->tree->drag.type = TV_DRAG_SELECTION;
4546 			treeview__cw_drag_status(ma->tree,
4547 						 CORE_WINDOW_DRAG_SELECTION);
4548 		}
4549 
4550 		if (ma->tree->drag.start_node != NULL &&
4551 		    ma->tree->drag.type == TV_DRAG_SELECTION) {
4552 			ma->tree->drag.start_node->flags ^= TV_NFLAGS_SELECTED;
4553 		}
4554 	}
4555 
4556 	/* Handle active drags */
4557 	switch (ma->tree->drag.type) {
4558 	case TV_DRAG_SELECTION:
4559 	{
4560 		int curr_y1 = ma->current_y + height;
4561 		int prev_y1 = ma->tree->drag.prev.node_y +
4562 			ma->tree->drag.prev.node_h;
4563 
4564 		r.y0 = (ma->current_y < ma->tree->drag.prev.node_y) ?
4565 			ma->current_y : ma->tree->drag.prev.node_y;
4566 		r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1;
4567 
4568 		redraw = true;
4569 
4570 		ma->tree->drag.prev.x = ma->x;
4571 		ma->tree->drag.prev.y = ma->y;
4572 		ma->tree->drag.prev.node_y = ma->current_y;
4573 		ma->tree->drag.prev.node_h = height;
4574 	}
4575 		break;
4576 
4577 	case TV_DRAG_MOVE:
4578 		redraw |= treeview_set_move_indicator(ma->tree, redraw,
4579 						      node, height,
4580 						      ma->current_y, ma->y, &r);
4581 		break;
4582 
4583 	default:
4584 		break;
4585 	}
4586 
4587 	click = ma->mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2);
4588 
4589 	if (((node->type == TREE_NODE_FOLDER) &&
4590 	     (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) ||
4591 	    (part == TV_NODE_PART_TOGGLE && click)) {
4592 		int h = treeview__get_display_height(ma->tree) +
4593 				ma->search_height;
4594 
4595 		/* Clear any existing selection */
4596 		redraw |= treeview_clear_selection(ma->tree, &r);
4597 
4598 		/* Toggle node expansion */
4599 		if (node->flags & TV_NFLAGS_EXPANDED) {
4600 			err = treeview_node_contract_internal(ma->tree, node);
4601 		} else {
4602 			err = treeview_node_expand_internal(ma->tree, node);
4603 		}
4604 		if (err != NSERROR_OK) {
4605 			return err;
4606 		}
4607 
4608 		/* Set up redraw */
4609 		if (!redraw || r.y0 > ma->current_y)
4610 			r.y0 = ma->current_y;
4611 		if (h > treeview__get_display_height(ma->tree) +
4612 				ma->search_height) {
4613 			r.y1 = h;
4614 		} else {
4615 			r.y1 = treeview__get_display_height(ma->tree) +
4616 				ma->search_height;
4617 		}
4618 		redraw = true;
4619 
4620 	} else if ((node->type == TREE_NODE_ENTRY) &&
4621 		   (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) {
4622 		struct treeview_node_msg msg;
4623 		msg.msg = TREE_MSG_NODE_LAUNCH;
4624 		msg.data.node_launch.mouse = ma->mouse;
4625 
4626 		/* Clear any existing selection */
4627 		redraw |= treeview_clear_selection(ma->tree, &r);
4628 
4629 		/* Tell client an entry was launched */
4630 		ma->tree->callbacks->entry(msg, node->client_data);
4631 
4632 	} else if (ma->mouse & BROWSER_MOUSE_PRESS_2 ||
4633 		   (ma->mouse & BROWSER_MOUSE_PRESS_1 &&
4634 		    ma->mouse & BROWSER_MOUSE_MOD_2)) {
4635 		/* Toggle selection of node */
4636 		action |= TV_NODE_ACTION_SELECTION;
4637 
4638 	} else if (ma->mouse & BROWSER_MOUSE_CLICK_1 &&
4639 		   ma->mouse &
4640 		   (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3) &&
4641 		   part != TV_NODE_PART_TOGGLE) {
4642 
4643 		/* Clear any existing selection */
4644 		redraw |= treeview_clear_selection(ma->tree, &r);
4645 
4646 		/* Edit node */
4647 		redraw |= treeview_edit_node_at_point(ma->tree, node,
4648 						      ma->current_y, ma->x,
4649 						      ma->y, &r);
4650 
4651 	} else if (ma->mouse & BROWSER_MOUSE_PRESS_1 &&
4652 		   !(ma->mouse &
4653 		     (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3)) &&
4654 		   !(node->flags & TV_NFLAGS_SELECTED) &&
4655 		   part != TV_NODE_PART_TOGGLE) {
4656 		/* Clear any existing selection */
4657 		redraw |= treeview_clear_selection(ma->tree, &r);
4658 
4659 		/* Select node */
4660 		action |= TV_NODE_ACTION_SELECTION;
4661 
4662 	}
4663 
4664 	if (action & TV_NODE_ACTION_SELECTION) {
4665 		/* Handle change in selection */
4666 		node->flags ^= TV_NFLAGS_SELECTED;
4667 
4668 		/* Redraw */
4669 		if (!redraw) {
4670 			r.y0 = ma->current_y;
4671 			r.y1 = ma->current_y + height;
4672 			redraw = true;
4673 		} else {
4674 			if (r.y0 > ma->current_y) {
4675 				r.y0 = ma->current_y;
4676 			}
4677 			if (r.y1 < ma->current_y + height) {
4678 				r.y1 = ma->current_y + height;
4679 			}
4680 		}
4681 	}
4682 
4683 	if (redraw) {
4684 		treeview__cw_invalidate_area(ma->tree, &r);
4685 	}
4686 
4687 	*end = true; /* Reached line with click; stop walking tree */
4688 	return NSERROR_OK;
4689 }
4690 
4691 
4692 /* Exported interface, documented in treeview.h */
4693 void
treeview_mouse_action(treeview * tree,browser_mouse_state mouse,int x,int y)4694 treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y)
4695 {
4696 	struct rect r;
4697 	bool redraw = false;
4698 	int search_height = treeview__get_search_height(tree);
4699 
4700 	assert(tree != NULL);
4701 	assert(tree->root != NULL);
4702 
4703 	/* Not interested in whether mouse leaves window. */
4704 	if (mouse == BROWSER_MOUSE_LEAVE) {
4705 		return;
4706 	}
4707 
4708 	/* Handle mouse drag captured by textarea */
4709 	if (tree->drag.type == TV_DRAG_TEXTAREA) {
4710 		textarea_mouse_action(tree->edit.textarea, mouse,
4711 				      x - tree->edit.x, y - tree->edit.y);
4712 		return;
4713 	} else if (tree->drag.type == TV_DRAG_SEARCH) {
4714 		if (tree->search.active == false) {
4715 			tree->search.active = true;
4716 			if (treeview_clear_selection(tree, &r)) {
4717 				treeview__cw_invalidate_area(tree, &r);
4718 			}
4719 		}
4720 		textarea_mouse_action(tree->search.textarea, mouse,
4721 				x - tree_g.window_padding - tree_g.icon_size,
4722 				y);
4723 		return;
4724 	} else if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) &&
4725 		   y < search_height && tree->search.active == false) {
4726 		tree->search.active = true;
4727 		if (treeview_clear_selection(tree, &r)) {
4728 			treeview__cw_invalidate_area(tree, &r);
4729 		}
4730 		textarea_mouse_action(tree->search.textarea, mouse,
4731 				x - tree_g.window_padding - tree_g.icon_size,
4732 				y);
4733 		return;
4734 	} else if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) &&
4735 			tree->search.active == true) {
4736 
4737 		tree->search.active = false;
4738 		textarea_set_caret(tree->search.textarea, -1);
4739 		r.x0 = 0;
4740 		r.y0 = 0;
4741 		r.x1 = REDRAW_MAX;
4742 		r.y1 = tree_g.line_height;
4743 		treeview__cw_invalidate_area(tree, &r);
4744 	}
4745 
4746 	/* Handle textarea related mouse action */
4747 	if (tree->edit.textarea != NULL) {
4748 		int ta_x = x - tree->edit.x;
4749 		int ta_y = y - tree->edit.y;
4750 
4751 		if (ta_x > 0 && ta_x < tree->edit.w &&
4752 		    ta_y > 0 && ta_y < tree->edit.h) {
4753 			/* Inside textarea */
4754 			textarea_mouse_action(tree->edit.textarea, mouse,
4755 					      ta_x, ta_y);
4756 			return;
4757 
4758 		} else if (mouse != BROWSER_MOUSE_HOVER) {
4759 			/* Action outside textarea */
4760 			treeview_edit_cancel(tree, true);
4761 		}
4762 	}
4763 
4764 	/* Handle drag ends */
4765 	if (mouse == BROWSER_MOUSE_HOVER) {
4766 		switch (tree->drag.type) {
4767 		case TV_DRAG_SELECTION:
4768 			treeview_commit_selection_drag(tree);
4769 			tree->drag.type = TV_DRAG_NONE;
4770 			tree->drag.start_node = NULL;
4771 
4772 			treeview__cw_drag_status(tree, CORE_WINDOW_DRAG_NONE);
4773 			return;
4774 		case TV_DRAG_MOVE:
4775 			treeview_move_selection(tree, &r);
4776 			tree->drag.type = TV_DRAG_NONE;
4777 			tree->drag.start_node = NULL;
4778 
4779 			tree->move.target = NULL;
4780 			tree->move.target_pos = TV_TARGET_NONE;
4781 
4782 			treeview__cw_drag_status(tree, CORE_WINDOW_DRAG_NONE);
4783 			treeview__cw_invalidate_area(tree, &r);
4784 			return;
4785 		default:
4786 			/* No drag to end */
4787 			break;
4788 		}
4789 	}
4790 
4791 	if (y > treeview__get_display_height(tree) + search_height) {
4792 		/* Below tree */
4793 
4794 		r.x0 = 0;
4795 		r.x1 = REDRAW_MAX;
4796 
4797 		/* Record what position / part a drag started on */
4798 		if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) &&
4799 		    tree->drag.type == TV_DRAG_NONE) {
4800 			tree->drag.selected = false;
4801 			tree->drag.start_node = NULL;
4802 			tree->drag.part = TV_NODE_PART_NONE;
4803 			tree->drag.start.x = x;
4804 			tree->drag.start.y = y;
4805 			tree->drag.start.node_y = tree->root->height;
4806 			tree->drag.start.node_h = 0;
4807 
4808 			tree->drag.prev.x = x;
4809 			tree->drag.prev.y = y;
4810 			tree->drag.prev.node_y = tree->root->height;
4811 			tree->drag.prev.node_h = 0;
4812 		}
4813 
4814 		/* Handle drag start */
4815 		if (tree->drag.type == TV_DRAG_NONE) {
4816 			if (mouse & BROWSER_MOUSE_DRAG_1 &&
4817 			    tree->drag.selected == false &&
4818 			    tree->drag.part == TV_NODE_PART_NONE) {
4819 				tree->drag.type = TV_DRAG_SELECTION;
4820 				treeview__cw_drag_status(tree,
4821 						CORE_WINDOW_DRAG_SELECTION);
4822 			} else if (mouse & BROWSER_MOUSE_DRAG_2) {
4823 				tree->drag.type = TV_DRAG_SELECTION;
4824 				treeview__cw_drag_status(tree,
4825 						CORE_WINDOW_DRAG_SELECTION);
4826 			}
4827 
4828 			if (tree->drag.start_node != NULL &&
4829 			    tree->drag.type == TV_DRAG_SELECTION) {
4830 				tree->drag.start_node->flags ^=
4831 					TV_NFLAGS_SELECTED;
4832 			}
4833 		}
4834 
4835 		/* Handle selection drags */
4836 		if (tree->drag.type == TV_DRAG_SELECTION) {
4837 			int curr_y1 = tree->root->height;
4838 			int prev_y1 = tree->drag.prev.node_y +
4839 				tree->drag.prev.node_h;
4840 
4841 			r.y0 = tree->drag.prev.node_y;
4842 			r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1;
4843 
4844 			redraw = true;
4845 
4846 			tree->drag.prev.x = x;
4847 			tree->drag.prev.y = y;
4848 			tree->drag.prev.node_y = curr_y1;
4849 			tree->drag.prev.node_h = 0;
4850 		}
4851 
4852 		if (mouse & BROWSER_MOUSE_PRESS_1) {
4853 			/* Clear any existing selection */
4854 			redraw |= treeview_clear_selection(tree, &r);
4855 		}
4856 
4857 		if (redraw) {
4858 			treeview__cw_invalidate_area(tree, &r);
4859 		}
4860 
4861 	} else {
4862 		/* On tree */
4863 		struct treeview_mouse_action ma = {
4864 			.tree = tree,
4865 			.mouse = mouse,
4866 			.x = x,
4867 			.y = y,
4868 			.current_y = search_height,
4869 			.search_height = search_height,
4870 		};
4871 
4872 		treeview_walk_internal(tree, tree->root,
4873 				TREEVIEW_WALK_MODE_DISPLAY, NULL,
4874 				treeview_node_mouse_action_cb, &ma);
4875 	}
4876 }
4877 
4878 /* Exported interface, documented in treeview.h */
treeview_get_height(treeview * tree)4879 int treeview_get_height(treeview *tree)
4880 {
4881 	int search_height = treeview__get_search_height(tree);
4882 	int height = treeview__get_display_height(tree);
4883 
4884 	assert(tree != NULL);
4885 	assert(tree->root != NULL);
4886 
4887 	treeview__cw_update_size(tree, -1, height);
4888 
4889 	return height + search_height;
4890 }
4891 
4892 /* Exported interface, documented in treeview.h */
treeview_set_search_string(treeview * tree,const char * string)4893 nserror treeview_set_search_string(
4894 		treeview *tree,
4895 		const char *string)
4896 {
4897 	if (!(tree->flags & TREEVIEW_SEARCHABLE)) {
4898 		return NSERROR_BAD_PARAMETER;
4899 	}
4900 
4901 	if (string == NULL || strlen(string) == 0) {
4902 		tree->search.active = false;
4903 		tree->search.search = false;
4904 		return treeview__search(tree, "", 0);
4905 	}
4906 
4907 	tree->search.active = true;
4908 	tree->search.search = true;
4909 	if (!textarea_set_text(tree->search.textarea, string)) {
4910 		return NSERROR_UNKNOWN;
4911 	}
4912 
4913 	treeview__cw_full_redraw(tree);
4914 
4915 	return NSERROR_OK;
4916 }
4917 
4918 /**
4919  * Initialise the plot styles from CSS system colour values.
4920  *
4921  * \param font_pt_size font size to use
4922  * \return NSERROR_OK on success else appropriate error code
4923  */
treeview_init_plot_styles(int font_pt_size)4924 static nserror treeview_init_plot_styles(int font_pt_size)
4925 {
4926 	/* Background colour */
4927 	plot_style_even.bg.stroke_type = PLOT_OP_TYPE_NONE;
4928 	plot_style_even.bg.stroke_width = 0;
4929 	plot_style_even.bg.stroke_colour = 0;
4930 	plot_style_even.bg.fill_type = PLOT_OP_TYPE_SOLID;
4931 	plot_style_even.bg.fill_colour = nscolours[NSCOLOUR_WIN_EVEN_BG];
4932 
4933 	/* Text colour */
4934 	plot_style_even.text.family = PLOT_FONT_FAMILY_SANS_SERIF;
4935 	plot_style_even.text.size = font_pt_size;
4936 	plot_style_even.text.weight = 400;
4937 	plot_style_even.text.flags = FONTF_NONE;
4938 	plot_style_even.text.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG];
4939 	plot_style_even.text.background = nscolours[NSCOLOUR_WIN_EVEN_BG];
4940 
4941 	/* Entry field text colour */
4942 	plot_style_even.itext = plot_style_even.text;
4943 	plot_style_even.itext.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_FADED];
4944 
4945 	/* Selected background colour */
4946 	plot_style_even.sbg = plot_style_even.bg;
4947 	plot_style_even.sbg.fill_colour = nscolours[NSCOLOUR_SEL_BG];
4948 
4949 	/* Selected text colour */
4950 	plot_style_even.stext = plot_style_even.text;
4951 	plot_style_even.stext.foreground = nscolours[NSCOLOUR_SEL_FG];
4952 	plot_style_even.stext.background = nscolours[NSCOLOUR_SEL_BG];
4953 
4954 	/* Selected entry field text colour */
4955 	plot_style_even.sitext = plot_style_even.stext;
4956 	plot_style_even.sitext.foreground = nscolours[NSCOLOUR_SEL_FG_SUBTLE];
4957 
4958 	/* Odd numbered node styles */
4959 	plot_style_odd.bg = plot_style_even.bg;
4960 	plot_style_odd.bg.fill_colour = nscolours[NSCOLOUR_WIN_ODD_BG];
4961 	plot_style_odd.text = plot_style_even.text;
4962 	plot_style_odd.text.background = plot_style_odd.bg.fill_colour;
4963 	plot_style_odd.itext = plot_style_odd.text;
4964 	plot_style_odd.itext.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_FADED];
4965 
4966 	plot_style_odd.sbg = plot_style_even.sbg;
4967 	plot_style_odd.stext = plot_style_even.stext;
4968 	plot_style_odd.sitext = plot_style_even.sitext;
4969 
4970 	return NSERROR_OK;
4971 }
4972 
4973 
4974 /**
4975  * Callback for hlcache retrieving resources.
4976  *
4977  * \param handle content hlcache handle
4978  * \param event The event that occurred on the content
4979  * \param pw treeview resource context
4980  */
4981 static nserror
treeview_res_cb(struct hlcache_handle * handle,const hlcache_event * event,void * pw)4982 treeview_res_cb(struct hlcache_handle *handle,
4983 		const hlcache_event *event,
4984 		void *pw)
4985 {
4986 	struct treeview_resource *r = pw;
4987 
4988 	switch (event->type) {
4989 	case CONTENT_MSG_READY:
4990 	case CONTENT_MSG_DONE:
4991 		r->ready = true;
4992 		r->height = content_get_height(handle);
4993 		break;
4994 
4995 	default:
4996 		break;
4997 	}
4998 
4999 	return NSERROR_OK;
5000 }
5001 
5002 
5003 /**
5004  * Fetch content resources used by treeview.
5005  */
treeview_init_resources(void)5006 static void treeview_init_resources(void)
5007 {
5008 	int i;
5009 
5010 	for (i = 0; i < TREE_RES_LAST; i++) {
5011 		nsurl *url;
5012 		treeview_res[i].ready = false;
5013 		treeview_res[i].height = 0;
5014 		if (nsurl_create(treeview_res[i].url, &url) == NSERROR_OK) {
5015 			hlcache_handle_retrieve(url, 0, NULL, NULL,
5016 						treeview_res_cb,
5017 						&(treeview_res[i]), NULL,
5018 						CONTENT_IMAGE,
5019 						&(treeview_res[i].c));
5020 			nsurl_unref(url);
5021 		}
5022 	}
5023 }
5024 
5025 
5026 /**
5027  * Create a right-pointing anti-aliased triangle bitmap
5028  *
5029  * \param bg background colour
5030  * \param fg foreground colour
5031  * \param size required bitmap size
5032  */
5033 static struct bitmap *
treeview_generate_triangle_bitmap(colour bg,colour fg,int size)5034 treeview_generate_triangle_bitmap(colour bg, colour fg, int size)
5035 {
5036 	struct bitmap *b = NULL;
5037 	int x, y;
5038 	unsigned char *rpos;
5039 	unsigned char *pos;
5040 	size_t stride;
5041 
5042 	/* Set up required colour graduations.  Ignores screen gamma. */
5043 	colour colour0 = bg;
5044 	colour colour1 = mix_colour(bg, fg, 255 * 3 / 4);
5045 	colour colour2 = blend_colour(bg, fg);
5046 	colour colour3 = mix_colour(bg, fg, 255 * 1 / 4);
5047 	colour colour4 = fg;
5048 
5049 	/* Create the bitmap */
5050 	b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
5051 	if (b == NULL)
5052 		return NULL;
5053 
5054 	rpos = guit->bitmap->get_buffer(b);
5055 	stride = guit->bitmap->get_rowstride(b);
5056 
5057 	/* Draw the triangle */
5058 	for (y = 0; y < size; y++) {
5059 		pos = rpos;
5060 
5061 		if (y < size / 2) {
5062 			/* Top half */
5063 			for (x = 0; x < y * 2; x++) {
5064 				*(pos++) = red_from_colour(colour4);
5065 				*(pos++) = green_from_colour(colour4);
5066 				*(pos++) = blue_from_colour(colour4);
5067 				*(pos++) = 0xff;
5068 			}
5069 			*(pos++) = red_from_colour(colour3);
5070 			*(pos++) = green_from_colour(colour3);
5071 			*(pos++) = blue_from_colour(colour3);
5072 			*(pos++) = 0xff;
5073 			*(pos++) = red_from_colour(colour1);
5074 			*(pos++) = green_from_colour(colour1);
5075 			*(pos++) = blue_from_colour(colour1);
5076 			*(pos++) = 0xff;
5077 			for (x = y * 2 + 2; x < size ; x++) {
5078 				*(pos++) = red_from_colour(colour0);
5079 				*(pos++) = green_from_colour(colour0);
5080 				*(pos++) = blue_from_colour(colour0);
5081 				*(pos++) = 0xff;
5082 			}
5083 		} else if ((y == size / 2) && (size & 0x1)) {
5084 			/* Middle row */
5085 			for (x = 0; x < size - 1; x++) {
5086 				*(pos++) = red_from_colour(colour4);
5087 				*(pos++) = green_from_colour(colour4);
5088 				*(pos++) = blue_from_colour(colour4);
5089 				*(pos++) = 0xff;
5090 			}
5091 			*(pos++) = red_from_colour(colour2);
5092 			*(pos++) = green_from_colour(colour2);
5093 			*(pos++) = blue_from_colour(colour2);
5094 			*(pos++) = 0xff;
5095 		} else {
5096 			/* Bottom half */
5097 			for (x = 0; x < (size - y - 1) * 2; x++) {
5098 				*(pos++) = red_from_colour(colour4);
5099 				*(pos++) = green_from_colour(colour4);
5100 				*(pos++) = blue_from_colour(colour4);
5101 				*(pos++) = 0xff;
5102 			}
5103 			*(pos++) = red_from_colour(colour3);
5104 			*(pos++) = green_from_colour(colour3);
5105 			*(pos++) = blue_from_colour(colour3);
5106 			*(pos++) = 0xff;
5107 			*(pos++) = red_from_colour(colour1);
5108 			*(pos++) = green_from_colour(colour1);
5109 			*(pos++) = blue_from_colour(colour1);
5110 			*(pos++) = 0xff;
5111 			for (x = (size - y) * 2; x < size ; x++) {
5112 				*(pos++) = red_from_colour(colour0);
5113 				*(pos++) = green_from_colour(colour0);
5114 				*(pos++) = blue_from_colour(colour0);
5115 				*(pos++) = 0xff;
5116 			}
5117 		}
5118 
5119 		rpos += stride;
5120 	}
5121 
5122 	guit->bitmap->modified(b);
5123 
5124 	return b;
5125 }
5126 
5127 
5128 /**
5129  * Create bitmap copy of another bitmap
5130  *
5131  * \param orig bitmap to copy
5132  * \param size required bitmap size
5133  */
5134 static struct bitmap *
treeview_generate_copy_bitmap(struct bitmap * orig,int size)5135 treeview_generate_copy_bitmap(struct bitmap *orig, int size)
5136 {
5137 	struct bitmap *b = NULL;
5138 	unsigned char *data;
5139 	unsigned char *orig_data;
5140 	size_t stride;
5141 
5142 	if (orig == NULL)
5143 		return NULL;
5144 
5145 	assert(size == guit->bitmap->get_width(orig));
5146 	assert(size == guit->bitmap->get_height(orig));
5147 
5148 	/* Create the bitmap */
5149 	b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
5150 	if (b == NULL)
5151 		return NULL;
5152 
5153 	stride = guit->bitmap->get_rowstride(b);
5154 	assert(stride == guit->bitmap->get_rowstride(orig));
5155 
5156 	data = guit->bitmap->get_buffer(b);
5157 	orig_data = guit->bitmap->get_buffer(orig);
5158 
5159 	/* Copy the bitmap */
5160 	memcpy(data, orig_data, stride * size);
5161 
5162 	guit->bitmap->modified(b);
5163 
5164 	/* We've not modified the original image, but we called
5165 	 * bitmap_get_buffer(), so we need to pair that with a
5166 	 * bitmap_modified() call to appease certain front ends. */
5167 	guit->bitmap->modified(orig);
5168 
5169 	return b;
5170 }
5171 
5172 
5173 /**
5174  * Create bitmap from rotation of another bitmap
5175  *
5176  * \param orig bitmap to create rotation of
5177  * \param size required bitmap size
5178  */
5179 static struct bitmap *
treeview_generate_rotate_bitmap(struct bitmap * orig,int size)5180 treeview_generate_rotate_bitmap(struct bitmap *orig, int size)
5181 {
5182 	struct bitmap *b = NULL;
5183 	int x, y;
5184 	unsigned char *rpos;
5185 	unsigned char *pos;
5186 	unsigned char *orig_data;
5187 	unsigned char *orig_pos;
5188 	size_t stride;
5189 
5190 	if (orig == NULL)
5191 		return NULL;
5192 
5193 	assert(size == guit->bitmap->get_width(orig));
5194 	assert(size == guit->bitmap->get_height(orig));
5195 
5196 	/* Create the bitmap */
5197 	b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
5198 	if (b == NULL)
5199 		return NULL;
5200 
5201 	stride = guit->bitmap->get_rowstride(b);
5202 	assert(stride == guit->bitmap->get_rowstride(orig));
5203 
5204 	rpos = guit->bitmap->get_buffer(b);
5205 	orig_data = guit->bitmap->get_buffer(orig);
5206 
5207 	/* Copy the rotated bitmap */
5208 	for (y = 0; y < size; y++) {
5209 		pos = rpos;
5210 
5211 		for (x = 0; x < size; x++) {
5212 			orig_pos = orig_data + x * stride + y * 4;
5213 			*(pos++) = *(orig_pos++);
5214 			*(pos++) = *(orig_pos++);
5215 			*(pos++) = *(orig_pos);
5216 			*(pos++) = 0xff;
5217 
5218 		}
5219 
5220 		rpos += stride;
5221 	}
5222 
5223 	guit->bitmap->modified(b);
5224 
5225 	/* We've not modified the original image, but we called
5226 	 * bitmap_get_buffer(), so we need to pair that with a
5227 	 * bitmap_modified() call to appease certain front ends.
5228 	 */
5229 	guit->bitmap->modified(orig);
5230 
5231 	return b;
5232 }
5233 
5234 
5235 /**
5236  * Measures width of characters used to represent treeview furniture.
5237  *
5238  * \return NSERROR_OK on success else error code
5239  */
treeview_init_furniture(void)5240 static nserror treeview_init_furniture(void)
5241 {
5242 	int size = tree_g.line_height / 2;
5243 
5244 	plot_style_odd.furn[TREE_FURN_EXPAND].size = size;
5245 	plot_style_odd.furn[TREE_FURN_EXPAND].bmp =
5246 		treeview_generate_triangle_bitmap(
5247 			plot_style_odd.bg.fill_colour,
5248 			plot_style_odd.itext.foreground, size);
5249 	plot_style_odd.furn[TREE_FURN_EXPAND].sel =
5250 		treeview_generate_triangle_bitmap(
5251 			plot_style_odd.sbg.fill_colour,
5252 			plot_style_odd.sitext.foreground, size);
5253 
5254 	plot_style_even.furn[TREE_FURN_EXPAND].size = size;
5255 	plot_style_even.furn[TREE_FURN_EXPAND].bmp =
5256 		treeview_generate_triangle_bitmap(
5257 			plot_style_even.bg.fill_colour,
5258 			plot_style_even.itext.foreground, size);
5259 	plot_style_even.furn[TREE_FURN_EXPAND].sel =
5260 		treeview_generate_copy_bitmap(
5261 			plot_style_odd.furn[TREE_FURN_EXPAND].sel, size);
5262 
5263 	plot_style_odd.furn[TREE_FURN_CONTRACT].size = size;
5264 	plot_style_odd.furn[TREE_FURN_CONTRACT].bmp =
5265 		treeview_generate_rotate_bitmap(
5266 			plot_style_odd.furn[TREE_FURN_EXPAND].bmp, size);
5267 	plot_style_odd.furn[TREE_FURN_CONTRACT].sel =
5268 		treeview_generate_rotate_bitmap(
5269 			plot_style_odd.furn[TREE_FURN_EXPAND].sel, size);
5270 
5271 	plot_style_even.furn[TREE_FURN_CONTRACT].size = size;
5272 	plot_style_even.furn[TREE_FURN_CONTRACT].bmp =
5273 		treeview_generate_rotate_bitmap(
5274 			plot_style_even.furn[TREE_FURN_EXPAND].bmp, size);
5275 	plot_style_even.furn[TREE_FURN_CONTRACT].sel =
5276 		treeview_generate_rotate_bitmap(
5277 			plot_style_even.furn[TREE_FURN_EXPAND].sel, size);
5278 
5279 	if (plot_style_odd.furn[TREE_FURN_EXPAND].bmp == NULL ||
5280 	    plot_style_odd.furn[TREE_FURN_EXPAND].sel == NULL ||
5281 	    plot_style_even.furn[TREE_FURN_EXPAND].bmp == NULL ||
5282 	    plot_style_even.furn[TREE_FURN_EXPAND].sel == NULL ||
5283 	    plot_style_odd.furn[TREE_FURN_CONTRACT].bmp == NULL ||
5284 	    plot_style_odd.furn[TREE_FURN_CONTRACT].sel == NULL ||
5285 	    plot_style_even.furn[TREE_FURN_CONTRACT].bmp == NULL ||
5286 	    plot_style_even.furn[TREE_FURN_CONTRACT].sel == NULL)
5287 		return NSERROR_NOMEM;
5288 
5289 	tree_g.furniture_width = size + tree_g.line_height / 4;
5290 
5291 	return NSERROR_OK;
5292 }
5293 
5294 
5295 /* Exported interface, documented in treeview.h */
treeview_init(void)5296 nserror treeview_init(void)
5297 {
5298 	long long font_px_size;
5299 	long long font_pt_size;
5300 	nserror res;
5301 
5302 	if (tree_g.initialised > 0) {
5303 		tree_g.initialised++;
5304 		return NSERROR_OK;
5305 	}
5306 
5307 	NSLOG(netsurf, INFO, "Initialising treeview module");
5308 
5309 	font_pt_size = nsoption_int(treeview_font_size);
5310 	if (font_pt_size <= 0) {
5311 		font_pt_size = 11 * 10;
5312 	}
5313 
5314 	font_px_size = (font_pt_size * FIXTOINT(nscss_screen_dpi) /
5315 			10 + 36) / 72;
5316 	tree_g.line_height = (font_px_size * 8 + 3) / 6;
5317 
5318 	res = treeview_init_plot_styles(font_pt_size * PLOT_STYLE_SCALE / 10);
5319 	if (res != NSERROR_OK) {
5320 		return res;
5321 	}
5322 
5323 	treeview_init_resources();
5324 
5325 	res = treeview_init_furniture();
5326 	if (res != NSERROR_OK) {
5327 		return res;
5328 	}
5329 
5330 	tree_g.step_width = tree_g.furniture_width;
5331 	tree_g.window_padding = 6;
5332 	tree_g.icon_size = 17;
5333 	tree_g.icon_step = 23;
5334 	tree_g.move_offset = 18;
5335 
5336 	tree_g.initialised++;
5337 
5338 	NSLOG(netsurf, INFO, "Initialised treeview module");
5339 
5340 	return NSERROR_OK;
5341 }
5342 
5343 
5344 /* Exported interface, documented in treeview.h */
treeview_fini(void)5345 nserror treeview_fini(void)
5346 {
5347 	int i;
5348 
5349 	if (tree_g.initialised > 1) {
5350 		tree_g.initialised--;
5351 		return NSERROR_OK;
5352 
5353 	} else if (tree_g.initialised == 0) {
5354 		NSLOG(netsurf, INFO,
5355 		      "Warning: tried to finalise uninitialised treeview module");
5356 		return NSERROR_OK;
5357 	}
5358 
5359 	NSLOG(netsurf, INFO, "Finalising treeview module");
5360 
5361 	for (i = 0; i < TREE_RES_LAST; i++) {
5362 		hlcache_handle_release(treeview_res[i].c);
5363 	}
5364 
5365 	guit->bitmap->destroy(plot_style_odd.furn[TREE_FURN_EXPAND].bmp);
5366 	guit->bitmap->destroy(plot_style_odd.furn[TREE_FURN_EXPAND].sel);
5367 	guit->bitmap->destroy(plot_style_even.furn[TREE_FURN_EXPAND].bmp);
5368 	guit->bitmap->destroy(plot_style_even.furn[TREE_FURN_EXPAND].sel);
5369 	guit->bitmap->destroy(plot_style_odd.furn[TREE_FURN_CONTRACT].bmp);
5370 	guit->bitmap->destroy(plot_style_odd.furn[TREE_FURN_CONTRACT].sel);
5371 	guit->bitmap->destroy(plot_style_even.furn[TREE_FURN_CONTRACT].bmp);
5372 	guit->bitmap->destroy(plot_style_even.furn[TREE_FURN_CONTRACT].sel);
5373 
5374 	tree_g.initialised--;
5375 
5376 	NSLOG(netsurf, INFO, "Finalised treeview module");
5377 
5378 	return NSERROR_OK;
5379 }
5380