Bug Summary

File:desktop/treeview.c
Warning:line 4059, column 8
Use of memory after it is freed

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