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