diff options
Diffstat (limited to 'desktop/treeview.c')
-rw-r--r-- | desktop/treeview.c | 2535 |
1 files changed, 1855 insertions, 680 deletions
diff --git a/desktop/treeview.c b/desktop/treeview.c index 4d8fbaaeb..48422e8e3 100644 --- a/desktop/treeview.c +++ b/desktop/treeview.c @@ -22,10 +22,13 @@ * Treeview handling implementation. */ +#include <string.h> + #include "utils/utils.h" #include "utils/log.h" #include "utils/nsurl.h" #include "utils/nsoption.h" +#include "utils/config.h" #include "netsurf/bitmap.h" #include "netsurf/content.h" #include "netsurf/plotters.h" @@ -42,9 +45,17 @@ #include "desktop/treeview.h" #include "desktop/gui_internal.h" -/** @todo get rid of REDRAW_MAX -- need to be able to know window size */ +/** + * The maximum horizontal size a treeview can possibly be. + * + * \todo get rid of REDRAW_MAX -- need to be able to know window size + */ #define REDRAW_MAX 8000 + +/** + * Treeview handling global context + */ struct treeview_globals { unsigned initialised; int line_height; @@ -56,32 +67,54 @@ struct treeview_globals { int move_offset; } tree_g; + +/** + * Section type of a treeview at a point + */ enum treeview_node_part { - TV_NODE_PART_TOGGLE, /**< Expansion toggle */ - TV_NODE_PART_ON_NODE, /**< Node content (text, icon) */ - TV_NODE_PART_NONE /**< Empty area */ -}; /**< Section type of a treeview at a point */ + TV_NODE_PART_TOGGLE, /**< Expansion toggle */ + TV_NODE_PART_ON_NODE, /**< Node content (text, icon) */ + TV_NODE_PART_NONE /**< Empty area */ +}; + +/** + * Text within a treeview field or node + */ struct treeview_text { const char *data; /**< Text string */ uint32_t len; /**< Length of string in bytes */ int width; /**< Width of text in px */ }; + +/** + * a treeview field + */ struct treeview_field { + /** flags controlling how field is interpreted */ enum treeview_field_flags flags; - lwc_string *field; - struct treeview_text value; + lwc_string *field; /**< field contents */ + struct treeview_text value; /**< field text */ }; + +/** + * flags indicating render state of node. + */ enum treeview_node_flags { - TV_NFLAGS_NONE = 0, /**< No node flags set */ - TV_NFLAGS_EXPANDED = (1 << 0), /**< Whether node is expanded */ - TV_NFLAGS_SELECTED = (1 << 1), /**< Whether node is selected */ - TV_NFLAGS_SPECIAL = (1 << 2) /**< Render as special node */ + TV_NFLAGS_NONE = 0, /**< No node flags set */ + TV_NFLAGS_EXPANDED = (1 << 0), /**< Whether node is expanded */ + TV_NFLAGS_SELECTED = (1 << 1), /**< Whether node is selected */ + TV_NFLAGS_SPECIAL = (1 << 2), /**< Render as special node */ + TV_NFLAGS_MATCHED = (1 << 3), /**< Whether node matches search */ }; + +/** + * Treeview target position + */ enum treeview_target_pos { TV_TARGET_ABOVE, TV_TARGET_INSIDE, @@ -89,6 +122,10 @@ enum treeview_target_pos { TV_TARGET_NONE }; + +/** + * Treeview node + */ struct treeview_node { enum treeview_node_flags flags; /**< Node flags */ enum treeview_node_type type; /**< Node type */ @@ -96,49 +133,73 @@ struct treeview_node { int height; /**< Includes height of any descendants (pixels) */ int inset; /**< Node's inset depending on tree depth (pixels) */ - treeview_node *parent; - treeview_node *prev_sib; - treeview_node *next_sib; - treeview_node *children; + treeview_node *parent; /**< parent node */ + treeview_node *prev_sib; /**< previous sibling node */ + treeview_node *next_sib; /**< next sibling node */ + treeview_node *children; /**< first child node */ void *client_data; /**< Passed to client on node event msg callback */ struct treeview_text text; /** Text to show for node (default field) */ -}; /**< Treeview node */ +}; + +/** + * Node entry + * + * node entry contains a base node at the beginning allowing for + * trivial containerof by cast and some number of fields. + */ struct treeview_node_entry { - treeview_node base; + treeview_node base; /**< Entry class inherits node base class */ struct treeview_field fields[FLEX_ARRAY_LEN_DECL]; -}; /**< Entry class inherits node base class */ +}; + +/** + * A mouse position wrt treeview + */ struct treeview_pos { int x; /**< Mouse X coordinate */ int y; /**< Mouse Y coordinate */ int node_y; /**< Top of node at y */ int node_h; /**< Height of node at y */ -}; /**< A mouse position wrt treeview */ +}; + +/** + * Treeview drag state + */ struct treeview_drag { enum { TV_DRAG_NONE, TV_DRAG_SELECTION, TV_DRAG_MOVE, - TV_DRAG_TEXTAREA + TV_DRAG_TEXTAREA, + TV_DRAG_SEARCH, } type; /**< Drag type */ treeview_node *start_node; /**< Start node */ bool selected; /**< Start node is selected */ enum treeview_node_part part; /**< Node part at start */ struct treeview_pos start; /**< Start pos */ struct treeview_pos prev; /**< Previous pos */ -}; /**< Drag state */ +}; + +/** + * Treeview node move details + */ struct treeview_move { treeview_node *root; /**< Head of yanked node list */ treeview_node *target; /**< Move target */ struct rect target_area; /**< Pos/size of target indicator */ enum treeview_target_pos target_pos; /**< Pos wrt render node */ -}; /**< Move details */ +}; + +/** + * Treeview node edit details + */ struct treeview_edit { treeview_node *node; /**< Node being edited, or NULL */ struct textarea *textarea; /**< Textarea for edit, or NULL */ @@ -147,22 +208,39 @@ struct treeview_edit { int y; /**< Textarea y position */ int w; /**< Textarea width */ int h; /**< Textarea height */ -}; /**< Edit details */ +}; + +/** + * Treeview search box details + */ +struct treeview_search { + struct textarea *textarea; /**< Search box. */ + bool active; /**< Whether the search box has focus. */ + bool search; /**< Whether we have a search term. */ + int height; /**< Current search display height. */ +}; + + +/** + * The treeview context + */ struct treeview { - uint32_t view_width; /**< Viewport size */ + uint32_t view_width; /**< Viewport horizontal size */ - treeview_flags flags; /**< Treeview behaviour settings */ + treeview_flags flags; /**< Treeview behaviour settings */ - treeview_node *root; /**< Root node */ + treeview_node *root; /**< Root node */ struct treeview_field *fields; /**< Array of fields */ int n_fields; /**< fields[n_fields] is folder, lower are entry fields */ int field_width; /**< Max width of shown field names */ - struct treeview_drag drag; /**< Drag state */ - struct treeview_move move; /**< Move drag details */ - struct treeview_edit edit; /**< Edit details */ + struct treeview_drag drag; /**< Drag state */ + struct treeview_move move; /**< Move drag details */ + struct treeview_edit edit; /**< Edit details */ + + struct treeview_search search; /**< Treeview search box */ const struct treeview_callback_table *callbacks; /**< For node events */ @@ -171,38 +249,62 @@ struct treeview { }; +/** + * Treeview furniture states. + */ enum treeview_furniture_id { TREE_FURN_EXPAND = 0, TREE_FURN_CONTRACT, TREE_FURN_LAST }; -struct treeview_furniture { - int size; - struct bitmap *bmp; - struct bitmap *sel; -}; + +/** + * style for a node + */ struct treeview_node_style { - plot_style_t bg; /**< Background */ - plot_font_style_t text; /**< Text */ - plot_font_style_t itext; /**< Entry field text */ + plot_style_t bg; /**< Background */ + plot_font_style_t text; /**< Text */ + plot_font_style_t itext; /**< Entry field text */ + + plot_style_t sbg; /**< Selected background */ + plot_font_style_t stext; /**< Selected text */ + plot_font_style_t sitext; /**< Selected entry field text */ + + struct { + int size; + struct bitmap *bmp; + struct bitmap *sel; + } furn[TREE_FURN_LAST]; +}; - plot_style_t sbg; /**< Selected background */ - plot_font_style_t stext; /**< Selected text */ - plot_font_style_t sitext; /**< Selected entry field text */ - struct treeview_furniture furn[TREE_FURN_LAST]; -}; +/** + * Plot style for odd rows + */ +struct treeview_node_style plot_style_odd; + + +/** + * Plot style for even rows + */ +struct treeview_node_style plot_style_even; -struct treeview_node_style plot_style_odd; /**< Plot style for odd rows */ -struct treeview_node_style plot_style_even; /**< Plot style for even rows */ +/** + * Treeview content resource data + */ struct treeview_resource { const char *url; struct hlcache_handle *c; int height; bool ready; -}; /**< Treeview content resource data */ +}; + + +/** + * treeview resource indexes + */ enum treeview_resource_id { TREE_RES_ARROW = 0, TREE_RES_CONTENT, @@ -211,27 +313,46 @@ enum treeview_resource_id { TREE_RES_SEARCH, TREE_RES_LAST }; + + +/** + * Treeview content resources + */ static struct treeview_resource treeview_res[TREE_RES_LAST] = { { "resource:icons/arrow-l.png", NULL, 0, false }, { "resource:icons/content.png", NULL, 0, false }, { "resource:icons/directory.png", NULL, 0, false }, { "resource:icons/directory2.png", NULL, 0, false }, { "resource:icons/search.png", NULL, 0, false } -}; /**< Treeview content resources */ +}; + + +/** + * Get the display height of the treeview data component of the display. + * + * \param[in] tree Treeview to get the height of. + * \return the display height in pixels. + */ +static inline int treeview__get_display_height(const treeview *tree) +{ + return (tree->search.search == false) ? + tree->root->height : + tree->search.height; +} /** * Corewindow callback wrapper: Request a redraw of the window * - * \param[in] cw the core window object + * \param[in] tree The treeview to request redraw on. * \param[in] r rectangle to redraw */ -static inline void treeview__cw_redraw_request( +static inline void treeview__cw_invalidate_area( const struct treeview *tree, const struct rect *r) { if (tree->cw_t != NULL) { - tree->cw_t->redraw_request(tree->cw_h, r); + tree->cw_t->invalidate(tree->cw_h, r); } } @@ -239,16 +360,41 @@ static inline void treeview__cw_redraw_request( /** * Corewindow callback wrapper: Update the limits of the window * - * \param[in] cw the core window object + * \param[in] tree The treeview to update size for. * \param[in] width the width in px, or negative if don't care * \param[in] height the height in px, or negative if don't care */ static inline void treeview__cw_update_size( - const struct treeview *tree, - int width, int height) + const struct treeview *tree, + int width, int height) { + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + if (tree->cw_t != NULL) { - tree->cw_t->update_size(tree->cw_h, width, height); + tree->cw_t->update_size(tree->cw_h, width, + height + search_height); + } +} + + +/** + * Corewindow callback_wrapper: Scroll to top of window. + * + * \param[in] tree The treeview to scroll. + */ +static inline void treeview__cw_scroll_top( + const struct treeview *tree) +{ + struct rect r = { + .x0 = 0, + .y0 = 0, + .x1 = tree_g.window_padding, + .y1 = tree_g.line_height, + }; + + if (tree->cw_t != NULL) { + tree->cw_t->scroll_visible(tree->cw_h, &r); } } @@ -256,13 +402,13 @@ static inline void treeview__cw_update_size( /** * Corewindow callback wrapper: Get window viewport dimensions * - * \param[in] cw the core window object + * \param[in] tree The treeview to get dimensions for. * \param[out] width to be set to viewport width in px * \param[out] height to be set to viewport height in px */ static inline void treeview__cw_get_window_dimensions( - const struct treeview *tree, - int *width, int *height) + const struct treeview *tree, + int *width, int *height) { if (tree->cw_t != NULL) { tree->cw_t->get_window_dimensions(tree->cw_h, width, height); @@ -273,12 +419,12 @@ static inline void treeview__cw_get_window_dimensions( /** * Corewindow callback wrapper: Inform corewindow owner of drag status * - * \param[in] cw the core window object + * \param[in] tree The treeview to report status on. * \param[in] ds the current drag status */ static inline void treeview__cw_drag_status( - const struct treeview *tree, - core_window_drag_status ds) + const struct treeview *tree, + core_window_drag_status ds) { if (tree->cw_t != NULL) { tree->cw_t->drag_status(tree->cw_h, ds); @@ -286,15 +432,16 @@ static inline void treeview__cw_drag_status( } -/* Helper function to access the given field of a node +/** + * Helper function to access the given field of a node * - * \param tree Treeview that node belongs to - * \param n Node to get field from - * \param i Index of field of interest + * \param tree Treeview that node belongs to + * \param n Node to get field from + * \param i Index of field of interest * \return text entry for field or NULL. */ -static inline struct treeview_text * treeview_get_text_for_field( - treeview *tree, treeview_node *n, int i) +static inline struct treeview_text * +treeview_get_text_for_field(treeview *tree, treeview_node *n, int i) { if (i == 0) { return &n->text; @@ -309,19 +456,19 @@ static inline struct treeview_text * treeview_get_text_for_field( } -/* Find the next node in depth first tree order +/** + * Find the next node in depth first tree order * - * \param node Start node - * \param full Iff true, visit children of collapsed nodes - * \param next Updated to next node, or NULL if 'node' is last node - * \return NSERROR_OK on success, or appropriate error otherwise + * \param node Start node + * \param full Iff true, visit children of collapsed nodes + * \return next node, or NULL if \a node is last node */ static inline treeview_node * treeview_node_next(treeview_node *node, bool full) { assert(node != NULL); if ((full || (node->flags & TV_NFLAGS_EXPANDED)) && - node->children != NULL) { + node->children != NULL) { /* Next node is child */ node = node->children; } else { @@ -345,17 +492,17 @@ static inline treeview_node * treeview_node_next(treeview_node *node, bool full) } -/* Find node at given y-position +/** + * Find node at given y-position * - * \param tree Treeview object to delete node from - * \param target_y Target y-position + * \param tree Treeview object to delete node from + * \param target_y Target y-position * \return node at y_target */ static treeview_node * treeview_y_node(treeview *tree, int target_y) { + int y = (tree->flags & TREEVIEW_SEARCHABLE) ? tree_g.line_height : 0; treeview_node *n; - int y = 0; - int h; assert(tree != NULL); assert(tree->root != NULL); @@ -363,8 +510,8 @@ static treeview_node * treeview_y_node(treeview *tree, int target_y) n = treeview_node_next(tree->root, false); while (n != NULL) { - h = (n->type == TREE_NODE_ENTRY) ? - n->height : tree_g.line_height; + int h = (n->type == TREE_NODE_ENTRY) ? + n->height : tree_g.line_height; if (target_y >= y && target_y < y + h) return n; y += h; @@ -376,16 +523,19 @@ static treeview_node * treeview_y_node(treeview *tree, int target_y) } -/* Find y position of the top of a node +/** + * Find y position of the top of a node * - * \param tree Treeview object to delete node from - * \param node Node to get position of + * \param tree Treeview object to delete node from + * \param node Node to get position of * \return node's y position */ -static int treeview_node_y(treeview *tree, treeview_node *node) +static int treeview_node_y( + const treeview *tree, + const treeview_node *node) { treeview_node *n; - int y = 0; + int y = (tree->flags & TREEVIEW_SEARCHABLE) ? tree_g.line_height : 0; assert(tree != NULL); assert(tree->root != NULL); @@ -394,7 +544,7 @@ static int treeview_node_y(treeview *tree, treeview_node *node) while (n != NULL && n != node) { y += (n->type == TREE_NODE_ENTRY) ? - n->height : tree_g.line_height; + n->height : tree_g.line_height; n = treeview_node_next(n, false); } @@ -403,40 +553,108 @@ static int treeview_node_y(treeview *tree, treeview_node *node) } -/* Walk a treeview subtree, calling a callback at each node (depth first) +/** + * Redraw tree from given node to the bottom. + * + * \param[in] tree Tree to redraw from node in. + * \param[in] node Node to redraw from. + */ +static void treeview__redraw_from_node( + const treeview *tree, + const treeview_node *node) +{ + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + struct rect r = { + .x0 = 0, + .y0 = treeview_node_y(tree, node), + .x1 = REDRAW_MAX, + .y1 = treeview__get_display_height(tree) + search_height, + }; + + assert(tree != NULL); + + treeview__cw_invalidate_area(tree, &r); +} + + +/** + * The treeview walk mode. Controls which nodes are visited in a walk. + */ +enum treeview_walk_mode { + /** + * Walk to all nodes in the (sub)tree. + */ + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, + + /** + * Walk to expanded nodes in the (sub)tree only. Children of + * collapsed nodes are not visited. + */ + TREEVIEW_WALK_MODE_LOGICAL_EXPANDED, + + /** + * Walk displayed nodes. This differs from the + * `TREEVIEW_WALK_MODE_LOGICAL_EXPANDED` mode when there is + * an active search filter display. + */ + TREEVIEW_WALK_MODE_DISPLAY, +}; + + +/** + * Walk a treeview subtree, calling a callback at each node (depth first) * + * \param tree Treeview being walked. * \param root Root to walk tree from (doesn't get a callback call) - * \param full Iff true, visit children of collapsed nodes + * \param mode The treeview walk mode to use. * \param callback_bwd Function to call on each node in backwards order * \param callback_fwd Function to call on each node in forwards order * \param ctx Context to pass to callback * \return NSERROR_OK on success, or appropriate error otherwise * - * Note: Any node deletion must happen in callback_bwd. + * \note Any node deletion must happen in callback_bwd. */ -static nserror treeview_walk_internal(treeview_node *root, bool full, - nserror (*callback_bwd)(treeview_node *n, void *ctx, bool *end), - nserror (*callback_fwd)(treeview_node *n, void *ctx, - bool *skip_children, bool *end), +static nserror treeview_walk_internal( + treeview *tree, + treeview_node *root, + enum treeview_walk_mode mode, + nserror (*callback_bwd)( + treeview_node *n, + void *ctx, + bool *end), + nserror (*callback_fwd)( + treeview_node *n, + void *ctx, + bool *skip_children, + bool *end), void *ctx) { treeview_node *node, *child, *parent, *next_sibling; - bool abort = false; + bool walking_search = (mode == TREEVIEW_WALK_MODE_DISPLAY && + tree->search.search == true); bool skip_children = false; + bool abort = false; + bool full = false; nserror err; + bool entry; assert(root != NULL); + if (mode == TREEVIEW_WALK_MODE_LOGICAL_COMPLETE || walking_search) { + /* We need to visit children of collapsed folders. */ + full = true; + } + node = root; parent = node->parent; next_sibling = node->next_sib; - child = (!skip_children && - (full || (node->flags & TV_NFLAGS_EXPANDED))) ? - node->children : NULL; + child = (full || (node->flags & TV_NFLAGS_EXPANDED)) ? + node->children : NULL; while (node != NULL) { - if (child != NULL) { + if (child != NULL && !skip_children) { /* Down to children */ node = child; } else { @@ -444,9 +662,10 @@ static nserror treeview_walk_internal(treeview_node *root, bool full, * go to next sibling if present, or nearest ancestor * with a next sibling. */ - while (node != root && - next_sibling == NULL) { - if (callback_bwd != NULL) { + while (node != root && next_sibling == NULL) { + entry = (node->type == TREE_NODE_ENTRY); + if (callback_bwd != NULL && + (entry || !walking_search)) { /* Backwards callback */ err = callback_bwd(node, ctx, &abort); @@ -486,10 +705,17 @@ static nserror treeview_walk_internal(treeview_node *root, bool full, assert(node != NULL); assert(node != root); + entry = (node->type == TREE_NODE_ENTRY); + parent = node->parent; next_sibling = node->next_sib; child = (full || (node->flags & TV_NFLAGS_EXPANDED)) ? - node->children : NULL; + node->children : NULL; + + if (walking_search && (!entry || + !(node->flags & TV_NFLAGS_MATCHED))) { + continue; + } if (callback_fwd != NULL) { /* Forwards callback */ @@ -503,16 +729,251 @@ static nserror treeview_walk_internal(treeview_node *root, bool full, return NSERROR_OK; } } - child = skip_children ? NULL : child; } return NSERROR_OK; } /** + * Data used when doing a treeview walk for search. + */ +struct treeview_search_walk_data { + treeview *tree; /**< The treeview to search. */ + const char *text; /**< The string being searched for. */ + const unsigned int len; /**< Length of string being searched for. */ + int window_height; /**< Accumulate height for matching entries. */ +}; + + +/** + * Treewalk node callback for handling search. + * + * \param[in] n Current node. + * \param[in] ctx Treeview search context. + * \param[in,out] skip_children Flag to allow children to be skipped. + * \param[in,out] end Flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror treeview__search_walk_cb( + treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) +{ + struct treeview_search_walk_data *sw = ctx; + + if (n->type != TREE_NODE_ENTRY) { + return NSERROR_OK; + } + + if (sw->len == 0) { + n->flags &= ~TV_NFLAGS_MATCHED; + } else { + struct treeview_node_entry *entry = + (struct treeview_node_entry *)n; + bool matched = false; + + for (int i = 0; i < sw->tree->n_fields; i++) { + struct treeview_field *ef = &(sw->tree->fields[i + 1]); + if (ef->flags & TREE_FLAG_SEARCHABLE) { + if (strcasestr(entry->fields[i].value.data, + sw->text) != NULL) { + matched = true; + break; + } + } + } + + if (!matched && strcasestr(n->text.data, sw->text) != NULL) { + matched = true; + } + + if (matched) { + n->flags |= TV_NFLAGS_MATCHED; + sw->window_height += n->height; + } else { + n->flags &= ~TV_NFLAGS_MATCHED; + } + } + + return NSERROR_OK; +} + + +/** + * Search treeview for text. + * + * \param[in] tree Treeview to search. + * \param[in] text UTF-8 string to search for. (NULL-terminated.) + * \param[in] len Byte length of UTF-8 string. + * \return NSERROR_OK on success, appropriate error otherwise. + */ +static nserror treeview__search( + treeview *tree, + const char *text, + unsigned int len) +{ + nserror err; + uint32_t height; + uint32_t prev_height = treeview__get_display_height(tree); + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + struct treeview_search_walk_data sw = { + .len = len, + .text = text, + .tree = tree, + .window_height = 0, + }; + struct rect r = { + .x0 = 0, + .y0 = search_height, + .x1 = REDRAW_MAX, + }; + + assert(text[len] == '\0'); + + if (tree->root == NULL) { + return NSERROR_OK; + } + + err = treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, NULL, + treeview__search_walk_cb, &sw); + if (err != NSERROR_OK) { + return err; + } + + if (len > 0) { + tree->search.height = sw.window_height; + tree->search.search = true; + height = sw.window_height; + } else { + tree->search.search = false; + height = tree->root->height; + } + + r.y1 = ((height > prev_height) ? height : prev_height) + search_height; + treeview__cw_invalidate_area(tree, &r); + treeview__cw_update_size(tree, -1, height); + treeview__cw_scroll_top(tree); + + return NSERROR_OK; +} + + +/** + * Cancel a treeview search, optionally droping focus from search widget. + * + * \param[in] tree Treeview to cancel search in. + * \param[in] drop_focus Iff true, drop input focus from search widget. + */ +static void treeview__search_cancel(treeview *tree, bool drop_focus) +{ + struct rect r = { + .x0 = tree_g.window_padding + tree_g.icon_size, + .x1 = 600, + .y0 = 0, + .y1 = tree_g.line_height, + }; + + tree->search.search = false; + if (tree->search.active == false) { + return; + } + + if (drop_focus) { + tree->search.active = false; + textarea_set_caret(tree->search.textarea, -1); + } else { + textarea_set_caret(tree->search.textarea, 0); + } + + textarea_set_text(tree->search.textarea, ""); + treeview__cw_invalidate_area(tree, &r); +} + + +/** + * Callback for textarea_create, in desktop/treeview.h + * + * \param data treeview context + * \param msg textarea message + */ +static void treeview_textarea_search_callback(void *data, + struct textarea_msg *msg) +{ + treeview *tree = data; + struct rect *r; + + if (tree->search.active == false || tree->root == NULL) { + return; + } + + switch (msg->type) { + case TEXTAREA_MSG_DRAG_REPORT: + if (msg->data.drag == TEXTAREA_DRAG_NONE) { + /* Textarea drag finished */ + tree->drag.type = TV_DRAG_NONE; + } else { + /* Textarea drag started */ + tree->drag.type = TV_DRAG_SEARCH; + } + treeview__cw_drag_status(tree, tree->drag.type); + break; + + case TEXTAREA_MSG_REDRAW_REQUEST: + r = &msg->data.redraw; + r->x0 += tree_g.window_padding + tree_g.icon_size; + r->y0 += 0; + r->x1 += 600; + r->y1 += tree_g.line_height; + + /* Redraw the textarea */ + treeview__cw_invalidate_area(tree, r); + break; + + case TEXTAREA_MSG_TEXT_MODIFIED: + /* Textarea length includes trailing NULL, so subtract it. */ + treeview__search(tree, + msg->data.modified.text, + msg->data.modified.len - 1); + break; + + default: + break; + } +} + + +/** + * Update the layout for any active search. + * + * \param[in] tree The tree to update. + */ +static void treeview__search_update_display( + treeview *tree) +{ + const char *string; + unsigned int len; + + if (tree->search.search == false) { + /* No active search to update view for. */ + return; + } + + string = textarea_data(tree->search.textarea, &len); + if (string == NULL || len == 0) { + return; + } + + treeview__search(tree, string, len - 1); +} + + +/** * Create treeview's root node * - * \param root Returns root node + * \param[out] root Returns root node * \return NSERROR_OK on success, appropriate error otherwise */ static nserror treeview_create_node_root(treeview_node **root) @@ -548,10 +1009,20 @@ static nserror treeview_create_node_root(treeview_node **root) /** - * Set a node's inset from its parent (can be used as treeview walk callback) + * Set a node's inset from its parent + * + * This may be used as treeview walk callback + * + * \param[in] n node to set inset on + * \param[in] ctx context unused + * \param[out] skip_children set to false so child nodes are not skipped. + * \param[out] end unused flag so treewalk in not terminated early. */ -static nserror treeview_set_inset_from_parent(treeview_node *n, void *ctx, - bool *skip_children, bool *end) +static nserror +treeview_set_inset_from_parent(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { if (n->parent != NULL) n->inset = n->parent->inset + tree_g.step_width; @@ -559,14 +1030,20 @@ static nserror treeview_set_inset_from_parent(treeview_node *n, void *ctx, *skip_children = false; return NSERROR_OK; } + + /** * Insert a treeview node into a treeview * - * \param a parentless node to insert - * \param b tree node to insert a as a relation of - * \param rel a's relationship to b + * \param tree the treeview to insert node into. + * \param a parentless node to insert + * \param b tree node to insert a as a relation of + * \param rel The relationship between \a a and \a b */ -static inline void treeview_insert_node(treeview_node *a, +static inline void +treeview_insert_node( + treeview *tree, + treeview_node *a, treeview_node *b, enum treeview_relationship rel) { @@ -603,7 +1080,8 @@ static inline void treeview_insert_node(treeview_node *a, a->inset = a->parent->inset + tree_g.step_width; if (a->children != NULL) { - treeview_walk_internal(a, true, NULL, + treeview_walk_internal(tree, a, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, NULL, treeview_set_inset_from_parent, NULL); } @@ -627,12 +1105,14 @@ static inline void treeview_insert_node(treeview_node *a, /* Exported interface, documented in treeview.h */ -nserror treeview_create_node_folder(treeview *tree, - treeview_node **folder, - treeview_node *relation, - enum treeview_relationship rel, - const struct treeview_field_data *field, - void *data, treeview_node_options_flags flags) +nserror +treeview_create_node_folder(treeview *tree, + treeview_node **folder, + treeview_node *relation, + enum treeview_relationship rel, + const struct treeview_field_data *field, + void *data, + treeview_node_options_flags flags) { treeview_node *n; @@ -651,7 +1131,7 @@ nserror treeview_create_node_folder(treeview *tree, } n->flags = (flags & TREE_OPTION_SPECIAL_DIR) ? - TV_NFLAGS_SPECIAL : TV_NFLAGS_NONE; + TV_NFLAGS_SPECIAL : TV_NFLAGS_NONE; n->type = TREE_NODE_FOLDER; n->height = tree_g.line_height; @@ -667,13 +1147,13 @@ nserror treeview_create_node_folder(treeview *tree, n->client_data = data; - treeview_insert_node(n, relation, rel); + treeview_insert_node(tree, n, relation, rel); if (n->parent->flags & TV_NFLAGS_EXPANDED) { /* Inform front end of change in dimensions */ if (!(flags & TREE_OPTION_SUPPRESS_RESIZE)) treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); /* Redraw */ if (!(flags & TREE_OPTION_SUPPRESS_REDRAW)) { @@ -682,7 +1162,7 @@ nserror treeview_create_node_folder(treeview *tree, r.y0 = treeview_node_y(tree, n); r.x1 = REDRAW_MAX; r.y1 = tree->root->height; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } } @@ -693,10 +1173,11 @@ nserror treeview_create_node_folder(treeview *tree, /* Exported interface, documented in treeview.h */ -nserror treeview_update_node_folder(treeview *tree, - treeview_node *folder, - const struct treeview_field_data *field, - void *data) +nserror +treeview_update_node_folder(treeview *tree, + treeview_node *folder, + const struct treeview_field_data *field, + void *data) { bool match; @@ -708,8 +1189,8 @@ nserror treeview_update_node_folder(treeview *tree, assert(field != NULL); assert(lwc_string_isequal(tree->fields[tree->n_fields].field, - field->field, &match) == lwc_error_ok && - match == true); + field->field, &match) == lwc_error_ok && + match == true); folder->text.data = field->value; folder->text.len = field->value_len; folder->text.width = 0; @@ -717,9 +1198,9 @@ nserror treeview_update_node_folder(treeview *tree, if (folder->parent->flags & TV_NFLAGS_EXPANDED) { /* Text will be seen, get its width */ guit->layout->width(&plot_style_odd.text, - folder->text.data, - folder->text.len, - &(folder->text.width)); + folder->text.data, + folder->text.len, + &(folder->text.width)); } else { /* Just invalidate the width, since it's not needed now */ folder->text.width = 0; @@ -732,7 +1213,7 @@ nserror treeview_update_node_folder(treeview *tree, r.y0 = treeview_node_y(tree, folder); r.x1 = REDRAW_MAX; r.y1 = r.y0 + tree_g.line_height; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } return NSERROR_OK; @@ -740,10 +1221,11 @@ nserror treeview_update_node_folder(treeview *tree, /* Exported interface, documented in treeview.h */ -nserror treeview_update_node_entry(treeview *tree, - treeview_node *entry, - const struct treeview_field_data fields[], - void *data) +nserror +treeview_update_node_entry(treeview *tree, + treeview_node *entry, + const struct treeview_field_data fields[], + void *data) { bool match; struct treeview_node_entry *e = (struct treeview_node_entry *)entry; @@ -758,8 +1240,8 @@ nserror treeview_update_node_entry(treeview *tree, assert(fields != NULL); assert(fields[0].field != NULL); assert(lwc_string_isequal(tree->fields[0].field, - fields[0].field, &match) == lwc_error_ok && - match == true); + fields[0].field, &match) == lwc_error_ok && + match == true); entry->text.data = fields[0].value; entry->text.len = fields[0].value_len; entry->text.width = 0; @@ -767,9 +1249,9 @@ nserror treeview_update_node_entry(treeview *tree, if (entry->parent->flags & TV_NFLAGS_EXPANDED) { /* Text will be seen, get its width */ guit->layout->width(&plot_style_odd.text, - entry->text.data, - entry->text.len, - &(entry->text.width)); + entry->text.data, + entry->text.len, + &(entry->text.width)); } else { /* Just invalidate the width, since it's not needed now */ entry->text.width = 0; @@ -778,8 +1260,8 @@ nserror treeview_update_node_entry(treeview *tree, for (i = 1; i < tree->n_fields; i++) { assert(fields[i].field != NULL); assert(lwc_string_isequal(tree->fields[i].field, - fields[i].field, &match) == lwc_error_ok && - match == true); + fields[i].field, &match) == lwc_error_ok && + match == true); e->fields[i - 1].value.data = fields[i].value; e->fields[i - 1].value.len = fields[i].value_len; @@ -787,15 +1269,17 @@ nserror treeview_update_node_entry(treeview *tree, if (entry->flags & TV_NFLAGS_EXPANDED) { /* Text will be seen, get its width */ guit->layout->width(&plot_style_odd.text, - e->fields[i - 1].value.data, - e->fields[i - 1].value.len, - &(e->fields[i - 1].value.width)); + e->fields[i - 1].value.data, + e->fields[i - 1].value.len, + &(e->fields[i - 1].value.width)); } else { /* Invalidate the width, since it's not needed yet */ e->fields[i - 1].value.width = 0; } } + treeview__search_update_display(tree); + /* Redraw */ if (entry->parent->flags & TV_NFLAGS_EXPANDED) { struct rect r; @@ -803,7 +1287,7 @@ nserror treeview_update_node_entry(treeview *tree, r.y0 = treeview_node_y(tree, entry); r.x1 = REDRAW_MAX; r.y1 = r.y0 + entry->height; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } return NSERROR_OK; @@ -811,12 +1295,14 @@ nserror treeview_update_node_entry(treeview *tree, /* Exported interface, documented in treeview.h */ -nserror treeview_create_node_entry(treeview *tree, - treeview_node **entry, - treeview_node *relation, - enum treeview_relationship rel, - const struct treeview_field_data fields[], - void *data, treeview_node_options_flags flags) +nserror +treeview_create_node_entry(treeview *tree, + treeview_node **entry, + treeview_node *relation, + enum treeview_relationship rel, + const struct treeview_field_data fields[], + void *data, + treeview_node_options_flags flags) { bool match; struct treeview_node_entry *e; @@ -873,13 +1359,13 @@ nserror treeview_create_node_entry(treeview *tree, e->fields[i - 1].value.width = 0; } - treeview_insert_node(n, relation, rel); + treeview_insert_node(tree, n, relation, rel); if (n->parent->flags & TV_NFLAGS_EXPANDED) { /* Inform front end of change in dimensions */ if (!(flags & TREE_OPTION_SUPPRESS_RESIZE)) treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); /* Redraw */ if (!(flags & TREE_OPTION_SUPPRESS_REDRAW)) { @@ -888,25 +1374,42 @@ nserror treeview_create_node_entry(treeview *tree, r.y0 = treeview_node_y(tree, n); r.x1 = REDRAW_MAX; r.y1 = tree->root->height; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } } + treeview__search_update_display(tree); + *entry = n; return NSERROR_OK; } +/** + * Treewalk iterator context + */ struct treeview_walk_ctx { treeview_walk_cb enter_cb; treeview_walk_cb leave_cb; void *ctx; enum treeview_node_type type; }; -/** Treewalk node enter callback. */ -static nserror treeview_walk_fwd_cb(treeview_node *n, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node enter callback. + * + * \param n current node + * \param ctx treewalk context + * \param skip_children set if child nodes should be skipped + * \param end set if iteration should end early + */ +static nserror +treeview_walk_fwd_cb(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_walk_ctx *tw = ctx; @@ -916,7 +1419,15 @@ static nserror treeview_walk_fwd_cb(treeview_node *n, void *ctx, return NSERROR_OK; } -/** Treewalk node leave callback. */ + + +/** + * Treewalk node leave callback. + * + * \param n current node + * \param ctx treewalk context + * \param end set if iteration should end early + */ static nserror treeview_walk_bwd_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_walk_ctx *tw = ctx; @@ -927,10 +1438,16 @@ static nserror treeview_walk_bwd_cb(treeview_node *n, void *ctx, bool *end) return NSERROR_OK; } + + /* Exported interface, documented in treeview.h */ -nserror treeview_walk(treeview *tree, treeview_node *root, - treeview_walk_cb enter_cb, treeview_walk_cb leave_cb, - void *ctx, enum treeview_node_type type) +nserror +treeview_walk(treeview *tree, + treeview_node *root, + treeview_walk_cb enter_cb, + treeview_walk_cb leave_cb, + void *ctx, + enum treeview_node_type type) { struct treeview_walk_ctx tw = { .enter_cb = enter_cb, @@ -945,16 +1462,18 @@ nserror treeview_walk(treeview *tree, treeview_node *root, if (root == NULL) root = tree->root; - return treeview_walk_internal(root, true, + return treeview_walk_internal(tree, root, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, (leave_cb != NULL) ? treeview_walk_bwd_cb : NULL, - (enter_cb != NULL) ? treeview_walk_fwd_cb : NULL, &tw); + (enter_cb != NULL) ? treeview_walk_fwd_cb : NULL, + &tw); } /** * Unlink a treeview node * - * \param n Node to unlink + * \param n Node to unlink * \return true iff ancestor heights need to be reduced */ static inline bool treeview_unlink_node(treeview_node *n) @@ -975,7 +1494,8 @@ static inline bool treeview_unlink_node(treeview_node *n) } /* Reduce ancestor heights */ - if (n->parent != NULL && n->parent->flags & TV_NFLAGS_EXPANDED) { + if ((n->parent != NULL) && + (n->parent->flags & TV_NFLAGS_EXPANDED)) { return true; } @@ -986,8 +1506,8 @@ static inline bool treeview_unlink_node(treeview_node *n) /** * Cancel the editing of a treeview node * - * \param tree Treeview object to cancel node editing in - * \param redraw Set true iff redraw of removed textarea area required + * \param tree Treeview object to cancel node editing in + * \param redraw Set true iff redraw of removed textarea area required */ static void treeview_edit_cancel(treeview *tree, bool redraw) { @@ -1009,15 +1529,17 @@ static void treeview_edit_cancel(treeview *tree, bool redraw) r.y0 = tree->edit.y; r.x1 = tree->edit.x + tree->edit.w; r.y1 = tree->edit.y + tree->edit.h; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } } /** - * Complete a treeview edit, by informing the client with a change request msg + * Complete a treeview edit + * + * Complete edit by informing the client with a change request msg * - * \param tree Treeview object to complete edit in + * \param tree Treeview object to complete edit in */ static void treeview_edit_done(treeview *tree) { @@ -1027,8 +1549,9 @@ static void treeview_edit_done(treeview *tree) struct treeview_node_msg msg; msg.msg = TREE_MSG_NODE_EDIT; - if (tree->edit.textarea == NULL) + if (tree->edit.textarea == NULL) { return; + } assert(n != NULL); @@ -1066,7 +1589,6 @@ static void treeview_edit_done(treeview *tree) break; } - /* Finished with the new text */ free(new_text); @@ -1075,17 +1597,25 @@ static void treeview_edit_done(treeview *tree) } +/** + * context for treeview node deletion iterator + */ struct treeview_node_delete { treeview *tree; int h_reduction; bool user_interaction; }; -/** Treewalk node callback deleting nodes. */ -static nserror treeview_delete_node_walk_cb(treeview_node *n, - void *ctx, bool *end) + + +/** + * Treewalk node callback deleting nodes. + */ +static nserror +treeview_delete_node_walk_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_node_delete *nd = (struct treeview_node_delete *)ctx; struct treeview_node_msg msg; + msg.msg = TREE_MSG_NODE_DELETE; msg.data.delete.user = nd->user_interaction; @@ -1093,51 +1623,60 @@ static nserror treeview_delete_node_walk_cb(treeview_node *n, if (treeview_unlink_node(n)) nd->h_reduction += (n->type == TREE_NODE_ENTRY) ? - n->height : tree_g.line_height; + n->height : tree_g.line_height; /* Handle any special treatment */ switch (n->type) { case TREE_NODE_ENTRY: nd->tree->callbacks->entry(msg, n->client_data); break; + case TREE_NODE_FOLDER: nd->tree->callbacks->folder(msg, n->client_data); break; + case TREE_NODE_ROOT: break; + default: return NSERROR_BAD_PARAMETER; } /* Cancel any edit of this node */ if (nd->tree->edit.textarea != NULL && - nd->tree->edit.node == n) + nd->tree->edit.node == n) { treeview_edit_cancel(nd->tree, false); + } /* Free the node */ free(n); return NSERROR_OK; } + + /** * Delete a treeview node * - * \param tree Treeview object to delete node from - * \param n Node to delete - * \param interaction Delete is result of user interaction with treeview - * \param flags Treeview node options flags - * \return NSERROR_OK on success, appropriate error otherwise - * * Will emit folder or entry deletion msg callback. * - * Note this can be called from inside a treeview_walk fwd callback. + * \note this can be called from inside a treeview_walk fwd callback. * For example walking the tree and calling this for any node that's selected. * * This function does not delete empty nodes, so if TREEVIEW_DEL_EMPTY_DIRS is * set, caller must also call treeview_delete_empty. + * + * \param tree Treeview object to delete node from + * \param n Node to delete + * \param interaction Delete is result of user interaction with treeview + * \param flags Treeview node options flags + * \return NSERROR_OK on success, appropriate error otherwise */ -static nserror treeview_delete_node_internal(treeview *tree, treeview_node *n, - bool interaction, treeview_node_options_flags flags) +static nserror +treeview_delete_node_internal(treeview *tree, + treeview_node *n, + bool interaction, + treeview_node_options_flags flags) { nserror err; treeview_node *p = n->parent; @@ -1152,8 +1691,9 @@ static nserror treeview_delete_node_internal(treeview *tree, treeview_node *n, } /* Delete any children first */ - err = treeview_walk_internal(n, true, treeview_delete_node_walk_cb, - NULL, &nd); + err = treeview_walk_internal(tree, n, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, + treeview_delete_node_walk_cb, NULL, &nd); if (err != NSERROR_OK) { return err; } @@ -1175,12 +1715,14 @@ static nserror treeview_delete_node_internal(treeview *tree, treeview_node *n, /* Inform front end of change in dimensions */ if (tree->root != NULL && p != NULL && p->flags & TV_NFLAGS_EXPANDED && - nd.h_reduction > 0 && - !(flags & TREE_OPTION_SUPPRESS_RESIZE)) { + nd.h_reduction > 0 && + !(flags & TREE_OPTION_SUPPRESS_RESIZE)) { treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); } + treeview__search_update_display(tree); + return NSERROR_OK; } @@ -1188,8 +1730,8 @@ static nserror treeview_delete_node_internal(treeview *tree, treeview_node *n, /** * Delete any empty treeview folder nodes * - * \param tree Treeview object to delete empty nodes from - * \param interaction Delete is result of user interaction with treeview + * \param tree Treeview object to delete empty nodes from + * \param interaction Delete is result of user interaction with treeview * \return NSERROR_OK on success, appropriate error otherwise * * Note this must not be called within a treeview_walk. It may delete the @@ -1226,21 +1768,21 @@ static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction) * with a next sibling. */ while (node->parent != NULL && - next_sibling == NULL) { + next_sibling == NULL) { if (node->type == TREE_NODE_FOLDER && - node->children == NULL) { + node->children == NULL) { /* Delete node */ p = node->parent; err = treeview_delete_node_walk_cb( - node, &nd, &abort); + node, &nd, &abort); if (err != NSERROR_OK) { return err; } /* Reduce ancestor heights */ while (p != NULL && - p->flags & - TV_NFLAGS_EXPANDED) { + p->flags & + TV_NFLAGS_EXPANDED) { p->height -= nd.h_reduction; p = p->parent; } @@ -1255,18 +1797,18 @@ static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction) break; if (node->type == TREE_NODE_FOLDER && - node->children == NULL) { + node->children == NULL) { /* Delete node */ p = node->parent; err = treeview_delete_node_walk_cb( - node, &nd, &abort); + node, &nd, &abort); if (err != NSERROR_OK) { return err; } /* Reduce ancestor heights */ while (p != NULL && - p->flags & TV_NFLAGS_EXPANDED) { + p->flags & TV_NFLAGS_EXPANDED) { p->height -= nd.h_reduction; p = p->parent; } @@ -1281,7 +1823,7 @@ static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction) parent = node->parent; next_sibling = node->next_sib; child = (node->flags & TV_NFLAGS_EXPANDED) ? - node->children : NULL; + node->children : NULL; } return NSERROR_OK; @@ -1289,8 +1831,10 @@ static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction) /* Exported interface, documented in treeview.h */ -nserror treeview_delete_node(treeview *tree, treeview_node *n, - treeview_node_options_flags flags) +nserror +treeview_delete_node(treeview *tree, + treeview_node *n, + treeview_node_options_flags flags) { nserror err; struct rect r; @@ -1321,7 +1865,7 @@ nserror treeview_delete_node(treeview *tree, treeview_node *n, r.y0 = 0; if (!(flags & TREE_OPTION_SUPPRESS_RESIZE)) { treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); } } } @@ -1330,19 +1874,69 @@ nserror treeview_delete_node(treeview *tree, treeview_node *n, if (visible && !(flags & TREE_OPTION_SUPPRESS_REDRAW)) { r.x0 = 0; r.x1 = REDRAW_MAX; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } return NSERROR_OK; } +/** + * Helper to create a textarea. + * + * \param[in] tree The treeview we're creating the textarea for. + * \param[in] width The width of the textarea. + * \param[in] height The height of the textarea. + * \param[in] border The border colour to use. + * \param[in] background The background colour to use. + * \param[in] foreground The foreground colour to use. + * \param[in] text The text style to use for the text area. + * \param[in] ta_callback The textarea callback function to give the textarea. + * \return the textarea pointer on success, or NULL on failure. + */ +static struct textarea *treeview__create_textarea( + treeview *tree, + int width, + int height, + colour border, + colour background, + colour foreground, + plot_font_style_t text, + textarea_client_callback ta_callback) +{ + /* Configure the textarea */ + textarea_flags ta_flags = TEXTAREA_INTERNAL_CARET; + textarea_setup ta_setup = { + .text = text, + .width = width, + .height = height, + .pad_top = 0, + .pad_left = 2, + .pad_right = 2, + .pad_bottom = 0, + .border_width = 1, + .border_col = border, + .selected_bg = foreground, + .selected_text = background, + }; + + ta_setup.text.foreground = foreground; + ta_setup.text.background = background; + + /* Create text area */ + return textarea_create(ta_flags, &ta_setup, ta_callback, tree); +} + + /* Exported interface, documented in treeview.h */ -nserror treeview_create(treeview **tree, +nserror +treeview_create(treeview **tree, const struct treeview_callback_table *callbacks, - int n_fields, struct treeview_field_desc fields[], + int n_fields, + struct treeview_field_desc fields[], const struct core_window_callback_table *cw_t, - struct core_window *cw, treeview_flags flags) + struct core_window *cw, + treeview_flags flags) { nserror error; int i; @@ -1383,7 +1977,7 @@ nserror treeview_create(treeview **tree, f->value.len = lwc_string_length(fields[i].field); guit->layout->width(&plot_style_odd.text, f->value.data, - f->value.len, &(f->value.width)); + f->value.len, &(f->value.width)); if (f->flags & TREE_FLAG_SHOW_NAME) if ((*tree)->field_width < f->value.width) @@ -1413,6 +2007,24 @@ nserror treeview_create(treeview **tree, (*tree)->edit.textarea = NULL; (*tree)->edit.node = NULL; + if (flags & TREEVIEW_SEARCHABLE) { + (*tree)->search.textarea = treeview__create_textarea( + *tree, 600, tree_g.line_height, + plot_style_even.text.background, + plot_style_even.text.background, + plot_style_even.text.foreground, + plot_style_odd.text, + treeview_textarea_search_callback); + if ((*tree)->search.textarea == NULL) { + treeview_destroy(*tree); + return NSERROR_NOMEM; + } + } else { + (*tree)->search.textarea = NULL; + } + (*tree)->search.active = false; + (*tree)->search.search = false; + (*tree)->flags = flags; (*tree)->cw_t = cw_t; @@ -1423,15 +2035,16 @@ nserror treeview_create(treeview **tree, /* Exported interface, documented in treeview.h */ -nserror treeview_cw_attach(treeview *tree, - const struct core_window_callback_table *cw_t, - struct core_window *cw) +nserror +treeview_cw_attach(treeview *tree, + const struct core_window_callback_table *cw_t, + struct core_window *cw) { assert(cw_t != NULL); assert(cw != NULL); if (tree->cw_t != NULL || tree->cw_h != NULL) { - LOG("Treeview already attached."); + NSLOG(netsurf, INFO, "Treeview already attached."); return NSERROR_UNKNOWN; } tree->cw_t = cw_t; @@ -1447,6 +2060,8 @@ nserror treeview_cw_detach(treeview *tree) tree->cw_t = NULL; tree->cw_h = NULL; + treeview__search_cancel(tree, true); + return NSERROR_OK; } @@ -1458,10 +2073,16 @@ nserror treeview_destroy(treeview *tree) assert(tree != NULL); + if (tree->search.textarea != NULL) { + tree->search.active = false; + tree->search.search = false; + textarea_destroy(tree->search.textarea); + } + /* Destroy nodes */ treeview_delete_node_internal(tree, tree->root, false, - TREE_OPTION_SUPPRESS_RESIZE | - TREE_OPTION_SUPPRESS_REDRAW); + TREE_OPTION_SUPPRESS_RESIZE | + TREE_OPTION_SUPPRESS_REDRAW); /* Destroy feilds */ for (f = 0; f <= tree->n_fields; f++) { @@ -1483,8 +2104,8 @@ nserror treeview_destroy(treeview *tree) * \param node The node to expand. * \return NSERROR_OK on success, appropriate error otherwise. */ -static nserror treeview_node_expand_internal(treeview *tree, - treeview_node *node) +static nserror +treeview_node_expand_internal(treeview *tree, treeview_node *node) { treeview_node *child; struct treeview_node_entry *e; @@ -1496,7 +2117,7 @@ static nserror treeview_node_expand_internal(treeview *tree, if (node->flags & TV_NFLAGS_EXPANDED) { /* What madness is this? */ - LOG("Tried to expand an expanded node."); + NSLOG(netsurf, INFO, "Tried to expand an expanded node."); return NSERROR_OK; } @@ -1509,12 +2130,11 @@ static nserror treeview_node_expand_internal(treeview *tree, } do { - assert((child->flags & TV_NFLAGS_EXPANDED) == false); if (child->text.width == 0) { guit->layout->width(&plot_style_odd.text, - child->text.data, - child->text.len, - &(child->text.width)); + child->text.data, + child->text.len, + &(child->text.width)); } additional_height += child->height; @@ -1533,9 +2153,9 @@ static nserror treeview_node_expand_internal(treeview *tree, if (e->fields[i].value.width == 0) { guit->layout->width(&plot_style_odd.text, - e->fields[i].value.data, - e->fields[i].value.len, - &(e->fields[i].value.width)); + e->fields[i].value.data, + e->fields[i].value.len, + &(e->fields[i].value.width)); } /* Add height for field */ @@ -1554,17 +2174,24 @@ static nserror treeview_node_expand_internal(treeview *tree, /* Update the node */ node->flags |= TV_NFLAGS_EXPANDED; - /* And parent's heights */ - do { - node->height += additional_height; - node = node->parent; - } while (node->parent != NULL); + /* And node heights */ + for (struct treeview_node *n = node; + (n != NULL) && (n->flags & TV_NFLAGS_EXPANDED); + n = n->parent) { + n->height += additional_height; + } - node->height += additional_height; + if (tree->search.search && + node->type == TREE_NODE_ENTRY && + node->flags & TV_NFLAGS_MATCHED) { + tree->search.height += additional_height; + } /* Inform front end of change in dimensions */ - if (additional_height != 0) - treeview__cw_update_size(tree, -1, tree->root->height); + if (additional_height != 0) { + treeview__cw_update_size(tree, -1, + treeview__get_display_height(tree)); + } return NSERROR_OK; } @@ -1573,29 +2200,37 @@ static nserror treeview_node_expand_internal(treeview *tree, /* Exported interface, documented in treeview.h */ nserror treeview_node_expand(treeview *tree, treeview_node *node) { - nserror err; - struct rect r; - - err = treeview_node_expand_internal(tree, node); - if (err != NSERROR_OK) - return err; - - r.x0 = 0; - r.y0 = treeview_node_y(tree, node); - r.x1 = REDRAW_MAX; - r.y1 = tree->root->height; - - /* Redraw */ - treeview__cw_redraw_request(tree, &r); + nserror res; + + res = treeview_node_expand_internal(tree, node); + NSLOG(netsurf, INFO, "Expanding!"); + if (res == NSERROR_OK) { + /* expansion was successful, attempt redraw */ + treeview__redraw_from_node(tree, node); + NSLOG(netsurf, INFO, "Expanded!"); + } - return NSERROR_OK; + return res; } +/** + * context for treeview contraction callback + */ struct treeview_contract_data { + treeview *tree; bool only_entries; }; -/** Treewalk node callback for handling node contraction. */ + + +/** + * Treewalk node callback for handling node contraction. + * + * \param n node + * \param ctx contract iterator context + * \param end flag to end iteration now + * \return NSERROR_OK on success else appropriate error code + */ static nserror treeview_node_contract_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_contract_data *data = ctx; @@ -1607,32 +2242,39 @@ static nserror treeview_node_contract_cb(treeview_node *n, void *ctx, bool *end) n->flags &= ~TV_NFLAGS_SELECTED; if ((n->flags & TV_NFLAGS_EXPANDED) == false || - (n->type == TREE_NODE_FOLDER && data->only_entries)) { + (n->type == TREE_NODE_FOLDER && data->only_entries)) { /* Nothing to do. */ return NSERROR_OK; } - n->flags ^= TV_NFLAGS_EXPANDED; h_reduction = n->height - tree_g.line_height; assert(h_reduction >= 0); + for (struct treeview_node *node = n; + (node != NULL) && (node->flags & TV_NFLAGS_EXPANDED); + node = node->parent) { + node->height -= h_reduction; + } - do { - n->height -= h_reduction; - n = n->parent; - } while (n != NULL); + if (data->tree->search.search) { + data->tree->search.height -= h_reduction; + } + + n->flags ^= TV_NFLAGS_EXPANDED; return NSERROR_OK; } + + /** * Contract a treeview node * - * \param tree Treeview object to contract node in - * \param node Node to contract + * \param tree Treeview object to contract node in + * \param node Node to contract * \return NSERROR_OK on success, appropriate error otherwise */ -static nserror treeview_node_contract_internal(treeview *tree, - treeview_node *node) +static nserror +treeview_node_contract_internal(treeview *tree, treeview_node *node) { struct treeview_contract_data data; bool selected; @@ -1640,16 +2282,17 @@ static nserror treeview_node_contract_internal(treeview *tree, if ((node->flags & TV_NFLAGS_EXPANDED) == false) { /* What madness is this? */ - LOG("Tried to contract a contracted node."); + NSLOG(netsurf, INFO, "Tried to contract a contracted node."); return NSERROR_OK; } + data.tree = tree; data.only_entries = false; selected = node->flags & TV_NFLAGS_SELECTED; /* Contract children. */ - treeview_walk_internal(node, false, treeview_node_contract_cb, - NULL, &data); + treeview_walk_internal(tree, node, TREEVIEW_WALK_MODE_LOGICAL_EXPANDED, + treeview_node_contract_cb, NULL, &data); /* Contract node */ treeview_node_contract_cb(node, &data, false); @@ -1658,7 +2301,7 @@ static nserror treeview_node_contract_internal(treeview *tree, node->flags |= TV_NFLAGS_SELECTED; /* Inform front end of change in dimensions */ - treeview__cw_update_size(tree, -1, tree->root->height); + treeview__cw_update_size(tree, -1, treeview__get_display_height(tree)); return NSERROR_OK; } @@ -1667,30 +2310,27 @@ static nserror treeview_node_contract_internal(treeview *tree, /* Exported interface, documented in treeview.h */ nserror treeview_node_contract(treeview *tree, treeview_node *node) { - nserror err; - struct rect r; + nserror res; assert(tree != NULL); - r.x0 = 0; - r.y0 = treeview_node_y(tree, node); - r.x1 = REDRAW_MAX; - r.y1 = tree->root->height; - - err = treeview_node_contract_internal(tree, node); - if (err != NSERROR_OK) - return err; - - /* Redraw */ - treeview__cw_redraw_request(tree, &r); + res = treeview_node_contract_internal(tree, node); + NSLOG(netsurf, INFO, "Contracting!"); + if (res == NSERROR_OK) { + /* successful contraction, request redraw */ + treeview__redraw_from_node(tree, node); + NSLOG(netsurf, INFO, "Contracted!"); + } - return NSERROR_OK; + return res; } /* Exported interface, documented in treeview.h */ nserror treeview_contract(treeview *tree, bool all) { + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; struct treeview_contract_data data; bool selected; treeview_node *n; @@ -1702,8 +2342,9 @@ nserror treeview_contract(treeview *tree, bool all) r.x0 = 0; r.y0 = 0; r.x1 = REDRAW_MAX; - r.y1 = tree->root->height; + r.y1 = tree->root->height + search_height; + data.tree = tree; data.only_entries = !all; for (n = tree->root->children; n != NULL; n = n->next_sib) { @@ -1714,7 +2355,8 @@ nserror treeview_contract(treeview *tree, bool all) selected = n->flags & TV_NFLAGS_SELECTED; /* Contract children. */ - treeview_walk_internal(n, false, + treeview_walk_internal(tree, n, + TREEVIEW_WALK_MODE_LOGICAL_EXPANDED, treeview_node_contract_cb, NULL, &data); /* Contract node */ @@ -1728,19 +2370,35 @@ nserror treeview_contract(treeview *tree, bool all) treeview__cw_update_size(tree, -1, tree->root->height); /* Redraw */ - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); return NSERROR_OK; } +/** + * context data for treeview expansion + */ struct treeview_expand_data { treeview *tree; bool only_folders; }; -/** Treewalk node callback for handling recursive node expansion. */ -static nserror treeview_expand_cb(treeview_node *n, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node callback for handling recursive node expansion. + * + * \param n current node + * \param ctx node expansion context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_expand_cb(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_expand_data *data = ctx; nserror err; @@ -1749,7 +2407,7 @@ static nserror treeview_expand_cb(treeview_node *n, void *ctx, assert(n->type != TREE_NODE_ROOT); if (n->flags & TV_NFLAGS_EXPANDED || - (data->only_folders && n->type != TREE_NODE_FOLDER)) { + (data->only_folders && n->type != TREE_NODE_FOLDER)) { /* Nothing to do. */ return NSERROR_OK; } @@ -1758,11 +2416,13 @@ static nserror treeview_expand_cb(treeview_node *n, void *ctx, return err; } + + /* Exported interface, documented in treeview.h */ nserror treeview_expand(treeview *tree, bool only_folders) { struct treeview_expand_data data; - nserror err; + nserror res; struct rect r; assert(tree != NULL); @@ -1771,50 +2431,57 @@ nserror treeview_expand(treeview *tree, bool only_folders) data.tree = tree; data.only_folders = only_folders; - err = treeview_walk_internal(tree->root, true, NULL, - treeview_expand_cb, &data); - if (err != NSERROR_OK) - return err; - - r.x0 = 0; - r.y0 = 0; - r.x1 = REDRAW_MAX; - r.y1 = tree->root->height; + res = treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, + NULL, treeview_expand_cb, &data); + if (res == NSERROR_OK) { + /* expansion succeeded, schedule redraw */ - /* Redraw */ - treeview__cw_redraw_request(tree, &r); + r.x0 = 0; + r.y0 = 0; + r.x1 = REDRAW_MAX; + r.y1 = tree->root->height; - return NSERROR_OK; + treeview__cw_invalidate_area(tree, &r); + } + return res; } -/* Exported interface, documented in treeview.h */ -void treeview_redraw(treeview *tree, const int x, const int y, - struct rect *clip, const struct redraw_context *ctx) +/** + * Draw a treeview normally, in tree mode. + * + * \param[in] tree The treeview we're rendering. + * \param[in] x X coordinate we're rendering the treeview at. + * \param[in] y Y coordinate we're rendering the treeview at. + * \param[in,out] render_y Current vertical position in tree, updated on exit. + * \param[in] r Clip rectangle. + * \param[in] data Redraw data for rendering contents. + * \param[in] ctx Current render context. + */ +static void treeview_redraw_tree( + treeview *tree, + const int x, + const int y, + int *render_y_in_out, + struct rect *r, + struct content_redraw_data *data, + const struct redraw_context *ctx) { - struct redraw_context new_ctx = *ctx; - treeview_node *node, *root, *next; - struct treeview_node_entry *entry; struct treeview_node_style *style = &plot_style_odd; - struct content_redraw_data data; - struct rect r; - uint32_t count = 0; - int render_y = y; - int inset; - int x0, y0, y1; - int baseline = (tree_g.line_height * 3 + 2) / 4; enum treeview_resource_id res = TREE_RES_CONTENT; - plot_style_t *bg_style; - plot_font_style_t *text_style; + int baseline = (tree_g.line_height * 3 + 2) / 4; plot_font_style_t *infotext_style; - struct bitmap *furniture; - int height; + treeview_node *root = tree->root; + treeview_node *node = tree->root; + int render_y = *render_y_in_out; + plot_font_style_t *text_style; + plot_style_t *bg_style; int sel_min, sel_max; - bool invert_selection; - - assert(tree != NULL); - assert(tree->root != NULL); - assert(tree->root->flags & TV_NFLAGS_EXPANDED); + uint32_t count = 0; + struct rect rect; + int inset; + int x0; if (tree->drag.start.y > tree->drag.prev.y) { sel_min = tree->drag.prev.y; @@ -1824,31 +2491,16 @@ void treeview_redraw(treeview *tree, const int x, const int y, sel_max = tree->drag.prev.y; } - /* Start knockout rendering if it's available for this plotter */ - if (ctx->plot->option_knockout) - knockout_plot_start(ctx, &new_ctx); - - /* Set up clip rectangle */ - r.x0 = clip->x0 + x; - r.y0 = clip->y0 + y; - r.x1 = clip->x1 + x; - r.y1 = clip->y1 + y; - new_ctx.plot->clip(&r); - - /* Draw the tree */ - node = root = tree->root; - - /* Setup common content redraw data */ - data.width = tree_g.icon_size; - data.height = tree_g.icon_size; - data.scale = 1; - data.repeat_x = false; - data.repeat_y = false; - while (node != NULL) { + struct treeview_node_entry *entry; + struct bitmap *furniture; + bool invert_selection; + treeview_node *next; + int height; int i; + next = (node->flags & TV_NFLAGS_EXPANDED) ? - node->children : NULL; + node->children : NULL; if (next != NULL) { /* down to children */ @@ -1859,7 +2511,7 @@ void treeview_redraw(treeview *tree, const int x, const int y, * with a next sibling. */ while (node != root && - node->next_sib == NULL) { + node->next_sib == NULL) { node = node->parent; } @@ -1872,14 +2524,14 @@ void treeview_redraw(treeview *tree, const int x, const int y, assert(node != NULL); assert(node != root); assert(node->type == TREE_NODE_FOLDER || - node->type == TREE_NODE_ENTRY); + node->type == TREE_NODE_ENTRY); count++; inset = x + node->inset; height = (node->type == TREE_NODE_ENTRY) ? node->height : - tree_g.line_height; + tree_g.line_height; - if ((render_y + height) < r.y0) { + if ((render_y + height) < r->y0) { /* This node's line is above clip region */ render_y += height; continue; @@ -1887,70 +2539,77 @@ void treeview_redraw(treeview *tree, const int x, const int y, style = (count & 0x1) ? &plot_style_odd : &plot_style_even; if (tree->drag.type == TV_DRAG_SELECTION && - (render_y + height >= sel_min && - render_y < sel_max)) { + (render_y + height >= sel_min && + render_y < sel_max)) { invert_selection = true; } else { invert_selection = false; } if ((node->flags & TV_NFLAGS_SELECTED && !invert_selection) || - (!(node->flags & TV_NFLAGS_SELECTED) && - invert_selection)) { + (!(node->flags & TV_NFLAGS_SELECTED) && + invert_selection)) { bg_style = &style->sbg; text_style = &style->stext; infotext_style = &style->sitext; furniture = (node->flags & TV_NFLAGS_EXPANDED) ? - style->furn[TREE_FURN_CONTRACT].sel : - style->furn[TREE_FURN_EXPAND].sel; + style->furn[TREE_FURN_CONTRACT].sel : + style->furn[TREE_FURN_EXPAND].sel; } else { bg_style = &style->bg; text_style = &style->text; infotext_style = &style->itext; furniture = (node->flags & TV_NFLAGS_EXPANDED) ? - style->furn[TREE_FURN_CONTRACT].bmp : - style->furn[TREE_FURN_EXPAND].bmp; + style->furn[TREE_FURN_CONTRACT].bmp : + style->furn[TREE_FURN_EXPAND].bmp; } /* Render background */ - y0 = render_y; - y1 = render_y + height; - new_ctx.plot->rectangle(r.x0, y0, r.x1, y1, bg_style); + rect.x0 = r->x0; + rect.y0 = render_y; + rect.x1 = r->x1; + rect.y1 = render_y + height; + ctx->plot->rectangle(ctx, bg_style, &rect); /* Render toggle */ - new_ctx.plot->bitmap(inset, render_y + tree_g.line_height / 4, + ctx->plot->bitmap(ctx, + furniture, + inset, + render_y + tree_g.line_height / 4, style->furn[TREE_FURN_EXPAND].size, style->furn[TREE_FURN_EXPAND].size, - furniture, - bg_style->fill_colour, BITMAPF_NONE); + bg_style->fill_colour, + BITMAPF_NONE); /* Render icon */ - if (node->type == TREE_NODE_ENTRY) + if (node->type == TREE_NODE_ENTRY) { res = TREE_RES_CONTENT; - else if (node->flags & TV_NFLAGS_SPECIAL) + } else if (node->flags & TV_NFLAGS_SPECIAL) { res = TREE_RES_FOLDER_SPECIAL; - else + } else { res = TREE_RES_FOLDER; + } if (treeview_res[res].ready) { /* Icon resource is available */ - data.x = inset + tree_g.step_width; - data.y = render_y + ((tree_g.line_height - - treeview_res[res].height + 1) / 2); - data.background_colour = bg_style->fill_colour; + data->x = inset + tree_g.step_width; + data->y = render_y + ((tree_g.line_height - + treeview_res[res].height + 1) / 2); + data->background_colour = bg_style->fill_colour; - content_redraw(treeview_res[res].c, - &data, &r, &new_ctx); + content_redraw(treeview_res[res].c, data, r, ctx); } /* Render text */ x0 = inset + tree_g.step_width + tree_g.icon_step; - new_ctx.plot->text(x0, render_y + baseline, - node->text.data, node->text.len, - text_style); + ctx->plot->text(ctx, + text_style, + x0, render_y + baseline, + node->text.data, + node->text.len); /* Rendered the node */ render_y += tree_g.line_height; - if (render_y > r.y1) { + if (render_y > r->y1) { /* Passed the bottom of what's in the clip region. * Done. */ break; @@ -1958,7 +2617,7 @@ void treeview_redraw(treeview *tree, const int x, const int y, if (node->type != TREE_NODE_ENTRY || - !(node->flags & TV_NFLAGS_EXPANDED)) + !(node->flags & TV_NFLAGS_EXPANDED)) /* Done everything for this node */ continue; @@ -1970,57 +2629,364 @@ void treeview_redraw(treeview *tree, const int x, const int y, if (ef->flags & TREE_FLAG_SHOW_NAME) { int max_width = tree->field_width; - new_ctx.plot->text(x0 + max_width - - ef->value.width - - tree_g.step_width, + ctx->plot->text(ctx, + infotext_style, + x0 + max_width - ef->value.width - tree_g.step_width, render_y + baseline, ef->value.data, - ef->value.len, - infotext_style); + ef->value.len); - new_ctx.plot->text(x0 + max_width, + ctx->plot->text(ctx, + infotext_style, + x0 + max_width, render_y + baseline, entry->fields[i].value.data, - entry->fields[i].value.len, - infotext_style); + entry->fields[i].value.len); } else { - new_ctx.plot->text(x0, render_y + baseline, + ctx->plot->text(ctx, + infotext_style, + x0, render_y + baseline, entry->fields[i].value.data, - entry->fields[i].value.len, - infotext_style); + entry->fields[i].value.len); + } + + /* Rendered the expanded entry field */ + render_y += tree_g.line_height; + } + + /* Finished rendering expanded entry */ + + if (render_y > r->y1) { + /* Passed the bottom of what's in the clip region. + * Done. */ + break; + } + } + + *render_y_in_out = render_y; +} + + +/** + * Draw a treeview normally, in tree mode. + * + * \param[in] tree The treeview we're rendering. + * \param[in] x X coordinate we're rendering the treeview at. + * \param[in] y Y coordinate we're rendering the treeview at. + * \param[in,out] render_y Current vertical position in tree, updated on exit. + * \param[in] r Clip rectangle. + * \param[in] data Redraw data for rendering contents. + * \param[in] ctx Current render context. + */ +static void treeview_redraw_search( + treeview *tree, + const int x, + const int y, + int *render_y_in_out, + struct rect *r, + struct content_redraw_data *data, + const struct redraw_context *ctx) +{ + struct treeview_node_style *style = &plot_style_odd; + enum treeview_resource_id res = TREE_RES_CONTENT; + int baseline = (tree_g.line_height * 3 + 2) / 4; + plot_font_style_t *infotext_style; + treeview_node *root = tree->root; + treeview_node *node = tree->root; + int render_y = *render_y_in_out; + plot_font_style_t *text_style; + plot_style_t *bg_style; + int sel_min, sel_max; + uint32_t count = 0; + struct rect rect; + int inset; + int x0; + + if (tree->drag.start.y > tree->drag.prev.y) { + sel_min = tree->drag.prev.y; + sel_max = tree->drag.start.y; + } else { + sel_min = tree->drag.start.y; + sel_max = tree->drag.prev.y; + } + + while (node != NULL) { + struct treeview_node_entry *entry; + struct bitmap *furniture; + bool invert_selection; + treeview_node *next; + int height; + int i; + + next = node->children; + + if (next != NULL) { + /* down to children */ + node = next; + } else { + /* No children. As long as we're not at the root, + * go to next sibling if present, or nearest ancestor + * with a next sibling. */ + + while (node != root && + node->next_sib == NULL) { + node = node->parent; + } + + if (node == root) + break; + + node = node->next_sib; + } + + assert(node != NULL); + assert(node != root); + assert(node->type == TREE_NODE_FOLDER || + node->type == TREE_NODE_ENTRY); + + if (node->type == TREE_NODE_FOLDER || + !(node->flags & TV_NFLAGS_MATCHED)) { + continue; + } + + count++; + inset = x + tree_g.window_padding; + height = node->height; + + if ((render_y + height) < r->y0) { + /* This node's line is above clip region */ + render_y += height; + continue; + } + + style = (count & 0x1) ? &plot_style_odd : &plot_style_even; + if (tree->drag.type == TV_DRAG_SELECTION && + (render_y + height >= sel_min && + render_y < sel_max)) { + invert_selection = true; + } else { + invert_selection = false; + } + if ((node->flags & TV_NFLAGS_SELECTED && !invert_selection) || + (!(node->flags & TV_NFLAGS_SELECTED) && + invert_selection)) { + bg_style = &style->sbg; + text_style = &style->stext; + infotext_style = &style->sitext; + furniture = (node->flags & TV_NFLAGS_EXPANDED) ? + style->furn[TREE_FURN_CONTRACT].sel : + style->furn[TREE_FURN_EXPAND].sel; + } else { + bg_style = &style->bg; + text_style = &style->text; + infotext_style = &style->itext; + furniture = (node->flags & TV_NFLAGS_EXPANDED) ? + style->furn[TREE_FURN_CONTRACT].bmp : + style->furn[TREE_FURN_EXPAND].bmp; + } + + /* Render background */ + rect.x0 = r->x0; + rect.y0 = render_y; + rect.x1 = r->x1; + rect.y1 = render_y + height; + ctx->plot->rectangle(ctx, bg_style, &rect); + + /* Render toggle */ + ctx->plot->bitmap(ctx, + furniture, + inset, + render_y + tree_g.line_height / 4, + style->furn[TREE_FURN_EXPAND].size, + style->furn[TREE_FURN_EXPAND].size, + bg_style->fill_colour, + BITMAPF_NONE); + + /* Render icon */ + if (node->type == TREE_NODE_ENTRY) { + res = TREE_RES_CONTENT; + } else if (node->flags & TV_NFLAGS_SPECIAL) { + res = TREE_RES_FOLDER_SPECIAL; + } else { + res = TREE_RES_FOLDER; + } + + if (treeview_res[res].ready) { + /* Icon resource is available */ + data->x = inset + tree_g.step_width; + data->y = render_y + ((tree_g.line_height - + treeview_res[res].height + 1) / 2); + data->background_colour = bg_style->fill_colour; + + content_redraw(treeview_res[res].c, data, r, ctx); + } + + /* Render text */ + x0 = inset + tree_g.step_width + tree_g.icon_step; + ctx->plot->text(ctx, + text_style, + x0, render_y + baseline, + node->text.data, + node->text.len); + + /* Rendered the node */ + render_y += tree_g.line_height; + if (render_y > r->y1) { + /* Passed the bottom of what's in the clip region. + * Done. */ + break; + } + + + if (node->type != TREE_NODE_ENTRY || + !(node->flags & TV_NFLAGS_EXPANDED)) + /* Done everything for this node */ + continue; + + /* Render expanded entry fields */ + entry = (struct treeview_node_entry *)node; + for (i = 0; i < tree->n_fields - 1; i++) { + struct treeview_field *ef = &(tree->fields[i + 1]); + + if (ef->flags & TREE_FLAG_SHOW_NAME) { + int max_width = tree->field_width; + ctx->plot->text(ctx, + infotext_style, + x0 + max_width - ef->value.width - tree_g.step_width, + render_y + baseline, + ef->value.data, + ef->value.len); + + ctx->plot->text(ctx, + infotext_style, + x0 + max_width, + render_y + baseline, + entry->fields[i].value.data, + entry->fields[i].value.len); + } else { + ctx->plot->text(ctx, + infotext_style, + x0, render_y + baseline, + entry->fields[i].value.data, + entry->fields[i].value.len); } /* Rendered the expanded entry field */ render_y += tree_g.line_height; } - /* Finshed rendering expanded entry */ + /* Finished rendering expanded entry */ - if (render_y > r.y1) { + if (render_y > r->y1) { /* Passed the bottom of what's in the clip region. * Done. */ break; } } + *render_y_in_out = render_y; +} + + +/* Exported interface, documented in treeview.h */ +void +treeview_redraw(treeview *tree, + const int x, + const int y, + struct rect *clip, + const struct redraw_context *ctx) +{ + struct redraw_context new_ctx = *ctx; + struct content_redraw_data data; + struct rect r; + struct rect rect; + int render_y = y; + + assert(tree != NULL); + assert(tree->root != NULL); + assert(tree->root->flags & TV_NFLAGS_EXPANDED); + + /* Start knockout rendering if it's available for this plotter */ + if (ctx->plot->option_knockout) { + knockout_plot_start(ctx, &new_ctx); + } + + /* Set up clip rectangle */ + r.x0 = clip->x0 + x; + r.y0 = clip->y0 + y; + r.x1 = clip->x1 + x; + r.y1 = clip->y1 + y; + new_ctx.plot->clip(&new_ctx, &r); + + /* Setup common content redraw data */ + data.width = tree_g.icon_size; + data.height = tree_g.icon_size; + data.scale = 1; + data.repeat_x = false; + data.repeat_y = false; + + if (tree->flags & TREEVIEW_SEARCHABLE) { + if (render_y < r.y1) { + enum treeview_resource_id icon = TREE_RES_SEARCH; + + /* Fill the blank area at the bottom */ + rect.x0 = r.x0; + rect.y0 = render_y; + rect.x1 = r.x1; + rect.y1 = render_y + tree_g.line_height; + new_ctx.plot->rectangle(&new_ctx, &plot_style_even.bg, + &rect); + + if (treeview_res[icon].ready) { + /* Icon resource is available */ + data.x = tree_g.window_padding; + data.y = render_y + ((tree_g.line_height - + treeview_res[icon].height + 1) / + 2); + data.background_colour = plot_style_even.bg. + fill_colour; + + content_redraw(treeview_res[icon].c, + &data, &r, &new_ctx); + } + + textarea_redraw(tree->search.textarea, + x + tree_g.window_padding + + tree_g.icon_step, y, + plot_style_even.bg.fill_colour, 1.0, + &r, &new_ctx); + } + render_y += tree_g.line_height; + } + + /* Render the treeview data */ + if (tree->search.search == true) { + treeview_redraw_search(tree, x, y, + &render_y, &r, &data, &new_ctx); + } else { + treeview_redraw_tree(tree, x, y, + &render_y, &r, &data, &new_ctx); + } + if (render_y < r.y1) { /* Fill the blank area at the bottom */ - y0 = render_y; - new_ctx.plot->rectangle(r.x0, y0, r.x1, r.y1, - &plot_style_even.bg); + rect.x0 = r.x0; + rect.y0 = render_y; + rect.x1 = r.x1; + rect.y1 = r.y1; + new_ctx.plot->rectangle(&new_ctx, &plot_style_even.bg, &rect); } /* All normal treeview rendering is done; render any overlays */ - if (tree->move.target_pos != TV_TARGET_NONE && - treeview_res[TREE_RES_ARROW].ready) { + if ((tree->move.target_pos != TV_TARGET_NONE) && + (treeview_res[TREE_RES_ARROW].ready)) { /* Got a MOVE drag; render move indicator arrow */ data.x = tree->move.target_area.x0 + x; data.y = tree->move.target_area.y0 + y; data.background_colour = plot_style_even.bg.fill_colour; - content_redraw(treeview_res[TREE_RES_ARROW].c, - &data, &r, &new_ctx); + content_redraw(treeview_res[TREE_RES_ARROW].c, &data, &r, &new_ctx); } else if (tree->edit.textarea != NULL) { /* Edit in progress; render textarea */ @@ -2031,10 +2997,15 @@ void treeview_redraw(treeview *tree, const int x, const int y, } /* Rendering complete */ - if (ctx->plot->option_knockout) - knockout_plot_end(); + if (ctx->plot->option_knockout) { + knockout_plot_end(ctx); + } } + +/** + * context for treeview selection + */ struct treeview_selection_walk_data { enum { TREEVIEW_WALK_HAS_SELECTION, @@ -2059,6 +3030,7 @@ struct treeview_selection_walk_data { } drag; struct { treeview_node *prev; + treeview_node *fixed; } yank; struct { treeview_node *n; @@ -2071,9 +3043,22 @@ struct treeview_selection_walk_data { int current_y; treeview *tree; }; -/** Treewalk node callback for handling selection related actions. */ -static nserror treeview_node_selection_walk_cb(treeview_node *n, - void *ctx, bool *skip_children, bool *end) + + +/** + * Treewalk node callback for handling selection related actions. + * + * \param n current node + * \param ctx node selection context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_node_selection_walk_cb(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_selection_walk_data *sw = ctx; int height; @@ -2103,7 +3088,7 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, case TREEVIEW_WALK_DELETE_SELECTION: if (n->flags & TV_NFLAGS_SELECTED) { err = treeview_delete_node_internal(sw->tree, n, true, - TREE_OPTION_NONE); + TREE_OPTION_NONE); if (err != NSERROR_OK) { return err; } @@ -2114,8 +3099,8 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, case TREEVIEW_WALK_PROPAGATE_SELECTION: if (n->parent != NULL && - n->parent->flags & TV_NFLAGS_SELECTED && - !(n->flags & TV_NFLAGS_SELECTED)) { + n->parent->flags & TV_NFLAGS_SELECTED && + !(n->flags & TV_NFLAGS_SELECTED)) { n->flags ^= TV_NFLAGS_SELECTED; changed = true; } @@ -2137,8 +3122,8 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, case TREEVIEW_WALK_COMMIT_SELECT_DRAG: if (sw->current_y >= sw->data.drag.sel_min && - sw->current_y - height < - sw->data.drag.sel_max) { + sw->current_y - height < + sw->data.drag.sel_max) { n->flags ^= TV_NFLAGS_SELECTED; } return NSERROR_OK; @@ -2148,6 +3133,10 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, treeview_node *p = n->parent; int h = 0; + if (n == sw->data.yank.fixed) { + break; + } + if (treeview_unlink_node(n)) h = n->height; @@ -2175,7 +3164,7 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, case TREEVIEW_WALK_COPY_SELECTION: if (n->flags & TV_NFLAGS_SELECTED && - n->type == TREE_NODE_ENTRY) { + n->type == TREE_NODE_ENTRY) { int i; char *temp; uint32_t len; @@ -2190,12 +3179,12 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, continue; } val = treeview_get_text_for_field(sw->tree, - n, i); + n, i); text = val->data; len = val->len; temp = realloc(sw->data.copy.text, - sw->data.copy.len + len + 1); + sw->data.copy.len + len + 1); if (temp == NULL) { free(sw->data.copy.text); sw->data.copy.text = NULL; @@ -2238,7 +3227,8 @@ bool treeview_has_selection(treeview *tree) sw.purpose = TREEVIEW_WALK_HAS_SELECTION; sw.data.has_selection = false; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.has_selection; @@ -2248,7 +3238,7 @@ bool treeview_has_selection(treeview *tree) /** * Get first selected node (in any) * - * \param tree Treeview object in which to create folder + * \param tree Treeview object in which to create folder * \return the first selected treeview node, or NULL */ static treeview_node * treeview_get_first_selected(treeview *tree) @@ -2258,7 +3248,8 @@ static treeview_node * treeview_get_first_selected(treeview *tree) sw.purpose = TREEVIEW_WALK_GET_FIRST_SELECTED; sw.data.first.n = NULL; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.first.n; @@ -2267,7 +3258,7 @@ static treeview_node * treeview_get_first_selected(treeview *tree) /* Exported interface, documented in treeview.h */ enum treeview_node_type treeview_get_selection(treeview *tree, - void **node_data) + void **node_data) { treeview_node *n; @@ -2288,8 +3279,8 @@ enum treeview_node_type treeview_get_selection(treeview *tree, /** * Clear any selection in a treeview * - * \param tree Treeview object to clear selection in - * \param rect Redraw rectangle (if redraw required) + * \param tree Treeview object to clear selection in + * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ static bool treeview_clear_selection(treeview *tree, struct rect *rect) @@ -2304,9 +3295,11 @@ static bool treeview_clear_selection(treeview *tree, struct rect *rect) sw.purpose = TREEVIEW_WALK_CLEAR_SELECTION; sw.data.redraw.required = false; sw.data.redraw.rect = rect; - sw.current_y = 0; + sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.redraw.required; @@ -2316,8 +3309,8 @@ static bool treeview_clear_selection(treeview *tree, struct rect *rect) /** * Select all in a treeview * - * \param tree Treeview object to select all in - * \param rect Redraw rectangle (if redraw required) + * \param tree Treeview object to select all in + * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ static bool treeview_select_all(treeview *tree, struct rect *rect) @@ -2332,9 +3325,11 @@ static bool treeview_select_all(treeview *tree, struct rect *rect) sw.purpose = TREEVIEW_WALK_SELECT_ALL; sw.data.redraw.required = false; sw.data.redraw.rect = rect; - sw.current_y = 0; + sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.redraw.required; @@ -2344,14 +3339,15 @@ static bool treeview_select_all(treeview *tree, struct rect *rect) /** * Commit a current selection drag, modifying the node's selection state. * - * \param tree Treeview object to commit drag selection in + * \param tree Treeview object to commit drag selection in */ static void treeview_commit_selection_drag(treeview *tree) { struct treeview_selection_walk_data sw; sw.purpose = TREEVIEW_WALK_COMMIT_SELECT_DRAG; - sw.current_y = 0; + sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; if (tree->drag.start.y > tree->drag.prev.y) { sw.data.drag.sel_min = tree->drag.prev.y; @@ -2361,7 +3357,8 @@ static void treeview_commit_selection_drag(treeview *tree) sw.data.drag.sel_max = tree->drag.prev.y; } - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); } @@ -2369,17 +3366,20 @@ static void treeview_commit_selection_drag(treeview *tree) /** * Yank a selection to the node move list. * - * \param tree Treeview object to yank selection from + * \param tree Treeview object to yank selection from + * \param fixed Treeview node that should not be yanked */ -static void treeview_move_yank_selection(treeview *tree) +static void treeview_move_yank_selection(treeview *tree, treeview_node *fixed) { struct treeview_selection_walk_data sw; sw.purpose = TREEVIEW_WALK_YANK_SELECTION; + sw.data.yank.fixed = fixed; sw.data.yank.prev = NULL; sw.tree = tree; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); } @@ -2387,7 +3387,7 @@ static void treeview_move_yank_selection(treeview *tree) /** * Copy a selection to the clipboard. * - * \param tree Treeview object to yank selection from + * \param tree Treeview object to yank selection from */ static void treeview_copy_selection(treeview *tree) { @@ -2399,7 +3399,8 @@ static void treeview_copy_selection(treeview *tree) sw.data.copy.len = 0; sw.tree = tree; - err = treeview_walk_internal(tree->root, false, NULL, + err = treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); if (err != NSERROR_OK) { return; @@ -2407,7 +3408,7 @@ static void treeview_copy_selection(treeview *tree) if (sw.data.copy.text != NULL) { guit->clipboard->set(sw.data.copy.text, - sw.data.copy.len - 1, NULL, 0); + sw.data.copy.len - 1, NULL, 0); free(sw.data.copy.text); } } @@ -2416,8 +3417,8 @@ static void treeview_copy_selection(treeview *tree) /** * Delete a selection. * - * \param tree Treeview object to delete selected nodes from - * \param rect Updated to redraw rectangle + * \param tree Treeview object to delete selected nodes from + * \param rect Updated to redraw rectangle * \return true iff redraw required. */ static bool treeview_delete_selection(treeview *tree, struct rect *rect) @@ -2438,7 +3439,8 @@ static bool treeview_delete_selection(treeview *tree, struct rect *rect) sw.current_y = 0; sw.tree = tree; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.redraw.required; @@ -2448,8 +3450,8 @@ static bool treeview_delete_selection(treeview *tree, struct rect *rect) /** * Propagate selection to visible descendants of selected nodes. * - * \param tree Treeview object to propagate selection in - * \param rect Redraw rectangle (if redraw required) + * \param tree Treeview object to propagate selection in + * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ static bool treeview_propagate_selection(treeview *tree, struct rect *rect) @@ -2470,7 +3472,8 @@ static bool treeview_propagate_selection(treeview *tree, struct rect *rect) sw.current_y = 0; sw.tree = tree; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.redraw.required; @@ -2480,8 +3483,9 @@ static bool treeview_propagate_selection(treeview *tree, struct rect *rect) /** * Move a selection according to the current move drag. * - * \param tree Treeview object to move selected nodes in - * \param rect Redraw rectangle + * \param tree Treeview object to move selected nodes in + * \param rect Redraw rectangle + * \return NSERROR_OK on success else appropriate error code */ static nserror treeview_move_selection(treeview *tree, struct rect *rect) { @@ -2534,7 +3538,7 @@ static nserror treeview_move_selection(treeview *tree, struct rect *rect) break; default: - LOG("Bad drop target for move."); + NSLOG(netsurf, INFO, "Bad drop target for move."); return NSERROR_BAD_PARAMETER; } @@ -2544,23 +3548,24 @@ static nserror treeview_move_selection(treeview *tree, struct rect *rect) parent = relation->parent; } - /* The node that we're moving selection to can't itself be selected */ - assert(!(relation->flags & TV_NFLAGS_SELECTED)); - /* Move all selected nodes from treeview to tree->move.root */ - treeview_move_yank_selection(tree); + treeview_move_yank_selection(tree, relation); /* Move all nodes on tree->move.root to target location */ for (node = tree->move.root; node != NULL; node = next) { next = node->next_sib; + if (node == relation) { + continue; + } + if (!(parent->flags & TV_NFLAGS_EXPANDED)) { if (node->flags & TV_NFLAGS_EXPANDED) treeview_node_contract_internal(tree, node); node->flags &= ~TV_NFLAGS_SELECTED; } - treeview_insert_node(node, relation, relationship); + treeview_insert_node(tree, node, relation, relationship); relation = node; relationship = TREE_REL_NEXT_SIBLING; @@ -2581,13 +3586,20 @@ static nserror treeview_move_selection(treeview *tree, struct rect *rect) } +/** + * context for treeview launch action + */ struct treeview_launch_walk_data { int selected_depth; treeview *tree; }; -/** Treewalk node walk backward callback for tracking folder selection. */ -static nserror treeview_node_launch_walk_bwd_cb(treeview_node *n, void *ctx, - bool *end) + + +/** + * Treewalk node walk backward callback for tracking folder selection. + */ +static nserror +treeview_node_launch_walk_bwd_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_launch_walk_data *lw = ctx; @@ -2597,9 +3609,22 @@ static nserror treeview_node_launch_walk_bwd_cb(treeview_node *n, void *ctx, return NSERROR_OK; } -/** Treewalk node walk forward callback for launching nodes. */ -static nserror treeview_node_launch_walk_fwd_cb(treeview_node *n, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node walk forward callback for launching nodes. + * + * \param n current node + * \param ctx node launch context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_node_launch_walk_fwd_cb(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_launch_walk_data *lw = ctx; nserror ret = NSERROR_OK; @@ -2608,8 +3633,8 @@ static nserror treeview_node_launch_walk_fwd_cb(treeview_node *n, void *ctx, lw->selected_depth++; } else if (n->type == TREE_NODE_ENTRY && - (n->flags & TV_NFLAGS_SELECTED || - lw->selected_depth > 0)) { + (n->flags & TV_NFLAGS_SELECTED || + lw->selected_depth > 0)) { struct treeview_node_msg msg; msg.msg = TREE_MSG_NODE_LAUNCH; msg.data.node_launch.mouse = BROWSER_MOUSE_HOVER; @@ -2618,14 +3643,16 @@ static nserror treeview_node_launch_walk_fwd_cb(treeview_node *n, void *ctx, return ret; } + + /** * Launch a selection. * - * \param tree Treeview object to launch selected nodes in - * \return NSERROR_OK on success, appropriate error otherwise + * \note Selected entries are launched. Entries that are descendants + * of selected folders are also launched. * - * Note: Selected entries are launched. - * Entries that are descendants of selected folders are also launched. + * \param tree Treeview object to launch selected nodes in + * \return NSERROR_OK on success, appropriate error otherwise */ static nserror treeview_launch_selection(treeview *tree) { @@ -2637,15 +3664,20 @@ static nserror treeview_launch_selection(treeview *tree) lw.selected_depth = 0; lw.tree = tree; - return treeview_walk_internal(tree->root, true, + return treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, treeview_node_launch_walk_bwd_cb, treeview_node_launch_walk_fwd_cb, &lw); } /* Exported interface, documented in treeview.h */ -nserror treeview_get_relation(treeview *tree, treeview_node **relation, - enum treeview_relationship *rel, bool at_y, int y) +nserror +treeview_get_relation(treeview *tree, + treeview_node **relation, + enum treeview_relationship *rel, + bool at_y, + int y) { treeview_node *n; @@ -2687,6 +3719,9 @@ nserror treeview_get_relation(treeview *tree, treeview_node **relation, } +/** + * context for treeview keyboard action + */ struct treeview_nav_state { treeview *tree; treeview_node *prev; @@ -2696,9 +3731,22 @@ struct treeview_nav_state { int n_selected; int prev_n_selected; }; -/** Treewalk node callback for handling mouse action. */ -static nserror treeview_node_nav_cb(treeview_node *node, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node callback for handling mouse action. + * + * \param node current node + * \param ctx node context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_node_nav_cb(treeview_node *node, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_nav_state *ns = ctx; @@ -2724,19 +3772,21 @@ static nserror treeview_node_nav_cb(treeview_node *node, void *ctx, return NSERROR_OK; } + + /** * Handle keyboard navigation. * - * \param tree Treeview object to launch selected nodes in - * \param key The ucs4 character codepoint - * \param rect Updated to redraw rectangle - * \return true if treeview needs redraw, false otherwise - * - * Note: Selected entries are launched. + * \note Selected entries are launched. * Entries that are descendants of selected folders are also launched. + * + * \param tree Treeview object to launch selected nodes in + * \param key The ucs4 character codepoint + * \param rect Updated to redraw rectangle + * \return true if treeview needs redraw, false otherwise */ -static bool treeview_keyboard_navigation(treeview *tree, uint32_t key, - struct rect *rect) +static bool +treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect) { struct treeview_nav_state ns = { .tree = tree, @@ -2747,26 +3797,35 @@ static bool treeview_keyboard_navigation(treeview *tree, uint32_t key, .n_selected = 0, .prev_n_selected = 0 }; - int h = tree->root->height; + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + int h = treeview__get_display_height(tree) + search_height; bool redraw = false; /* Fill out the nav. state struct, by examining the current selection * state */ - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_nav_cb, &ns); - if (ns.next == NULL) - ns.next = tree->root->children; - if (ns.prev == NULL) - ns.prev = ns.last; + + if (tree->search.search == false) { + if (ns.next == NULL) + ns.next = tree->root->children; + if (ns.prev == NULL) + ns.prev = ns.last; + } /* Clear any existing selection */ redraw = treeview_clear_selection(tree, rect); switch (key) { case NS_KEY_LEFT: + if (tree->search.search == true) { + break; + } if (ns.curr != NULL && - ns.curr->parent != NULL && - ns.curr->parent->type != TREE_NODE_ROOT) { + ns.curr->parent != NULL && + ns.curr->parent->type != TREE_NODE_ROOT) { /* Step to parent */ ns.curr->parent->flags |= TV_NFLAGS_SELECTED; @@ -2784,7 +3843,7 @@ static bool treeview_keyboard_navigation(treeview *tree, uint32_t key, if (ns.curr->children != NULL) { /* Step to first child */ ns.curr->children->flags |= - TV_NFLAGS_SELECTED; + TV_NFLAGS_SELECTED; } else { /* Retain current node selection */ ns.curr->flags |= TV_NFLAGS_SELECTED; @@ -2824,8 +3883,8 @@ static bool treeview_keyboard_navigation(treeview *tree, uint32_t key, rect->x0 = 0; rect->y0 = 0; rect->x1 = REDRAW_MAX; - if (tree->root->height > h) - rect->y1 = tree->root->height; + if (treeview__get_display_height(tree) + search_height > h) + rect->y1 = treeview__get_display_height(tree) + search_height; else rect->y1 = h; redraw = true; @@ -2842,7 +3901,7 @@ bool treeview_keypress(treeview *tree, uint32_t key) assert(tree != NULL); - /* Pass to textarea, if editing in progress */ + /* Pass to any textarea, if editing in progress */ if (tree->edit.textarea != NULL) { switch (key) { case NS_KEY_ESCAPE: @@ -2855,6 +3914,17 @@ bool treeview_keypress(treeview *tree, uint32_t key) default: return textarea_keypress(tree->edit.textarea, key); } + } else if (tree->search.active == true) { + switch (key) { + case NS_KEY_ESCAPE: + treeview__search_cancel(tree, false); + return true; + case NS_KEY_NL: + case NS_KEY_CR: + return true; + default: + return textarea_keypress(tree->search.textarea, key); + } } /* Keypress to be handled by treeview */ @@ -2878,7 +3948,7 @@ bool treeview_keypress(treeview *tree, uint32_t key) if (tree->root->height != h) { r.y0 = 0; treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); } } break; @@ -2901,7 +3971,7 @@ bool treeview_keypress(treeview *tree, uint32_t key) } if (redraw) { - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } return true; @@ -2920,9 +3990,14 @@ bool treeview_keypress(treeview *tree, uint32_t key) * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ -static bool treeview_set_move_indicator(treeview *tree, bool need_redraw, - treeview_node *target, int node_height, - int node_y, int mouse_y, struct rect *rect) +static bool +treeview_set_move_indicator(treeview *tree, + bool need_redraw, + treeview_node *target, + int node_height, + int node_y, + int mouse_y, + struct rect *rect) { treeview_node *orig = target; enum treeview_target_pos target_pos; @@ -2937,44 +4012,44 @@ static bool treeview_set_move_indicator(treeview *tree, bool need_redraw, if (target->flags & TV_NFLAGS_SELECTED) { /* Find top selected ancestor */ while (target->parent && - target->parent->flags & TV_NFLAGS_SELECTED) { + target->parent->flags & TV_NFLAGS_SELECTED) { target = target->parent; } - /* Find top ajdacent selected sibling */ + /* Find top adjacent selected sibling */ while (target->prev_sib && - target->prev_sib->flags & TV_NFLAGS_SELECTED) { + target->prev_sib->flags & TV_NFLAGS_SELECTED) { target = target->prev_sib; } target_pos = TV_TARGET_ABOVE; } else switch (target->type) { - case TREE_NODE_FOLDER: - if (mouse_pos <= node_height / 4) { - target_pos = TV_TARGET_ABOVE; - } else if (mouse_pos <= (3 * node_height) / 4 || - target->flags & TV_NFLAGS_EXPANDED) { - target_pos = TV_TARGET_INSIDE; - } else { - target_pos = TV_TARGET_BELOW; - } - break; + case TREE_NODE_FOLDER: + if (mouse_pos <= node_height / 4) { + target_pos = TV_TARGET_ABOVE; + } else if (mouse_pos <= (3 * node_height) / 4 || + target->flags & TV_NFLAGS_EXPANDED) { + target_pos = TV_TARGET_INSIDE; + } else { + target_pos = TV_TARGET_BELOW; + } + break; - case TREE_NODE_ENTRY: - if (mouse_pos <= node_height / 2) { - target_pos = TV_TARGET_ABOVE; - } else { - target_pos = TV_TARGET_BELOW; - } - break; + case TREE_NODE_ENTRY: + if (mouse_pos <= node_height / 2) { + target_pos = TV_TARGET_ABOVE; + } else { + target_pos = TV_TARGET_BELOW; + } + break; - default: - assert(target->type != TREE_NODE_ROOT); - return false; - } + default: + assert(target->type != TREE_NODE_ROOT); + return false; + } if (target_pos == tree->move.target_pos && - target == tree->move.target) { + target == tree->move.target) { /* No change */ return need_redraw; } @@ -3008,7 +4083,7 @@ static bool treeview_set_move_indicator(treeview *tree, bool need_redraw, /* Oftsets are all relative to centred (INSIDE) */ node_y += (tree_g.line_height - - treeview_res[TREE_RES_ARROW].height + 1) / 2; + treeview_res[TREE_RES_ARROW].height + 1) / 2; x = target->inset + tree_g.move_offset; @@ -3043,6 +4118,9 @@ static bool treeview_set_move_indicator(treeview *tree, bool need_redraw, /** * Callback for textarea_create, in desktop/treeview.h + * + * \param data treeview context + * \param msg textarea message */ static void treeview_textarea_callback(void *data, struct textarea_msg *msg) { @@ -3069,7 +4147,7 @@ static void treeview_textarea_callback(void *data, struct textarea_msg *msg) r->y1 += tree->edit.y; /* Redraw the textarea */ - treeview__cw_redraw_request(tree, r); + treeview__cw_invalidate_area(tree, r); break; default: @@ -3081,16 +4159,21 @@ static void treeview_textarea_callback(void *data, struct textarea_msg *msg) /** * Start edit of node field, at given y-coord, if editable * - * \param tree Treeview object to consider editing in - * \param n The treeview node to try editing - * \param node_y The Y coord of the top of n - * \param mouse_x X coord of mouse position - * \param mouse_y Y coord of mouse position - * \param rect Redraw rectangle (if redraw required) + * \param tree Treeview object to consider editing in + * \param n The treeview node to try editing + * \param node_y The Y coord of the top of n + * \param mouse_x X coord of mouse position + * \param mouse_y Y coord of mouse position + * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ -static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, - int node_y, int mouse_x, int mouse_y, struct rect *rect) +static bool +treeview_edit_node_at_point(treeview *tree, + treeview_node *n, + int node_y, + int mouse_x, + int mouse_y, + struct rect *rect) { struct treeview_text *field_data = NULL; struct treeview_field *ef, *field_desc = NULL; @@ -3098,8 +4181,6 @@ static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, int field_y = node_y; int field_x; int width, height; - textarea_setup ta_setup; - textarea_flags ta_flags; bool success; /* If the main field is editable, make field_data point to it */ @@ -3125,7 +4206,7 @@ static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, ef = &(tree->fields[i + 1]); pos += tree_g.line_height; if (mouse_y <= pos && (ef->flags & - TREE_FLAG_ALLOW_EDIT)) { + TREE_FLAG_ALLOW_EDIT)) { field_data = &e->fields[i].value; field_desc = ef; field_y = pos - tree_g.line_height; @@ -3141,31 +4222,15 @@ static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, /* Get window width/height */ treeview__cw_get_window_dimensions(tree, &width, &height); - /* Anow textarea width/height */ + /* Calculate textarea width/height */ field_x = n->inset + tree_g.step_width + tree_g.icon_step - 3; width -= field_x; height = tree_g.line_height; - /* Configure the textarea */ - ta_flags = TEXTAREA_INTERNAL_CARET; - - ta_setup.width = width; - ta_setup.height = height; - ta_setup.pad_top = 0; - ta_setup.pad_right = 2; - ta_setup.pad_bottom = 0; - ta_setup.pad_left = 2; - ta_setup.border_width = 1; - ta_setup.border_col = 0x000000; - ta_setup.selected_text = 0xffffff; - ta_setup.selected_bg = 0x000000; - ta_setup.text = plot_style_odd.text; - ta_setup.text.foreground = 0x000000; - ta_setup.text.background = 0xffffff; - /* Create text area */ - tree->edit.textarea = textarea_create(ta_flags, &ta_setup, - treeview_textarea_callback, tree); + tree->edit.textarea = treeview__create_textarea(tree, width, height, + 0x000000, 0xffffff, 0x000000, plot_style_odd.text, + treeview_textarea_callback); if (tree->edit.textarea == NULL) { return false; } @@ -3187,8 +4252,8 @@ static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, mouse_x = width - 1; textarea_mouse_action(tree->edit.textarea, - BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1, - mouse_x, tree_g.line_height / 2); + BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1, + mouse_x, tree_g.line_height / 2); /* Position the textarea */ tree->edit.x = field_x; @@ -3232,7 +4297,7 @@ void treeview_edit_selection(treeview *tree) /* Edit node at y */ redraw = treeview_edit_node_at_point(tree, n, y, - 0, y + tree_g.line_height / 2, &rect); + 0, y + tree_g.line_height / 2, &rect); if (redraw == false) return; @@ -3242,20 +4307,37 @@ void treeview_edit_selection(treeview *tree) rect.y0 = y; rect.x1 = REDRAW_MAX; rect.y1 = y + tree_g.line_height; - treeview__cw_redraw_request(tree, &rect); + treeview__cw_invalidate_area(tree, &rect); } +/** + * context for treeview mouse handling + */ struct treeview_mouse_action { treeview *tree; browser_mouse_state mouse; int x; int y; int current_y; /* Y coordinate value of top of current node */ + int search_height; }; -/** Treewalk node callback for handling mouse action. */ -static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node callback for handling mouse action. + * + * \param node current node + * \param ctx node context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_node_mouse_action_cb(treeview_node *node, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_mouse_action *ma = ctx; struct rect r; @@ -3273,7 +4355,7 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, r.x1 = REDRAW_MAX; height = (node->type == TREE_NODE_ENTRY) ? node->height : - tree_g.line_height; + tree_g.line_height; /* Skip line if we've not reached mouse y */ if (ma->y > ma->current_y + height) { @@ -3283,25 +4365,29 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, /* Find where the mouse is */ if (ma->y <= ma->current_y + tree_g.line_height) { - if (ma->x >= node->inset - 1 && - ma->x < node->inset + tree_g.step_width) { + int inset = node->inset; + if (ma->tree->search.search == true) { + inset = tree_g.window_padding; + } + if (ma->x >= inset - 1 && + ma->x < inset + tree_g.step_width) { /* Over expansion toggle */ part = TV_NODE_PART_TOGGLE; - } else if (ma->x >= node->inset + tree_g.step_width && - ma->x < node->inset + tree_g.step_width + + } else if (ma->x >= inset + tree_g.step_width && + ma->x < inset + tree_g.step_width + tree_g.icon_step + node->text.width) { /* On node */ part = TV_NODE_PART_ON_NODE; } } else if (node->type == TREE_NODE_ENTRY && - height > tree_g.line_height) { + height > tree_g.line_height) { /* Expanded entries */ int x = node->inset + tree_g.step_width + tree_g.icon_step; int y = ma->current_y + tree_g.line_height; int i; struct treeview_node_entry *entry = - (struct treeview_node_entry *)node; + (struct treeview_node_entry *)node; for (i = 0; i < ma->tree->n_fields - 1; i++) { struct treeview_field *ef = &(ma->tree->fields[i + 1]); @@ -3314,21 +4400,21 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, int max_width = ma->tree->field_width; if (ma->x >= x + max_width - ef->value.width - - tree_g.step_width && - ma->x < x + max_width - - tree_g.step_width) { + tree_g.step_width && + ma->x < x + max_width - + tree_g.step_width) { /* On a field name */ part = TV_NODE_PART_ON_NODE; } else if (ma->x >= x + max_width && - ma->x < x + max_width + - entry->fields[i].value.width) { + ma->x < x + max_width + + entry->fields[i].value.width) { /* On a field value */ part = TV_NODE_PART_ON_NODE; } } else { if (ma->x >= x && ma->x < x + - entry->fields[i].value.width) { + entry->fields[i].value.width) { /* On a field value */ part = TV_NODE_PART_ON_NODE; } @@ -3340,7 +4426,7 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, /* Record what position / part a drag started on */ if (ma->mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) && - ma->tree->drag.type == TV_DRAG_NONE) { + ma->tree->drag.type == TV_DRAG_NONE) { ma->tree->drag.selected = node->flags & TV_NFLAGS_SELECTED; ma->tree->drag.start_node = node; ma->tree->drag.part = part; @@ -3358,29 +4444,30 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, /* Handle drag start */ if (ma->tree->drag.type == TV_DRAG_NONE) { if (ma->mouse & BROWSER_MOUSE_DRAG_1 && - ma->tree->drag.selected == false && - ma->tree->drag.part == TV_NODE_PART_NONE) { + ma->tree->drag.selected == false && + ma->tree->drag.part == TV_NODE_PART_NONE) { ma->tree->drag.type = TV_DRAG_SELECTION; treeview__cw_drag_status(ma->tree, - CORE_WINDOW_DRAG_SELECTION); + CORE_WINDOW_DRAG_SELECTION); - } else if (!(ma->tree->flags & TREEVIEW_NO_MOVES) && - ma->mouse & BROWSER_MOUSE_DRAG_1 && - (ma->tree->drag.selected == true || - ma->tree->drag.part == TV_NODE_PART_ON_NODE)) { + } else if (ma->tree->search.search == false && + !(ma->tree->flags & TREEVIEW_NO_MOVES) && + ma->mouse & BROWSER_MOUSE_DRAG_1 && + (ma->tree->drag.selected == true || + ma->tree->drag.part == TV_NODE_PART_ON_NODE)) { ma->tree->drag.type = TV_DRAG_MOVE; treeview__cw_drag_status(ma->tree, - CORE_WINDOW_DRAG_MOVE); + CORE_WINDOW_DRAG_MOVE); redraw |= treeview_propagate_selection(ma->tree, &r); } else if (ma->mouse & BROWSER_MOUSE_DRAG_2) { ma->tree->drag.type = TV_DRAG_SELECTION; treeview__cw_drag_status(ma->tree, - CORE_WINDOW_DRAG_SELECTION); + CORE_WINDOW_DRAG_SELECTION); } if (ma->tree->drag.start_node != NULL && - ma->tree->drag.type == TV_DRAG_SELECTION) { + ma->tree->drag.type == TV_DRAG_SELECTION) { ma->tree->drag.start_node->flags ^= TV_NFLAGS_SELECTED; } } @@ -3391,10 +4478,10 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, { int curr_y1 = ma->current_y + height; int prev_y1 = ma->tree->drag.prev.node_y + - ma->tree->drag.prev.node_h; + ma->tree->drag.prev.node_h; r.y0 = (ma->current_y < ma->tree->drag.prev.node_y) ? - ma->current_y : ma->tree->drag.prev.node_y; + ma->current_y : ma->tree->drag.prev.node_y; r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1; redraw = true; @@ -3408,7 +4495,8 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, case TV_DRAG_MOVE: redraw |= treeview_set_move_indicator(ma->tree, redraw, - node, height, ma->current_y, ma->y, &r); + node, height, + ma->current_y, ma->y, &r); break; default: @@ -3418,9 +4506,10 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, click = ma->mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2); if (((node->type == TREE_NODE_FOLDER) && - (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) || - (part == TV_NODE_PART_TOGGLE && click)) { - int h = ma->tree->root->height; + (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) || + (part == TV_NODE_PART_TOGGLE && click)) { + int h = treeview__get_display_height(ma->tree) + + ma->search_height; /* Clear any existing selection */ redraw |= treeview_clear_selection(ma->tree, &r); @@ -3438,11 +4527,17 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, /* Set up redraw */ if (!redraw || r.y0 > ma->current_y) r.y0 = ma->current_y; - r.y1 = h > ma->tree->root->height ? h : ma->tree->root->height; + if (h > treeview__get_display_height(ma->tree) + + ma->search_height) { + r.y1 = h; + } else { + r.y1 = treeview__get_display_height(ma->tree) + + ma->search_height; + } redraw = true; } else if ((node->type == TREE_NODE_ENTRY) && - (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) { + (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) { struct treeview_node_msg msg; msg.msg = TREE_MSG_NODE_LAUNCH; msg.data.node_launch.mouse = ma->mouse; @@ -3454,28 +4549,29 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, ma->tree->callbacks->entry(msg, node->client_data); } else if (ma->mouse & BROWSER_MOUSE_PRESS_2 || - (ma->mouse & BROWSER_MOUSE_PRESS_1 && - ma->mouse & BROWSER_MOUSE_MOD_2)) { + (ma->mouse & BROWSER_MOUSE_PRESS_1 && + ma->mouse & BROWSER_MOUSE_MOD_2)) { /* Toggle selection of node */ action |= TV_NODE_ACTION_SELECTION; } else if (ma->mouse & BROWSER_MOUSE_CLICK_1 && - ma->mouse & - (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3) && - part != TV_NODE_PART_TOGGLE) { + ma->mouse & + (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3) && + part != TV_NODE_PART_TOGGLE) { /* Clear any existing selection */ redraw |= treeview_clear_selection(ma->tree, &r); /* Edit node */ redraw |= treeview_edit_node_at_point(ma->tree, node, - ma->current_y, ma->x, ma->y, &r); + ma->current_y, ma->x, + ma->y, &r); } else if (ma->mouse & BROWSER_MOUSE_PRESS_1 && - !(ma->mouse & - (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3)) && - !(node->flags & TV_NFLAGS_SELECTED) && - part != TV_NODE_PART_TOGGLE) { + !(ma->mouse & + (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3)) && + !(node->flags & TV_NFLAGS_SELECTED) && + part != TV_NODE_PART_TOGGLE) { /* Clear any existing selection */ redraw |= treeview_clear_selection(ma->tree, &r); @@ -3494,35 +4590,69 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, r.y1 = ma->current_y + height; redraw = true; } else { - if (r.y0 > ma->current_y) + if (r.y0 > ma->current_y) { r.y0 = ma->current_y; - if (r.y1 < ma->current_y + height) + } + if (r.y1 < ma->current_y + height) { r.y1 = ma->current_y + height; + } } } if (redraw) { - treeview__cw_redraw_request(ma->tree, &r); + treeview__cw_invalidate_area(ma->tree, &r); } *end = true; /* Reached line with click; stop walking tree */ return NSERROR_OK; } + + /* Exported interface, documented in treeview.h */ -void treeview_mouse_action(treeview *tree, - browser_mouse_state mouse, int x, int y) +void +treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y) { struct rect r; bool redraw = false; + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; assert(tree != NULL); assert(tree->root != NULL); + /* Not interested in whether mouse leaves window. */ + if (mouse == BROWSER_MOUSE_LEAVE) { + return; + } + /* Handle mouse drag captured by textarea */ if (tree->drag.type == TV_DRAG_TEXTAREA) { textarea_mouse_action(tree->edit.textarea, mouse, - x - tree->edit.x, y - tree->edit.y); + x - tree->edit.x, y - tree->edit.y); + return; + } else if (tree->drag.type == TV_DRAG_SEARCH || + (y < search_height && + tree->drag.type == TV_DRAG_NONE)) { + if (tree->search.active == false) { + tree->search.active = true; + if (treeview_clear_selection(tree, &r)) { + treeview__cw_invalidate_area(tree, &r); + } + } + textarea_mouse_action(tree->search.textarea, mouse, + x - tree_g.window_padding - tree_g.icon_size, + y); return; + } else if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) && + tree->search.active == true) { + + tree->search.active = false; + textarea_set_caret(tree->search.textarea, -1); + r.x0 = 0; + r.y0 = 0; + r.x1 = REDRAW_MAX; + r.y1 = tree_g.line_height; + treeview__cw_invalidate_area(tree, &r); } /* Handle textarea related mouse action */ @@ -3531,10 +4661,10 @@ void treeview_mouse_action(treeview *tree, int ta_y = y - tree->edit.y; if (ta_x > 0 && ta_x < tree->edit.w && - ta_y > 0 && ta_y < tree->edit.h) { + ta_y > 0 && ta_y < tree->edit.h) { /* Inside textarea */ textarea_mouse_action(tree->edit.textarea, mouse, - ta_x, ta_y); + ta_x, ta_y); return; } else if (mouse != BROWSER_MOUSE_HOVER) { @@ -3562,7 +4692,7 @@ void treeview_mouse_action(treeview *tree, tree->move.target_pos = TV_TARGET_NONE; treeview__cw_drag_status(tree, CORE_WINDOW_DRAG_NONE); - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); return; default: /* No drag to end */ @@ -3570,7 +4700,7 @@ void treeview_mouse_action(treeview *tree, } } - if (y > tree->root->height) { + if (y > treeview__get_display_height(tree) + search_height) { /* Below tree */ r.x0 = 0; @@ -3578,7 +4708,7 @@ void treeview_mouse_action(treeview *tree, /* Record what position / part a drag started on */ if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) && - tree->drag.type == TV_DRAG_NONE) { + tree->drag.type == TV_DRAG_NONE) { tree->drag.selected = false; tree->drag.start_node = NULL; tree->drag.part = TV_NODE_PART_NONE; @@ -3596,8 +4726,8 @@ void treeview_mouse_action(treeview *tree, /* Handle drag start */ if (tree->drag.type == TV_DRAG_NONE) { if (mouse & BROWSER_MOUSE_DRAG_1 && - tree->drag.selected == false && - tree->drag.part == TV_NODE_PART_NONE) { + tree->drag.selected == false && + tree->drag.part == TV_NODE_PART_NONE) { tree->drag.type = TV_DRAG_SELECTION; treeview__cw_drag_status(tree, CORE_WINDOW_DRAG_SELECTION); @@ -3608,9 +4738,9 @@ void treeview_mouse_action(treeview *tree, } if (tree->drag.start_node != NULL && - tree->drag.type == TV_DRAG_SELECTION) { + tree->drag.type == TV_DRAG_SELECTION) { tree->drag.start_node->flags ^= - TV_NFLAGS_SELECTED; + TV_NFLAGS_SELECTED; } } @@ -3618,7 +4748,7 @@ void treeview_mouse_action(treeview *tree, if (tree->drag.type == TV_DRAG_SELECTION) { int curr_y1 = tree->root->height; int prev_y1 = tree->drag.prev.node_y + - tree->drag.prev.node_h; + tree->drag.prev.node_h; r.y0 = tree->drag.prev.node_y; r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1; @@ -3637,20 +4767,22 @@ void treeview_mouse_action(treeview *tree, } if (redraw) { - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } } else { /* On tree */ - struct treeview_mouse_action ma; - - ma.tree = tree; - ma.mouse = mouse; - ma.x = x; - ma.y = y; - ma.current_y = 0; - - treeview_walk_internal(tree->root, false, NULL, + struct treeview_mouse_action ma = { + ma.tree = tree, + ma.mouse = mouse, + ma.x = x, + ma.y = y, + ma.current_y = search_height, + ma.search_height = search_height, + }; + + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_mouse_action_cb, &ma); } } @@ -3659,78 +4791,111 @@ void treeview_mouse_action(treeview *tree, /* Exported interface, documented in treeview.h */ int treeview_get_height(treeview *tree) { + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + int height = treeview__get_display_height(tree); + assert(tree != NULL); assert(tree->root != NULL); - treeview__cw_update_size(tree, -1, tree->root->height); + treeview__cw_update_size(tree, -1, height); - return tree->root->height; + return height + search_height; } /** * Initialise the plot styles from CSS system colour values. + * + * \param font_pt_size font size to use + * \return NSERROR_OK on success else appropriate error code */ -static void treeview_init_plot_styles(int font_pt_size) +static nserror treeview_init_plot_styles(int font_pt_size) { + nserror res; + /* Background colour */ plot_style_even.bg.stroke_type = PLOT_OP_TYPE_NONE; plot_style_even.bg.stroke_width = 0; plot_style_even.bg.stroke_colour = 0; plot_style_even.bg.fill_type = PLOT_OP_TYPE_SOLID; - plot_style_even.bg.fill_colour = ns_system_colour_char("Window"); + res = ns_system_colour_char("Window", &plot_style_even.bg.fill_colour); + if (res != NSERROR_OK) { + return res; + } /* Text colour */ plot_style_even.text.family = PLOT_FONT_FAMILY_SANS_SERIF; plot_style_even.text.size = font_pt_size; plot_style_even.text.weight = 400; plot_style_even.text.flags = FONTF_NONE; - plot_style_even.text.foreground = ns_system_colour_char("WindowText"); - plot_style_even.text.background = ns_system_colour_char("Window"); + res = ns_system_colour_char("WindowText", &plot_style_even.text.foreground); + if (res != NSERROR_OK) { + return res; + } + res = ns_system_colour_char("Window", &plot_style_even.text.background); + if (res != NSERROR_OK) { + return res; + } /* Entry field text colour */ plot_style_even.itext = plot_style_even.text; plot_style_even.itext.foreground = mix_colour( - plot_style_even.text.foreground, - plot_style_even.text.background, 255 * 10 / 16); + plot_style_even.text.foreground, + plot_style_even.text.background, + 255 * 10 / 16); /* Selected background colour */ plot_style_even.sbg = plot_style_even.bg; - plot_style_even.sbg.fill_colour = ns_system_colour_char("Highlight"); + res = ns_system_colour_char("Highlight", &plot_style_even.sbg.fill_colour); + if (res != NSERROR_OK) { + return res; + } /* Selected text colour */ plot_style_even.stext = plot_style_even.text; - plot_style_even.stext.foreground = - ns_system_colour_char("HighlightText"); - plot_style_even.stext.background = ns_system_colour_char("Highlight"); + res = ns_system_colour_char("HighlightText", &plot_style_even.stext.foreground); + if (res != NSERROR_OK) { + return res; + } + res = ns_system_colour_char("Highlight", &plot_style_even.stext.background); + if (res != NSERROR_OK) { + return res; + } /* Selected entry field text colour */ plot_style_even.sitext = plot_style_even.stext; plot_style_even.sitext.foreground = mix_colour( - plot_style_even.stext.foreground, - plot_style_even.stext.background, 255 * 25 / 32); - + plot_style_even.stext.foreground, + plot_style_even.stext.background, + 255 * 25 / 32); /* Odd numbered node styles */ plot_style_odd.bg = plot_style_even.bg; plot_style_odd.bg.fill_colour = mix_colour( - plot_style_even.bg.fill_colour, - plot_style_even.text.foreground, 255 * 15 / 16); + plot_style_even.bg.fill_colour, + plot_style_even.text.foreground, 255 * 15 / 16); plot_style_odd.text = plot_style_even.text; plot_style_odd.text.background = plot_style_odd.bg.fill_colour; plot_style_odd.itext = plot_style_odd.text; plot_style_odd.itext.foreground = mix_colour( - plot_style_odd.text.foreground, - plot_style_odd.text.background, 255 * 10 / 16); + plot_style_odd.text.foreground, + plot_style_odd.text.background, 255 * 10 / 16); plot_style_odd.sbg = plot_style_even.sbg; plot_style_odd.stext = plot_style_even.stext; plot_style_odd.sitext = plot_style_even.sitext; + + return NSERROR_OK; } /** - * Callback for hlcache. + * Callback for hlcache retrieving resources. + * + * \param handle content hlcache handle + * \param event The event that occurred on the content + * \param pw treeview resource context */ static nserror treeview_res_cb(struct hlcache_handle *handle, @@ -3767,9 +4932,10 @@ static void treeview_init_resources(void) treeview_res[i].height = 0; if (nsurl_create(treeview_res[i].url, &url) == NSERROR_OK) { hlcache_handle_retrieve(url, 0, NULL, NULL, - treeview_res_cb, - &(treeview_res[i]), NULL, - CONTENT_IMAGE, &(treeview_res[i].c)); + treeview_res_cb, + &(treeview_res[i]), NULL, + CONTENT_IMAGE, + &(treeview_res[i].c)); nsurl_unref(url); } } @@ -3779,12 +4945,12 @@ static void treeview_init_resources(void) /** * Create a right-pointing anti-aliased triangle bitmap * - * bg background colour - * fg foreground colour - * size required bitmap size + * \param bg background colour + * \param fg foreground colour + * \param size required bitmap size */ -static struct bitmap * treeview_generate_triangle_bitmap( - colour bg, colour fg, int size) +static struct bitmap * +treeview_generate_triangle_bitmap(colour bg, colour fg, int size) { struct bitmap *b = NULL; int x, y; @@ -3881,11 +5047,11 @@ static struct bitmap * treeview_generate_triangle_bitmap( /** * Create bitmap copy of another bitmap * - * orig bitmap to copy - * size required bitmap size + * \param orig bitmap to copy + * \param size required bitmap size */ -static struct bitmap * treeview_generate_copy_bitmap( - struct bitmap *orig, int size) +static struct bitmap * +treeview_generate_copy_bitmap(struct bitmap *orig, int size) { struct bitmap *b = NULL; unsigned char *data; @@ -3926,11 +5092,11 @@ static struct bitmap * treeview_generate_copy_bitmap( /** * Create bitmap from rotation of another bitmap * - * orig bitmap to create rotation of - * size required bitmap size + * \param orig bitmap to create rotation of + * \param size required bitmap size */ -static struct bitmap * treeview_generate_rotate_bitmap( - struct bitmap *orig, int size) +static struct bitmap * +treeview_generate_rotate_bitmap(struct bitmap *orig, int size) { struct bitmap *b = NULL; int x, y; @@ -3967,7 +5133,7 @@ static struct bitmap * treeview_generate_rotate_bitmap( *(pos++) = *(orig_pos++); *(pos++) = *(orig_pos); *(pos++) = 0xff; - + } rpos += stride; @@ -3987,6 +5153,8 @@ static struct bitmap * treeview_generate_rotate_bitmap( /** * Measures width of characters used to represent treeview furniture. + * + * \return NSERROR_OK on success else error code */ static nserror treeview_init_furniture(void) { @@ -3994,47 +5162,47 @@ static nserror treeview_init_furniture(void) plot_style_odd.furn[TREE_FURN_EXPAND].size = size; plot_style_odd.furn[TREE_FURN_EXPAND].bmp = - treeview_generate_triangle_bitmap( + treeview_generate_triangle_bitmap( plot_style_odd.bg.fill_colour, plot_style_odd.itext.foreground, size); plot_style_odd.furn[TREE_FURN_EXPAND].sel = - treeview_generate_triangle_bitmap( + treeview_generate_triangle_bitmap( plot_style_odd.sbg.fill_colour, plot_style_odd.sitext.foreground, size); plot_style_even.furn[TREE_FURN_EXPAND].size = size; plot_style_even.furn[TREE_FURN_EXPAND].bmp = - treeview_generate_triangle_bitmap( + treeview_generate_triangle_bitmap( plot_style_even.bg.fill_colour, plot_style_even.itext.foreground, size); plot_style_even.furn[TREE_FURN_EXPAND].sel = - treeview_generate_copy_bitmap( + treeview_generate_copy_bitmap( plot_style_odd.furn[TREE_FURN_EXPAND].sel, size); plot_style_odd.furn[TREE_FURN_CONTRACT].size = size; plot_style_odd.furn[TREE_FURN_CONTRACT].bmp = - treeview_generate_rotate_bitmap( + treeview_generate_rotate_bitmap( plot_style_odd.furn[TREE_FURN_EXPAND].bmp, size); plot_style_odd.furn[TREE_FURN_CONTRACT].sel = - treeview_generate_rotate_bitmap( + treeview_generate_rotate_bitmap( plot_style_odd.furn[TREE_FURN_EXPAND].sel, size); plot_style_even.furn[TREE_FURN_CONTRACT].size = size; plot_style_even.furn[TREE_FURN_CONTRACT].bmp = - treeview_generate_rotate_bitmap( + treeview_generate_rotate_bitmap( plot_style_even.furn[TREE_FURN_EXPAND].bmp, size); plot_style_even.furn[TREE_FURN_CONTRACT].sel = - treeview_generate_rotate_bitmap( + treeview_generate_rotate_bitmap( plot_style_even.furn[TREE_FURN_EXPAND].sel, size); if (plot_style_odd.furn[TREE_FURN_EXPAND].bmp == NULL || - plot_style_odd.furn[TREE_FURN_EXPAND].sel == NULL || - plot_style_even.furn[TREE_FURN_EXPAND].bmp == NULL || - plot_style_even.furn[TREE_FURN_EXPAND].sel == NULL || - plot_style_odd.furn[TREE_FURN_CONTRACT].bmp == NULL || - plot_style_odd.furn[TREE_FURN_CONTRACT].sel == NULL || - plot_style_even.furn[TREE_FURN_CONTRACT].bmp == NULL || - plot_style_even.furn[TREE_FURN_CONTRACT].sel == NULL) + plot_style_odd.furn[TREE_FURN_EXPAND].sel == NULL || + plot_style_even.furn[TREE_FURN_EXPAND].bmp == NULL || + plot_style_even.furn[TREE_FURN_EXPAND].sel == NULL || + plot_style_odd.furn[TREE_FURN_CONTRACT].bmp == NULL || + plot_style_odd.furn[TREE_FURN_CONTRACT].sel == NULL || + plot_style_even.furn[TREE_FURN_CONTRACT].bmp == NULL || + plot_style_even.furn[TREE_FURN_CONTRACT].sel == NULL) return NSERROR_NOMEM; tree_g.furniture_width = size + tree_g.line_height / 4; @@ -4048,14 +5216,14 @@ nserror treeview_init(void) { long long font_px_size; long long font_pt_size; - nserror err; + nserror res; if (tree_g.initialised > 0) { tree_g.initialised++; return NSERROR_OK; } - LOG("Initialising treeview module"); + NSLOG(netsurf, INFO, "Initialising treeview module"); font_pt_size = nsoption_int(treeview_font_size); if (font_pt_size <= 0) { @@ -4066,11 +5234,17 @@ nserror treeview_init(void) 10 + 36) / 72; tree_g.line_height = (font_px_size * 8 + 3) / 6; - treeview_init_plot_styles(font_pt_size * FONT_SIZE_SCALE / 10); + res = treeview_init_plot_styles(font_pt_size * PLOT_STYLE_SCALE / 10); + if (res != NSERROR_OK) { + return res; + } + treeview_init_resources(); - err = treeview_init_furniture(); - if (err != NSERROR_OK) - return err; + + res = treeview_init_furniture(); + if (res != NSERROR_OK) { + return res; + } tree_g.step_width = tree_g.furniture_width; tree_g.window_padding = 6; @@ -4080,7 +5254,7 @@ nserror treeview_init(void) tree_g.initialised++; - LOG("Initialised treeview module"); + NSLOG(netsurf, INFO, "Initialised treeview module"); return NSERROR_OK; } @@ -4096,11 +5270,12 @@ nserror treeview_fini(void) return NSERROR_OK; } else if (tree_g.initialised == 0) { - LOG("Warning: tried to finalise uninitialised treeview module"); + NSLOG(netsurf, INFO, + "Warning: tried to finalise uninitialised treeview module"); return NSERROR_OK; } - LOG("Finalising treeview module"); + NSLOG(netsurf, INFO, "Finalising treeview module"); for (i = 0; i < TREE_RES_LAST; i++) { hlcache_handle_release(treeview_res[i].c); @@ -4117,7 +5292,7 @@ nserror treeview_fini(void) tree_g.initialised--; - LOG("Finalised treeview module"); + NSLOG(netsurf, INFO, "Finalised treeview module"); return NSERROR_OK; } |