Bug Summary

File:desktop/treeview.c
Warning:line 4337, column 8
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage

Annotated Source Code

Press '?' to see keyboard shortcuts

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