From 49c7994d0580a655ddcc78e07205eea45d74132c Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Mon, 2 Sep 2013 16:19:16 +0100 Subject: Remove bulk of old treeview implementation. --- desktop/tree.c | 3092 ++------------------------------------------------------ desktop/tree.h | 176 +--- 2 files changed, 78 insertions(+), 3190 deletions(-) (limited to 'desktop') diff --git a/desktop/tree.c b/desktop/tree.c index 607d12ba0..9e86668ce 100644 --- a/desktop/tree.c +++ b/desktop/tree.c @@ -30,152 +30,21 @@ #include "content/hlcache.h" #include "css/utils.h" #include "desktop/browser.h" -#include "desktop/knockout.h" -#include "desktop/textarea.h" -#include "desktop/textinput.h" #include "desktop/tree.h" #include "utils/nsoption.h" -#include "desktop/plotters.h" #include "render/font.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/utils.h" #include "utils/url.h" -#undef TREE_NOISY_DEBUG - -#define MAXIMUM_URL_LENGTH 1024 - -#define TREE_TEXT_SIZE_PT 11 -#define TREE_ICON_SIZE 17 -#define NODE_INSTEP 20 - -static int tree_text_size_px; -static int TREE_LINE_HEIGHT; - -static char *tree_icons_dir = NULL; - -static plot_font_style_t plot_fstyle = { - .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = TREE_TEXT_SIZE_PT * FONT_SIZE_SCALE, - .weight = 400, - .flags = FONTF_NONE -}; - -static plot_font_style_t plot_fstyle_def_folder = { - .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = TREE_TEXT_SIZE_PT * FONT_SIZE_SCALE, - .weight = 700, - .flags = FONTF_NONE -}; - -static plot_font_style_t plot_fstyle_selected = { - .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = TREE_TEXT_SIZE_PT * FONT_SIZE_SCALE, - .weight = 400, - .flags = FONTF_NONE -}; - -static plot_font_style_t plot_fstyle_selected_def_folder = { - .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = TREE_TEXT_SIZE_PT * FONT_SIZE_SCALE, - .weight = 700, - .flags = FONTF_NONE -}; - -/** plot style for treeview backgrounds. */ -static plot_style_t plot_style_fill_tree_background = { - .fill_type = PLOT_OP_TYPE_SOLID -}; - -/** plot style for treeview backgrounds. */ -static plot_style_t plot_style_fill_tree_selected = { - .fill_type = PLOT_OP_TYPE_SOLID -}; - -/** plot style for treeview furniture lines. */ -static plot_style_t plot_style_stroke_tree_furniture = { - .stroke_type = PLOT_OP_TYPE_SOLID -}; - -/** plot style for treeview furniture fills. */ -static plot_style_t plot_style_fill_tree_furniture = { - .fill_type = PLOT_OP_TYPE_SOLID -}; - -struct node; -struct tree; - -struct node_element_box { - int x; /**< X offset from origin */ - int y; /**< Y offset from origin */ - int width; /**< Element width */ - int height; /**< Element height */ -}; - -struct node_element { - struct node *parent; /**< Parent node */ - node_element_type type; /**< Element type */ - struct node_element_box box; /**< Element bounding box */ - char *text; /**< Text for the element */ - void *bitmap; /**< Bitmap for the element */ - struct node_element *next; /**< Next node element */ - unsigned int flag; /**< Client specified flag for data - being represented */ - bool editable; /**< Whether the node text can be - * modified, editable text is deleted - * without noticing the tree user - */ -}; - -struct node { - bool selected; /**< Whether the node is selected */ - bool expanded; /**< Whether the node is expanded */ - bool folder; /**< Whether the node is a folder */ - bool def_folder; /**< Whether the node is the default folder */ - bool retain_in_memory; /**< Whether the node remains - in memory after deletion */ - bool deleted; /**< Whether the node is currently - deleted */ - bool processing; /**< Internal flag used when moving */ - struct node_element_box box; /**< Bounding box of all elements */ - struct node_element data; /**< Data to display */ - struct node *parent; /**< Parent entry (NULL for root) */ - struct node *child; /**< First child */ - struct node *last_child; /**< Last child */ - struct node *previous; /**< Previous child of the parent */ - struct node *next; /**< Next child of the parent */ - - /** Sorting function for the node (for folder nodes only) */ - int (*sort) (struct node *, struct node *); - /** Gets called for each deleted node_element and on node launch */ - tree_node_user_callback user_callback; - /** User data to be passed to delete_callback */ - void *callback_data; -}; - struct tree { - struct node *root; /* Tree root element */ - int width; /* Tree width */ - int height; /* Tree height */ - unsigned int flags; /* Tree flags */ - struct textarea *textarea; /* Handle for UTF-8 textarea */ - int ta_height; /* Textarea height */ - struct node_element *editing; /* Node element being edited */ - - bool redraw; /* Flag indicating whether the tree - should be redrawn on layout - changes */ + unsigned int flags; /* Tree flags */ tree_drag_type drag; const struct treeview_table *callbacks; - void *client_data; /* User assigned data for the - callbacks */ - struct node *def_folder; /* Node to be used for additions by default */ + void *client_data; /* User assigned data for the callbacks */ }; - - - #include "desktop/treeview.h" #include "desktop/hotlist.h" #include "desktop/cookie_manager.h" @@ -184,7 +53,6 @@ struct tree { struct sslcert_session_data *ssl_current_session = NULL; const char *tree_hotlist_path = NULL; - int treeview_inits; static void treeview_test_redraw_request(struct core_window *cw, @@ -257,7 +125,7 @@ static bool treeview_test_init(struct tree *tree) { nserror err; - if (tree->flags & TREE_SSLCERT) { + if (tree->flags == TREE_SSLCERT) { treeview_inits++; @@ -277,19 +145,19 @@ static bool treeview_test_init(struct tree *tree) if (treeview_inits == 1) treeview_init(); - if (tree->flags & TREE_COOKIES) { + if (tree->flags == TREE_COOKIES) { err = cookie_manager_init(&cw_t, (struct core_window *)tree); if (err != NSERROR_OK) { warn_user("Couldn't init new cookie manager.", 0); } } - if (tree->flags & TREE_HISTORY) { + if (tree->flags == TREE_HISTORY) { err = global_history_init(&cw_t, (struct core_window *)tree); if (err != NSERROR_OK) { warn_user("Couldn't init new global history.", 0); } } - if (tree->flags & TREE_HOTLIST) { + if (tree->flags == TREE_HOTLIST) { err = hotlist_init(&cw_t, (struct core_window *)tree, tree_hotlist_path); if (err != NSERROR_OK) { @@ -304,7 +172,7 @@ static bool treeview_test_fini(struct tree *tree) { nserror err; - if (tree->flags & TREE_SSLCERT) { + if (tree->flags == TREE_SSLCERT) { err = sslcert_viewer_fini(ssl_current_session); if (err != NSERROR_OK) { warn_user("Couldn't finalise sslcert viewer.", 0); @@ -317,19 +185,19 @@ static bool treeview_test_fini(struct tree *tree) return true; } - if (tree->flags & TREE_COOKIES) { + if (tree->flags == TREE_COOKIES) { err = cookie_manager_fini(); if (err != NSERROR_OK) { warn_user("Couldn't finalise cookie manager.", 0); } } - if (tree->flags & TREE_HISTORY) { + if (tree->flags == TREE_HISTORY) { err = global_history_fini(); if (err != NSERROR_OK) { warn_user("Couldn't finalise cookie manager.", 0); } } - if (tree->flags & TREE_HOTLIST) { + if (tree->flags == TREE_HOTLIST) { err = hotlist_fini(tree_hotlist_path); if (err != NSERROR_OK) { warn_user("Couldn't finalise hotlist.", 0); @@ -354,20 +222,20 @@ static bool treeview_test_redraw(struct tree *tree, int x, int y, clip.x1 = clip_x + clip_width; clip.y1 = clip_y + clip_height; - if (tree->flags & TREE_SSLCERT) { + if (tree->flags == TREE_SSLCERT) { sslcert_viewer_redraw(ssl_current_session, x, y, &clip, ctx); return true; } - if (tree->flags & TREE_COOKIES) { + if (tree->flags == TREE_COOKIES) { cookie_manager_redraw(x, y, &clip, ctx); return true; } - if (tree->flags & TREE_HISTORY) { + if (tree->flags == TREE_HISTORY) { global_history_redraw(x, y, &clip, ctx); return true; } - if (tree->flags & TREE_HOTLIST) { + if (tree->flags == TREE_HOTLIST) { hotlist_redraw(x, y, &clip, ctx); return true; } @@ -378,20 +246,20 @@ static bool treeview_test_redraw(struct tree *tree, int x, int y, static bool treeview_test_mouse_action(struct tree *tree, browser_mouse_state mouse, int x, int y) { - if (tree->flags & TREE_SSLCERT) { + if (tree->flags == TREE_SSLCERT) { sslcert_viewer_mouse_action(ssl_current_session, mouse, x, y); return true; } - if (tree->flags & TREE_COOKIES) { + if (tree->flags == TREE_COOKIES) { cookie_manager_mouse_action(mouse, x, y); return true; } - if (tree->flags & TREE_HISTORY) { + if (tree->flags == TREE_HISTORY) { global_history_mouse_action(mouse, x, y); return true; } - if (tree->flags & TREE_HOTLIST) { + if (tree->flags == TREE_HOTLIST) { hotlist_mouse_action(mouse, x, y); return true; } @@ -401,20 +269,20 @@ static bool treeview_test_mouse_action(struct tree *tree, static bool treeview_test_keypress(struct tree *tree, uint32_t key) { - if (tree->flags & TREE_SSLCERT) { + if (tree->flags == TREE_SSLCERT) { sslcert_viewer_keypress(ssl_current_session, key); return true; } - if (tree->flags & TREE_COOKIES) { + if (tree->flags == TREE_COOKIES) { cookie_manager_keypress(key); return true; } - if (tree->flags & TREE_HISTORY) { + if (tree->flags == TREE_HISTORY) { global_history_keypress(key); return true; } - if (tree->flags & TREE_HOTLIST) { + if (tree->flags == TREE_HOTLIST) { hotlist_keypress(key); return true; } @@ -422,68 +290,10 @@ static bool treeview_test_keypress(struct tree *tree, uint32_t key) return false; } +/* -------------------------------------------------------------------------- */ - - - - - - - - - - - -void tree_set_icon_dir(char *icon_dir) -{ - LOG(("Tree icon directory set to %s", icon_dir)); - tree_icons_dir = icon_dir; -} - -/** - * Set up colours for plot styles used in tree redraw. - */ -void tree_setup_colours(void) -{ - /* Background colour */ - plot_style_fill_tree_background.fill_colour = - gui_system_colour_char("Window"); - - /* Selection background colour */ - plot_style_fill_tree_selected.fill_colour = - gui_system_colour_char("Highlight"); - - /* Furniture line colour */ - plot_style_stroke_tree_furniture.stroke_colour = blend_colour( - gui_system_colour_char("Window"), - gui_system_colour_char("WindowText")); - - /* Furniture fill colour */ - plot_style_fill_tree_furniture.fill_colour = - gui_system_colour_char("Window"); - - /* Text colour */ - plot_fstyle.foreground = gui_system_colour_char("WindowText"); - plot_fstyle.background = gui_system_colour_char("Window"); - plot_fstyle_def_folder.foreground = - gui_system_colour_char("InfoText"); - plot_fstyle_def_folder.background = - gui_system_colour_char("Window"); - - /* Selected text colour */ - plot_fstyle_selected.foreground = - gui_system_colour_char("HighlightText"); - plot_fstyle_selected.background = - gui_system_colour_char("Highlight"); - plot_fstyle_selected_def_folder.foreground = - gui_system_colour_char("HighlightText"); - plot_fstyle_selected_def_folder.background = - gui_system_colour_char("Highlight"); -} - - /** * Creates and initialises a new tree. * @@ -504,41 +314,11 @@ struct tree *tree_create(unsigned int flags, return NULL; } - tree->root = tree_create_folder_node(NULL, - NULL, - messages_get("Root"), - false, false, false); - if (tree->root == NULL) { - free(tree); - return NULL; - } - tree->root->expanded = true; - - tree->width = 0; - tree->height = 0; tree->flags = flags; - tree->textarea = NULL; - tree->editing = NULL; - tree->redraw = false; tree->drag = TREE_NO_DRAG; tree->callbacks = callbacks; tree->client_data = client_data; - /* Set text height in pixels */ - tree_text_size_px = - (TREE_TEXT_SIZE_PT * FIXTOINT(nscss_screen_dpi) + 36) / - 72; - /* Set line height appropriate for this text height in pixels - * Using 4/3 text height */ - TREE_LINE_HEIGHT = (tree_text_size_px * 8 + 3) / 6; - - /* But if that's too small for the icons, base the line height on - * the icon height. */ - if (TREE_LINE_HEIGHT < TREE_ICON_SIZE + 2) - TREE_LINE_HEIGHT = TREE_ICON_SIZE + 2; - - tree_setup_colours(); - treeview_test_init(tree); return tree; @@ -546,2823 +326,99 @@ struct tree *tree_create(unsigned int flags, /** - * Recalculates the dimensions of a node element. - * - * \param tree the tree to which the element belongs, may be NULL - * \param element the element to recalculate - */ -static void tree_recalculate_node_element(struct tree *tree, - struct node_element *element) -{ - struct bitmap *bitmap = NULL; - int width, height; - static char *cache_text = NULL; - static int cache_size = 0; - plot_font_style_t *fstyle; - static plot_font_style_t *cache_fstyle = NULL; - - assert(element != NULL); - - if (element->parent->def_folder) - fstyle = &plot_fstyle_def_folder; - else - fstyle = &plot_fstyle; - - switch (element->type) { - case NODE_ELEMENT_TEXT_PLUS_ICON: - case NODE_ELEMENT_TEXT: - if(element->text == NULL) - break; - - if (tree != NULL && element == tree->editing) { - textarea_get_dimensions(tree->textarea, - &element->box.width, NULL); - } else { - if ((cache_text != NULL) && - (strcmp(cache_text, element->text) == 0) && - (cache_fstyle == fstyle)) { - element->box.width = cache_size; - #ifdef TREE_NOISY_DEBUG - LOG(("Tree font width cache hit")); - #endif - } else { - if(cache_text != NULL) free(cache_text); - nsfont.font_width(fstyle, - element->text, - strlen(element->text), - &cache_size); - element->box.width = cache_size; - cache_text = strdup(element->text); - cache_fstyle = fstyle; - } - } - - element->box.width += 8; - element->box.height = TREE_LINE_HEIGHT; - - if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON) - element->box.width += NODE_INSTEP; - - break; - - case NODE_ELEMENT_BITMAP: - bitmap = element->bitmap; - if (bitmap != NULL) { - width = bitmap_get_width(bitmap); - height = bitmap_get_height(bitmap); - element->box.width = width + 1; - element->box.height = height + 2; - } else { - element->box.width = 0; - element->box.height = 0; - } - break; - } -} - - -/** - * Calculates the height of a node including any children - * - * \param node the node to calculate the height of - * \return the total height of the node and children - */ -static int tree_get_node_height(struct node *node) -{ - int y1; - - assert(node != NULL); - - if ((node->child == NULL) || (node->expanded == false)) { - return node->box.height; - } - - y1 = node->box.y; - if (y1 < 0) { - y1 = 0; - } - node = node->child; - - while ((node->next != NULL) || - ((node->child != NULL) && (node->expanded))) { - for (; node->next != NULL; node = node->next); - - if ((node->child != NULL) && (node->expanded)) { - node = node->child; - } - } - return node->box.y + node->box.height - y1; -} - - -/** - * Calculates the width of a node including any children - * - * \param node the node to calculate the height of - * \return the total width of the node and children - */ -static int tree_get_node_width(struct node *node) -{ - int width = 0; - int child_width; - - assert(node != NULL); - - for (; node != NULL; node = node->next) { - if (width < (node->box.x + node->box.width)) { - width = node->box.x + node->box.width; - } - - if ((node->child != NULL) && (node->expanded)) { - child_width = tree_get_node_width(node->child); - if (width < child_width) { - width = child_width; - } - } - } - return width; -} - - -/** - * Recalculates the position of a node, its siblings and children. - * - * \param tree the tree to which 'root' belongs - * \param root the root node to update from - */ -static void tree_recalculate_node_positions(struct tree *tree, - struct node *root) -{ - struct node *parent; - struct node *node; - struct node *child; - struct node_element *element; - int y; - bool has_icon; - - for (node = root; node != NULL; node = node->next) { - - parent = node->parent; - - if (node->previous != NULL) { - node->box.x = node->previous->box.x; - node->box.y = node->previous->box.y + - tree_get_node_height(node->previous); - } else if (parent != NULL) { - node->box.x = parent->box.x + NODE_INSTEP; - node->box.y = parent->box.y + - parent->box.height; - for (child = parent->child; child != node; - child = child->next) - node->box.y += child->box.height; - } else { - node->box.x = tree->flags & TREE_NO_FURNITURE - ? -NODE_INSTEP + 4 : 0; - node->box.y = -TREE_LINE_HEIGHT; - } - - if (!node->expanded) { - node->data.box.x = node->box.x; - node->data.box.y = node->box.y; - continue; - } - - if (node->folder) { - node->data.box.x = node->box.x; - node->data.box.y = node->box.y; - tree_recalculate_node_positions(tree, node->child); - } else { - y = node->box.y; - has_icon = false; - for (element = &node->data; element != NULL; - element = element->next) - if (element->type == - NODE_ELEMENT_TEXT_PLUS_ICON) { - has_icon = true; - break; - } - - for (element = &node->data; element != NULL; - element = element->next) { - element->box.x = node->box.x; - if (element->type != - NODE_ELEMENT_TEXT_PLUS_ICON && - has_icon) - element->box.x += NODE_INSTEP; - element->box.y = y; - y += element->box.height; - } - } - - } -} - - -/** - * Recalculates the size of a node. - * - * \param tree the tree to which node belongs, may be NULL - * \param node the node to update - * \param recalculate_sizes whether the node elements have changed - */ -static void tree_recalculate_node_sizes(struct tree *tree, struct node *node, - bool recalculate_sizes) -{ - struct node_element *element; - int height; - - assert(node != NULL); - - height = node->box.height; - node->box.width = 0; - node->box.height = 0; - if (node->expanded) { - for (element = &node->data; element != NULL; - element = element->next) { - if (recalculate_sizes) { - #ifdef TREE_NOISY_DEBUG - if(element->text) LOG(("%s", element->text)); - #endif - tree_recalculate_node_element(tree, element); - } - node->box.width = (node->box.width > element->box.x + - element->box.width - node->box.x) ? - node->box.width : - element->box.width + element->box.x - - node->box.x; - node->box.height += element->box.height; - } - } else { - if (recalculate_sizes) - for (element = &node->data; element != NULL; - element = element->next) { - #ifdef TREE_NOISY_DEBUG - if(element->text) LOG(("%s", element->text)); - #endif - tree_recalculate_node_element(tree, element); - } - - node->box.width = node->data.box.width; - node->box.height = node->data.box.height; - } - - if (tree != NULL && height != node->box.height) - tree_recalculate_node_positions(tree, tree->root); -} - - -/* exported interface documented in desktop/tree.h */ -struct node *tree_create_folder_node(struct tree *tree, struct node *parent, - const char *title, bool editable, bool retain_in_memory, - bool deleted) -{ - struct node *node; - - assert(title != NULL); - - node = calloc(sizeof(struct node), 1); - if (node == NULL) { - return NULL; - } - - node->data.text = strdup(title); - if (node->data.text == NULL) { - free(node); - return NULL; - } - - node->folder = true; - node->retain_in_memory = retain_in_memory; - node->deleted = deleted; - node->data.parent = node; - node->data.type = NODE_ELEMENT_TEXT; - node->data.flag = TREE_ELEMENT_TITLE; - node->data.editable = editable; - node->sort = NULL; - node->user_callback = NULL; - node->previous = NULL; - - tree_recalculate_node_sizes(tree, node, true); - if (parent != NULL) { - tree_link_node(tree, parent, node, false); - } - - return node; -} - -/* exported interface documented in desktop/tree.h */ -struct node *tree_create_leaf_node(struct tree *tree, struct node *parent, - const char *title, bool editable, bool retain_in_memory, - bool deleted) -{ - struct node *node; - - assert(title != NULL); - - node = calloc(sizeof(struct node), 1); - if (node == NULL) { - return NULL; - } - - node->data.text = strdup(title); - if (node->data.text == NULL) { - free(node); - return NULL; - } - - node->folder = false; - node->retain_in_memory = retain_in_memory; - node->deleted = deleted; - node->data.parent = node; - node->data.type = NODE_ELEMENT_TEXT; - node->data.flag = TREE_ELEMENT_TITLE; - node->data.editable = editable; - node->sort = NULL; - node->user_callback = NULL; - node->previous = NULL; - - tree_recalculate_node_sizes(tree, node, true); - if (parent != NULL) { - tree_link_node(tree, parent, node, false); - } - - return node; -} - - -/** - * Creates an empty text node element and links it to a node. - * - * \param parent the parent node - * \param type the required element type - * \param flag user assigned flag used for searches - * \return the newly created element. - */ -struct node_element *tree_create_node_element(struct node *parent, - node_element_type type, unsigned int flag, bool editable) -{ - struct node_element *element; - - element = calloc(sizeof(struct node_element), 1); - if (element == NULL) - return NULL; - - element->parent = parent; - element->flag = flag; - element->type = type; - element->editable = editable; - element->next = parent->data.next; - parent->data.next = element; - - return element; -} - - -/** - * Inserts a node into the correct place according to the parent's sort function + * Deletes all nodes of a tree and the tree itself. * - * \param parent the node whose child node 'node' becomes - * \param node the node to be inserted + * \param tree the tree to be deleted */ -static void tree_sort_insert(struct node *parent, struct node *node) +void tree_delete(struct tree *tree) { - struct node *after; - - assert(node != NULL); - assert(parent != NULL); - assert(parent->sort != NULL); - - after = parent->last_child; - while ((after != NULL) && - (parent->sort(node, after) == -1)) - after = after->previous; - - if (after != NULL) { - if (after->next != NULL) - after->next->previous = node; - node->next = after->next; - node->previous = after; - after->next = node; - } else { - node->previous = NULL; - node->next = parent->child; - if (parent->child != NULL) { - parent->child->previous = node; - } - parent->child = node; - } - - if (node->next == NULL) - parent->last_child = node; - - node->parent = parent; + treeview_test_fini(tree); + free(tree); } /** - * Recalculates the size of a tree. + * Redraws a tree. * - * \param tree the tree to recalculate + * \param tree the tree to draw + * \param x X coordinate to draw the tree at (wrt plot origin) + * \param y Y coordinate to draw the tree at (wrt plot origin) + * \param clip_x minimum x of the clipping rectangle (wrt tree origin) + * \param clip_y minimum y of the clipping rectangle (wrt tree origin) + * \param clip_width width of the clipping rectangle + * \param clip_height height of the clipping rectangle + * \param ctx current redraw context */ -static void tree_recalculate_size(struct tree *tree) +void tree_draw(struct tree *tree, int x, int y, + int clip_x, int clip_y, int clip_width, int clip_height, + const struct redraw_context *ctx) { - int width, height; - assert(tree != NULL); - width = tree->width; - height = tree->height; - - tree->width = tree_get_node_width(tree->root); - tree->height = tree_get_node_height(tree->root); - - if ((width != tree->width) || (height != tree->height)) - tree->callbacks->resized(tree, tree->width, tree->height, - tree->client_data); + treeview_test_redraw(tree, x, y, clip_x, clip_y, + clip_width, clip_height, ctx); } + /** - * Recalculate the node data and redraw the relevant section of the tree. + * Handles a mouse action for a tree * - * \param tree the tree to redraw, may be NULL - * \param node the node to update - * \param recalculate_sizes whether the elements have changed - * \param expansion the request is the result of a node expansion + * \param tree the tree to handle a click for + * \param mouse the mouse state + * \param x X coordinate of mouse action + * \param y Y coordinate of mouse action + * \return whether the click was handled */ -static void tree_handle_node_changed(struct tree *tree, struct node *node, - bool recalculate_sizes, bool expansion) +bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x, + int y) { - int node_width, node_height, tree_width, tree_height; - - assert(node != NULL); assert(tree != NULL); - node_width = node->box.width; - node_height = node->box.height; - tree_width = tree->width; - tree_height = tree->height; - - if ((recalculate_sizes) || (expansion)) { - tree_recalculate_node_sizes(tree, node, true); + if (treeview_test_mouse_action(tree, mouse, x, y)) { + return true; } - if (tree != NULL) { - if ((node->box.height != node_height) || (expansion)) { - tree_recalculate_node_positions(tree, tree->root); - tree_recalculate_size(tree); - if (tree->width > tree_width) - tree_width = tree->width; - if (tree->height > tree_height) - tree_height = tree->height; - if (tree->redraw) { - tree->callbacks->redraw_request(0, node->box.y, - tree_width, - tree_height - node->box.y, - tree->client_data); - } - } else { - if (node->box.width > node_width) - node_width = node->box.width; - if (tree->redraw) - tree->callbacks->redraw_request(node->box.x, - node->box.y, - node_width, node->box.height, - tree->client_data); - if (recalculate_sizes) { - tree_recalculate_size(tree); - } - } - } + return false; } /** - * Links a node to another node. + * Handle the end of a drag operation * - * \param tree the tree in which the link takes place, may be NULL - * \param link the node to link before/as a child (folders) - * or before/after (link) - * \param node the node to link - * \param before whether to link siblings before or after the supplied node + * \param tree the tree on which the drag was performed + * \param mouse mouse state during drag end + * \param x0 x coordinate of drag start + * \param y0 y coordinate of drag start + * \param x1 x coordinate of drag end + * \param y1 y coordinate of drag end */ -void tree_link_node(struct tree *tree, struct node *link, struct node *node, - bool before) +void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0, + int x1, int y1) { + assert(tree != NULL); - struct node *parent; - bool sort = false; - - assert(link != NULL); - assert(node != NULL); - - if ((link->folder == 0) || (before)) { - parent = node->parent = link->parent; - if (parent->sort) { - sort = true; - } else { - if (before) { - node->next = link; - node->previous = link->previous; - if (link->previous != NULL) - link->previous->next = node; - link->previous = node; - if ((parent != NULL) && (parent->child == link)) - parent->child = node; - } else { - node->previous = link; - node->next = link->next; - if (link->next != NULL) - link->next->previous = node; - link->next = node; - if ((parent != NULL) && - (parent->last_child == link)) - parent->last_child = node; - } - } - } else { - parent = node->parent = link; - if (parent->sort != NULL) { - sort = true; - } else { - node->next = NULL; - if (link->child == NULL) { - link->child = link->last_child = node; - node->previous = NULL; - } else { - link->last_child->next = node; - node->previous = link->last_child; - link->last_child = node; - } - } - - } - - if (sort) { - tree_sort_insert(parent, node); - } - - tree_handle_node_changed(tree, link, false, true); - - node->deleted = false; + treeview_test_mouse_action(tree, BROWSER_MOUSE_HOVER, x1, y1); } /** - * Recalculate the node element and redraw the relevant section of the tree. - * The tree size is not updated. + * Key press handling for a tree. * - * \param tree the tree to redraw, may be NULL - * \param element the node element to update + * \param tree The tree which got the keypress + * \param key The ucs4 character codepoint + * \return true if the keypress is dealt with, false otherwise. */ -static void tree_handle_node_element_changed(struct tree *tree, - struct node_element *element, bool text_changed) +bool tree_keypress(struct tree *tree, uint32_t key) { - int width, height; - - assert(element != NULL); - - width = element->box.width; - height = element->box.height; - - if(text_changed == true) { - #ifdef TREE_NOISY_DEBUG - if(element->text) LOG(("%s", element->text)); - #endif - tree_recalculate_node_element(tree, element); + if (treeview_test_keypress(tree, key)) { + return true; } - if (element->box.height != height) { - tree_recalculate_node_sizes(tree, element->parent, false); - if ((tree != NULL) && (tree->redraw)) { - tree->callbacks->redraw_request(0, element->box.y, - tree->width + element->box.width - - width, - tree->height - element->box.y + - element->box.height - height, - tree->client_data); - } - } else { - if (element->box.width != width) { - tree_recalculate_node_sizes(tree, element->parent, - false); - } - - if (tree != NULL) { - width = (width > element->box.width) ? width : - element->box.width; - if (tree->redraw) { - tree->callbacks->redraw_request(element->box.x, - element->box.y, - width, - element->box.height, - tree->client_data); - } - } - } + return false; } - -/** - * Stops editing a node_element - * - * \param tree The tree to stop editing for - * \param keep_changes If true the changes made to the text will be kept, - * if false they will be dropped - */ -static void tree_stop_edit(struct tree *tree, bool keep_changes) +tree_drag_type tree_drag_status(struct tree *tree) { - int text_len; - char *text = NULL; - struct node_element *element; - struct node_msg_data msg_data; - node_callback_resp response; - assert(tree != NULL); - - if (tree->editing == NULL || tree->textarea == NULL) - return; - - element = tree->editing; - - if (keep_changes) { - text_len = textarea_get_text(tree->textarea, NULL, 0); - text = malloc(text_len * sizeof(char)); - if (text == NULL) { - LOG(("malloc failed")); - warn_user("NoMemory", 0); - textarea_destroy(tree->textarea); - tree->textarea = NULL; - return; - } - textarea_get_text(tree->textarea, text, text_len); - } - - - if (keep_changes && element->parent->user_callback != NULL) { - msg_data.msg = NODE_ELEMENT_EDIT_FINISHING; - msg_data.flag = element->flag; - msg_data.node = element->parent; - msg_data.data.text = text; - response = element->parent->user_callback( - element->parent->callback_data, - &msg_data); - - switch (response) { - case NODE_CALLBACK_REJECT: - free(text); - text = NULL; - break; - case NODE_CALLBACK_CONTINUE: - free(text); - text = NULL; - return; - case NODE_CALLBACK_HANDLED: - case NODE_CALLBACK_NOT_HANDLED: - text = msg_data.data.text; - break; - } - } - - textarea_destroy(tree->textarea); - tree->textarea = NULL; - tree->editing = NULL; - - if (text != NULL) - tree_update_node_element(tree, element, text, NULL); - else - tree_handle_node_element_changed(tree, element, true); - - - tree_recalculate_size(tree); - if (element->parent->user_callback != NULL) { - msg_data.msg = keep_changes ? NODE_ELEMENT_EDIT_FINISHED : - NODE_ELEMENT_EDIT_CANCELLED; - msg_data.flag = element->flag; - msg_data.node = element->parent; - element->parent->user_callback(element->parent->callback_data, - &msg_data); - } -} - - -/** - * Delinks a node from the tree structures. - * - * \param tree the tree in which the delink takes place, may be NULL - * \param node the node to delink - */ -void tree_delink_node(struct tree *tree, struct node *node) -{ - struct node *parent; - - assert(node != NULL); - - /* do not remove the root */ - if (tree != NULL && node == tree->root) - return; - if ((tree != NULL) && (tree->editing != NULL)) { - parent = tree->editing->parent; - while (parent != NULL) { - if (node == parent) { - tree_stop_edit(tree, false); - break; - } - parent = parent->parent; - } - } - - if (node->parent->child == node) - node->parent->child = node->next; - if (node->parent->last_child == node) - node->parent->last_child = node->previous; - parent = node->parent; - node->parent = NULL; - - if (node->previous != NULL) - node->previous->next = node->next; - if (node->next != NULL) - node->next->previous = node->previous; - node->previous = NULL; - node->next = NULL; - - tree_handle_node_changed(tree, parent, false, true); -} - - -/** - * Deletes a node from the tree. - * - * \param tree the tree to delete from, may be NULL - * \param node the node to delete - * \param siblings whether to delete all siblings - */ -static void tree_delete_node_internal(struct tree *tree, struct node *node, - bool siblings) -{ - struct node *next, *child, *parent; - struct node_element *e, *f; - node_callback_resp response; - struct node_msg_data msg_data; - - assert(node != NULL); - - if (tree != NULL && tree->root == node) - return; - - next = node->next; - parent = node->parent; - if (tree != NULL && parent == tree->root) - parent = NULL; - if ((tree != NULL) && (tree->def_folder == node)) - tree->def_folder = NULL; - tree_delink_node(tree, node); - child = node->child; - node->child = NULL; - - node->deleted = true; - if (child != NULL) - tree_delete_node_internal(tree, child, true); - - if (!node->retain_in_memory) { - node->retain_in_memory = true; - for (e = &node->data; e != NULL; e = f) { - if (e->text != NULL) { - response = NODE_CALLBACK_NOT_HANDLED; - if (!e->editable && - node->user_callback != NULL) { - msg_data.msg = NODE_DELETE_ELEMENT_TXT; - msg_data.flag = e->flag; - msg_data.node = node; - msg_data.data.text = (void *)e->text; - response = node->user_callback( - node->callback_data, - &msg_data); - } - if (response != NODE_CALLBACK_HANDLED) - free((void *)e->text); - e->text = NULL; - } - if (e->bitmap != NULL) { - response = NODE_CALLBACK_NOT_HANDLED; - if (node->user_callback != NULL) { - msg_data.msg = NODE_DELETE_ELEMENT_IMG; - msg_data.flag = e->flag; - msg_data.node = node; - msg_data.data.bitmap = - (void *)e->bitmap; - response = node->user_callback( - node->callback_data, - &msg_data); - } - /* TODO the type of this field is platform - dependent */ - if (response != NODE_CALLBACK_HANDLED) - free(e->bitmap); - e->bitmap = NULL; - } - f = e->next; - if (e != &node->data) - free(e); - } - free(node); - } - - if (siblings && next) - tree_delete_node_internal(tree, next, true); - if ((tree->flags & TREE_DELETE_EMPTY_DIRS) && parent != NULL && - parent->child == NULL && !parent->deleted) - tree_delete_node_internal(tree, parent, false); -} - - -/** - * Deletes all nodes of a tree and the tree itself. - * - * \param tree the tree to be deleted - */ -void tree_delete(struct tree *tree) -{ - tree->redraw = false; - - treeview_test_fini(tree); - - if (tree->root->child != NULL) - tree_delete_node_internal(tree, tree->root->child, true); - - free((void *)tree->root->data.text); - free(tree->root); - free(tree); -} - - -/** - * Gets the redraw property of the given tree. - * - * \param tree the tree for which to retrieve the property - * \return the redraw property of the tree - */ -bool tree_get_redraw(struct tree *tree) -{ - return tree->redraw; -} - - -/** - * Deletes a node from the tree. - * - * \param tree the tree to delete from, may be NULL - * \param node the node to delete - * \param siblings whether to delete all siblings - */ -void tree_delete_node(struct tree *tree, struct node *node, bool siblings) -{ - int y = node->box.y; - int height = tree->height; - int width = tree->width; - bool redraw_setting = tree->redraw; - - tree->redraw = false; - - tree_delete_node_internal(tree, node, siblings); - tree_recalculate_node_positions(tree, tree->root); - - tree->redraw = redraw_setting; - - if (tree->redraw) - tree->callbacks->redraw_request(0, y, - width, height, tree->client_data); - tree_recalculate_size(tree); -} - - -/** - * Sets an icon for a node - * - * \param tree The tree to which node belongs, may be NULL - * \param node The node for which the icon is set - * \param icon the image to use - */ -void tree_set_node_icon(struct tree *tree, struct node *node, - hlcache_handle *icon) -{ - node->data.type = NODE_ELEMENT_TEXT_PLUS_ICON; - tree_update_node_element(tree, &(node->data), NULL, icon); -} - - -/** - * Updates all siblings and descendants of a node to an expansion state. - * No update is performed for the tree changes. - * - * \param tree the tree to which 'node' belongs - * \param node the node to set all siblings and descendants of - * \param expanded the expansion state to set - */ -static void tree_set_node_expanded_all(struct tree *tree, struct node *node, - bool expanded) -{ - for (; node != NULL; node = node->next) { - if (node->expanded != expanded) { - node->expanded = expanded; - tree_recalculate_node_sizes(tree, node, false); - } - if ((node->child != NULL) && (node->expanded)) - tree_set_node_expanded_all(tree, node->child, expanded); - } -} - - -/** - * Updates [all siblings and descendants of] a node to an expansion state. - * - * \param tree the tree to update - * \param node the node to set [all siblings and descendants of] - * \param expanded the expansion state to set - * \param folder whether to update folders, if this together with leaf - * will be false only 'node' will be updated - * \param leaf whether to update leaves (check also description for folder) - * \return whether any changes were made - */ -static bool tree_set_node_expanded_internal(struct tree *tree, - struct node *node, bool expanded, bool folder, bool leaf) -{ - bool redraw = false; - struct node *end = (folder == false && leaf == false) ? - node->next : NULL; - - if (tree->editing != NULL && node == tree->editing->parent) - tree_stop_edit(tree, false); - - for (; node != end; node = node->next) { - if ((node->expanded != expanded) && (node != tree->root) && - ((folder && (node->folder)) || - (leaf && (!node->folder)) || - (!folder && !leaf))) { - node->expanded = expanded; - if (node->child != NULL) - tree_set_node_expanded_all(tree, - node->child, false); - if ((node->data.next != NULL) && - (node->data.next->box.height == 0)) - tree_recalculate_node_sizes(tree, node, true); - else - tree_recalculate_node_sizes(tree, node, false); - redraw = true; - } - if ((folder || leaf) && (node->child != NULL) && - (node->expanded)) - redraw |= tree_set_node_expanded_internal(tree, - node->child, expanded, folder, leaf); - } - return redraw; -} - - -/** - * Updates [all siblings and descendants of] a node to an expansion state. - * - * \param tree the tree to update - * \param node the node to set [all siblings and descendants of] - * \param expanded the expansion state to set - * \param folder whether to update folders, if this together with leaf - * will be false only 'node' will be updated - * \param leaf whether to update leaves (check also description for folder) - */ -void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded, - bool folder, bool leaf) -{ - if (tree_set_node_expanded_internal(tree, node, expanded, folder, leaf)) - tree_handle_node_changed(tree, node, false, true); -} - - -/** - * Updates a node to an selected state. The required areas of the tree are - * redrawn. - * - * \param tree the tree to update nodes for, may be NULL - * \param node the node to set all siblings and descendants of - * \param all if true update node together with its siblings and - * descendants - * \param selected the selection state to set - */ -void tree_set_node_selected(struct tree *tree, struct node *node, bool all, - bool selected) -{ - struct node *end; - - if (tree != NULL && node == tree->root) - node = tree->root->child; - if (node == NULL) - return; - - end = all ? NULL : node->next; - - for (; node != end; node = node->next) { - if (node->selected != selected) { - node->selected = selected; - if (tree != NULL && tree->redraw) - tree->callbacks->redraw_request( - node->data.box.x, - node->data.box.y, - node->data.box.width, - node->data.box.height, - tree->client_data); - } - if (all && (node->child != NULL) && (node->expanded)) - tree_set_node_selected(tree, node->child, all, - selected); - } -} - - -/** - * Sets the sort function for a node - * - * \param tree the tree to which 'node' belongs, may be NULL - * \param node the node to be inserted - * \param sort pointer to the sorting function - */ -void tree_set_node_sort_function(struct tree *tree, struct node *node, - int (*sort) (struct node *, struct node *)) -{ - struct node *child; - - node->sort = sort; - - if (tree != NULL && tree->editing != NULL) - tree_stop_edit(tree, false); - - /* the node had already some children so they must get sorted */ - if (node->child != NULL) { - - child = node->child; - node->child = NULL; - - while (child != NULL) { - tree_sort_insert(node, child); - child = child->next; - } - - } - - if (tree != NULL) - tree_recalculate_node_positions(tree, node->child); -} - - -/** - * Sets the delete callback for a node. - * - * \param node the node for which the callback is set - * \param callback the callback functions to be set - * \param data user data to be passed to callback - */ -void tree_set_node_user_callback(struct node *node, - tree_node_user_callback callback, void *data) -{ - node->user_callback = callback; - node->callback_data = data; -} - - -/** - * Sets the redraw property to the given value. If redraw is true, the tree will - * be redrawn on layout/appearance changes. - * - * \param tree the tree for which the property is set - * \param redraw the value to set - */ -void tree_set_redraw(struct tree *tree, bool redraw) -{ - /* the tree might have no graphical representation, do not set the - redraw flag in such case */ - if (tree->callbacks == NULL) - return; - tree->redraw = redraw; -} - - -/** - * Checks whether a node, its siblings or any children are selected. - * - * \param node the root node to check from - * \return whether 'node', its siblings or any children are selected. - */ -bool tree_node_has_selection(struct node *node) -{ - for (; node != NULL; node = node->next) { - if (node->selected) - return true; - if ((node->child != NULL) && (node->expanded) && - (tree_node_has_selection(node->child))) - return true; - } - return false; -} - - -/** - * Returns the current value of the nodes deleted property. - * - * \param node the node to be checked - * \return the current value of the nodes deleted property - */ -bool tree_node_is_deleted(struct node *node) -{ - return node->deleted; -} - - -/** - * Returns true if the node is a folder - * - * \param node the node to be checked - * \return true if the node is a folder, false otherwise - */ -bool tree_node_is_folder(struct node *node) -{ - return node->folder; -} - - -/** - * Returns true if the node is the default folder for a tree - * - * \param node the node to be checked - * \return true if the node is a default folder, false otherwise - */ -bool tree_node_is_default(struct node *node) -{ - return node->def_folder; -} - - -/** - * Update the text of a node element if it has changed. - * - * \param element The node element to update. - * \param text The text to update the element with. The ownership of - * this string is taken by this function and must not be - * referred to after the function exits. - */ -bool tree_update_element_text(struct tree *tree, - struct node_element *element, char *text) -{ - const char *node_text; /* existing node text */ - - if (text == NULL) - return false; - - if (element == NULL) { - free(text); - return false; - } - - node_text = tree_node_element_get_text(element); - - if ((node_text == NULL) || (strcmp(node_text, text) != 0)) { - tree_update_node_element(tree, element, text, NULL); - } else { - /* text does not need changing, free it */ - free(text); - } - return true; -} - - -/** - * Updates the content of a node_element. - * - * \param tree the tree owning element, may be NULL - * \param element the element to be updated - * \param text new text to be set, may be NULL - * \param bitmap new bitmap to be set, may be NULL - */ -void tree_update_node_element(struct tree *tree, struct node_element *element, - const char *text, void *bitmap) -{ - node_callback_resp response; - struct node_msg_data msg_data; - bool text_changed = false; - - assert(element != NULL); - - if ((tree != NULL) && (element == tree->editing)) { - tree_stop_edit(tree, false); - } - - if ((text != NULL) && - (element->type == NODE_ELEMENT_TEXT || - element->type == NODE_ELEMENT_TEXT_PLUS_ICON)) { - if (element->text != NULL) { - if (strcmp(element->text, text) == 0) { - text_changed = true; - } - response = NODE_CALLBACK_NOT_HANDLED; - if ((!element->editable) && - (element->parent->user_callback != NULL)) { - msg_data.msg = NODE_DELETE_ELEMENT_TXT; - msg_data.flag = element->flag; - msg_data.node = element->parent; - msg_data.data.text = (void *)element->text; - response = element->parent->user_callback( - element->parent->callback_data, - &msg_data); - } - if (response != NODE_CALLBACK_HANDLED) { - free(element->text); - } - } - element->text = (char *)text; - } - - if ((bitmap != NULL) && - ((element->type == NODE_ELEMENT_BITMAP) || - (element->type == NODE_ELEMENT_TEXT_PLUS_ICON))) { - if (element->bitmap != NULL) { - response = NODE_CALLBACK_NOT_HANDLED; - if (element->parent->user_callback != NULL) { - msg_data.msg = NODE_DELETE_ELEMENT_IMG; - msg_data.flag = element->flag; - msg_data.node = element->parent; - msg_data.data.bitmap = (void *)element->bitmap; - response = element->parent->user_callback( - element->parent->callback_data, - &msg_data); - } - - if (response != NODE_CALLBACK_HANDLED) { - free(element->bitmap); - } - } else { - /* Increase the box width to accomodate the new icon */ - element->box.width += NODE_INSTEP; - } - - element->bitmap = bitmap; - } - - tree_handle_node_element_changed(tree, element, text_changed); -} - - -/** - * Returns the node element's text - * - * \return the node element's text - */ -const char *tree_node_element_get_text(struct node_element *element) -{ - return element->text; -} - - -/** - * Returns the node element's icon - * - * \return the node element's icon - */ -struct bitmap *tree_node_element_get_icon(struct node_element *element) -{ - return element->bitmap; -} - - -/** - * Get the root node of a tree - * - * \param tree the tree to get the root of - * \return the root of the tree - */ -struct node *tree_get_root(struct tree *tree) -{ - return tree->root; -} - - -/** - * Returns whether the current tree is being edited at this time - * - * \param tree the tree to be checked - * \return true if the tree is currently being edited - */ -bool tree_is_edited(struct tree *tree) -{ - return tree->editing == NULL ? false : true; -} - - -/** - * Get the drag state of a tree - * - * \param tree the tree to get the state of - * \return drag type (defined in desktop/tree.h) - */ -tree_drag_type tree_drag_status(struct tree *tree) -{ return tree->drag; } - - -/** - * Get the default node of a tree for additions - * - * \param tree the tree to get the default node of - * \return the default node - */ -struct node *tree_get_default_folder_node(struct tree *tree) -{ - if (tree->def_folder != NULL) { - return tree->def_folder; - } else { - return tree_get_root(tree); - } -} - - -/** - * Set the default node of a tree to the selected node - * - * \param tree the tree to set the default node of - * \param node the node to set as default (NULL for selected node) - * \return success - */ -bool tree_set_default_folder_node(struct tree *tree, struct node *node) -{ - struct node *sel_node; - - if (node == NULL) { - sel_node = tree_get_selected_node(tree->root); - } else { - sel_node = node; - } - - if((sel_node == NULL) || - (tree_node_is_folder(sel_node) == false)) { - return false; - } - - tree_clear_default_folder_node(tree); - - tree->def_folder = sel_node; - sel_node->def_folder = true; - tree_handle_node_changed(tree, sel_node, true, false); - - return true; -} - - -/** - * Clear the default node of a tree - * - * \param tree the tree to clear the default node of - */ -void tree_clear_default_folder_node(struct tree *tree) -{ - struct node *def_node = NULL; - def_node = tree_get_default_folder_node(tree); - - if (def_node != NULL) { - tree->def_folder = NULL; - def_node->def_folder = false; - tree_handle_node_changed(tree, def_node, true, false); - } -} - - -/** - * Returns the parent of a node - * - * \param node the node to get the parent of - * \return the node's parent - */ -struct node *tree_node_get_parent(struct node *node) -{ - return node->parent; -} - - -/** - * Returns the first child of a node - * - * \param node the node to get the child of - * \return the nodes first child - */ -struct node *tree_node_get_child(struct node *node) -{ - return node->child; -} - - -/** - * Returns the closest sibling a node - * - * \param node the node to get the sibling of - * \return the nodes sibling - */ -struct node *tree_node_get_next(struct node *node) -{ - return node->next; -} - - -/** - * Draws an element's expansion icon - * - * \param tree the tree to draw the expansion for - * \param element the element to draw the expansion for - * \param tree_x X coordinate of the tree - * \param tree_y Y coordinate of the tree - * \param ctx current redraw context - */ -static void tree_draw_node_expansion_toggle(struct tree *tree, - struct node *node, int tree_x, int tree_y, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - int x, y; - - assert(tree != NULL); - assert(node != NULL); - - if ((node->child != NULL) || (node->data.next != NULL)) { - x = tree_x + node->box.x - (NODE_INSTEP / 2) - 4; - y = tree_y + node->box.y + (TREE_LINE_HEIGHT - 9) / 2; - plot->rectangle(x, y, x + 9, y + 9, - &plot_style_fill_tree_furniture); - plot->rectangle(x , y, x + 8, y + 8, - &plot_style_stroke_tree_furniture); - plot->line(x + 2, y + 4, x + 7, y + 4, - &plot_style_stroke_tree_furniture); - if (!node->expanded) - plot->line(x + 4, y + 2, x + 4, y + 7, - &plot_style_stroke_tree_furniture); - - } - -} - - -/** - * Draws an element, including any expansion icons - * - * \param tree the tree to draw an element for - * \param element the element to draw - * \param tree_x X coordinate to draw the tree at (wrt plot origin) - * \param tree_y Y coordinate to draw the tree at (wrt plot origin) - * \param clip clipping rectangle (wrt plot origin) - * \param ctx current redraw context - */ -static void tree_draw_node_element(struct tree *tree, - struct node_element *element, int tree_x, int tree_y, - const struct rect *clip, const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - struct bitmap *bitmap = NULL; - int x, y, width; - bool selected = false; - bool def_folder = false; - hlcache_handle *icon; - plot_font_style_t *fstyle; - const int icon_inset = (TREE_LINE_HEIGHT - TREE_ICON_SIZE) / 2; - - assert(tree != NULL); - assert(element != NULL); - assert(element->parent != NULL); - - x = tree_x + element->box.x; - y = tree_y + element->box.y; - width = element->box.width; - if (&element->parent->data == element) { - if (element->parent->selected) - selected = true; - if (element->parent->def_folder) - def_folder = true; - } - - switch (element->type) { - case NODE_ELEMENT_TEXT_PLUS_ICON: - icon = element->bitmap; - if (icon != NULL && (content_get_status(icon) == - CONTENT_STATUS_READY || - content_get_status(icon) == - CONTENT_STATUS_DONE) && - x + TREE_ICON_SIZE > clip->x0 && - x < clip->x1) { - struct rect c; - /* Clip to image area */ - c.x0 = x; - c.y0 = y + icon_inset; - c.x1 = x + TREE_ICON_SIZE; - c.y1 = y + icon_inset + TREE_ICON_SIZE; - if (c.x0 < clip->x0) c.x0 = clip->x0; - if (c.y0 < clip->y0) c.y0 = clip->y0; - if (c.x1 > clip->x1) c.x1 = clip->x1; - if (c.y1 > clip->y1) c.y1 = clip->y1; - - if (c.x1 > c.x0 && c.y1 > c.y0) { - /* Valid clip rectangles only */ - struct content_redraw_data data; - - plot->clip(&c); - - data.x = x; - data.y = y + icon_inset; - data.width = TREE_ICON_SIZE; - data.height = TREE_ICON_SIZE; - - data.background_colour = 0xFFFFFF; - data.scale = 1; - data.repeat_x = false; - data.repeat_y = false; - - content_redraw(icon, &data, &c, ctx); - - /* Restore previous clipping area */ - plot->clip(clip); - } - } - - x += NODE_INSTEP; - width -= NODE_INSTEP; - - /* fall through */ - case NODE_ELEMENT_TEXT: - if (element->text == NULL || clip->x1 < x) - break; - - if (element == tree->editing) - return; - - if (selected) { - if (def_folder == true) - fstyle = &plot_fstyle_selected_def_folder; - else - fstyle = &plot_fstyle_selected; - - plot->rectangle(x, y, x + width, - y + element->box.height, - &plot_style_fill_tree_selected); - } else { - if (def_folder == true) - fstyle = &plot_fstyle_def_folder; - else - fstyle = &plot_fstyle; - } - - plot->text(x + 4, y + (TREE_LINE_HEIGHT * 3 + 2) / 4, - element->text, strlen(element->text), - fstyle); - break; - case NODE_ELEMENT_BITMAP: - bitmap = element->bitmap; - if (bitmap == NULL) - break; - plot->bitmap(x, y, element->box.width - 1, - element->box.height - 2, - bitmap, 0xFFFFFF, BITMAPF_NONE); - if (!(tree->flags & TREE_NO_FURNITURE)) - plot->rectangle(x, y, x + element->box.width - 1, - y + element->box.height - 3, - &plot_style_stroke_tree_furniture); - - break; - } - -} - - -/** - * Redraws a node. - * - * \param tree the tree to draw - * \param node the node to draw children and siblings of - * \param tree_x X coordinate to draw the tree at (wrt plot origin) - * \param tree_y Y coordinate to draw the tree at (wrt plot origin) - * \param clip clipping rectangle (wrt plot origin) - * \param ctx current redraw context - */ -static void tree_draw_node(struct tree *tree, struct node *node, - int tree_x, int tree_y, struct rect clip, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - struct node_element *element; - struct node *parent; - int x0, y0, x1, y1; - struct rect node_extents; - - assert(tree != NULL); - assert(node != NULL); - - /* Find node's extents, including children's area */ - node_extents.x0 = tree_x + node->box.x - NODE_INSTEP; - node_extents.y0 = tree_y + node->box.y; - node_extents.x1 = tree_x + node->box.x + node->box.width + NODE_INSTEP; - if (node->next != NULL) - node_extents.y1 = tree_y + node->next->box.y; - else - node_extents.y1 = tree_y + node->box.y + node->box.height; - - /* Nothing to draw, if node is outside clip region */ - if ((node_extents.x1 < clip.x0) && (node_extents.y1 < clip.y0) && - (node_extents.x0 > clip.x1) && - (node_extents.y0 > clip.y1)) { - return; - } - - /* Intersect clip region with node's extents */ - if (clip.x0 < node_extents.x0) clip.x0 = node_extents.x0; - if (clip.y0 < node_extents.y0) clip.y0 = node_extents.y0; - if (clip.x1 > node_extents.x1) clip.x1 = node_extents.x1; - if (clip.y1 > node_extents.y1) clip.y1 = node_extents.y1; - - if (clip.x0 >= clip.x1 || clip.y0 >= clip.y1) { - /* Invalid clip rectangle */ - return; - } - - /* Set up the clipping area */ - plot->clip(&clip); - - /* Draw node's furniture */ - if (!(tree->flags & TREE_NO_FURNITURE)) { - /* Display furniture */ - if (node->previous != NULL) { - /* There is a node above this - * Display furniture; line connecting up to previous */ - x0 = x1 = tree_x + node->box.x - (NODE_INSTEP / 2); - y0 = tree_y + node->previous->box.y; - y1 = tree_y + node->box.y + (TREE_LINE_HEIGHT / 2); - plot->line(x0, y0, x1, y1, - &plot_style_stroke_tree_furniture); - } - if (node->next != NULL) { - /* There is a node below this - * Display furniture; line connecting down to next */ - x0 = x1 = tree_x + node->box.x - (NODE_INSTEP / 2); - y0 = tree_y + node->box.y + (TREE_LINE_HEIGHT / 2); - y1 = tree_y + node->next->box.y; - plot->line(x0, y0, x1, y1, - &plot_style_stroke_tree_furniture); - } - - parent = node->parent; - if ((parent != NULL) && (parent != tree->root) && - (parent->child == node)) { - /* Node is first child */ - x0 = x1 = tree_x + parent->box.x + (NODE_INSTEP / 2); - y0 = tree_y + parent->data.box.y + - parent->data.box.height; - y1 = y0 + (TREE_LINE_HEIGHT / 2); - plot->line(x0, y0, x1, y1, - &plot_style_stroke_tree_furniture); - } - /* Line from expansion toggle to icon */ - x0 = tree_x + node->box.x - (NODE_INSTEP / 2); - x1 = x0 + (NODE_INSTEP / 2) - 2; - y0 = y1 = tree_y + node->data.box.y + node->data.box.height - - (TREE_LINE_HEIGHT / 2); - plot->line(x0, y0, x1, y1, &plot_style_stroke_tree_furniture); - - tree_draw_node_expansion_toggle(tree, node, - tree_x, tree_y, ctx); - } - - /* Draw node's element(s) - * NOTE: node's children are handled later in tree_draw_tree() */ - if (node->expanded) { - for (element = &node->data; element != NULL; - element = element->next) { - /* Draw each element of expanded node */ - tree_draw_node_element(tree, element, tree_x, tree_y, - &clip, ctx); - } - } else { - /* Draw main title element of node */ - tree_draw_node_element(tree, &node->data, tree_x, tree_y, - &clip, ctx); - } -} - - -/** - * Redraws a node's descendants. - * - * \param tree the tree to draw - * \param node the node to draw children and siblings of - * \param tree_x X coordinate to draw the tree at (wrt plot origin) - * \param tree_y Y coordinate to draw the tree at (wrt plot origin) - * \param clip clipping rectangle (wrt plot origin) - * \param ctx current redraw context - */ -static void tree_draw_tree(struct tree *tree, struct node *node, - int tree_x, int tree_y, struct rect clip, - const struct redraw_context *ctx) -{ - struct node *child; - - assert(tree != NULL); - assert(node != NULL); - - for (child = node->child; child != NULL; child = child->next) { - /* Draw children that are inside the clip region */ - - if (child->next != NULL && - (child->next->box.y + tree_y < clip.y0)) - /* Child is above clip region */ - continue; - if (child->box.y + tree_y > clip.y1) - /* Child is below clip region - * further siblings will be too */ - return; - - /* Draw current child */ - tree_draw_node(tree, child, tree_x, tree_y, clip, ctx); - /* And its children */ - if ((child->child != NULL) && (child->expanded)) { - /* Child has children and they are visible */ - tree_draw_tree(tree, child, tree_x, tree_y, clip, ctx); - } - } -} - - -/** - * Redraws a tree. - * - * \param tree the tree to draw - * \param x X coordinate to draw the tree at (wrt plot origin) - * \param y Y coordinate to draw the tree at (wrt plot origin) - * \param clip_x minimum x of the clipping rectangle (wrt tree origin) - * \param clip_y minimum y of the clipping rectangle (wrt tree origin) - * \param clip_width width of the clipping rectangle - * \param clip_height height of the clipping rectangle - * \param ctx current redraw context - */ -void tree_draw(struct tree *tree, int x, int y, - int clip_x, int clip_y, int clip_width, int clip_height, - const struct redraw_context *ctx) -{ - struct redraw_context new_ctx = *ctx; - struct rect clip; - - assert(tree != NULL); - assert(tree->root != NULL); - - if (treeview_test_redraw(tree, x, y, clip_x, clip_y, - clip_width, clip_height, ctx)) { - return; - } - - /* 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 */ - clip.x0 = x + clip_x; - clip.y0 = y + clip_y; - clip.x1 = clip.x0 + clip_width; - clip.y1 = clip.y0 + clip_height; - new_ctx.plot->clip(&clip); - - /* Flat fill extents of clipping area */ - new_ctx.plot->rectangle(clip.x0, clip.y0, clip.x1, clip.y1, - &plot_style_fill_tree_background); - - /* don't draw empty trees or trees with redraw flag set to false */ - if (tree->root->child != NULL && tree->redraw) { - - /* Draw the tree */ - tree_draw_tree(tree, tree->root, x, y, clip, &new_ctx); - - /* Draw textarea, if present */ - if (tree->editing != NULL) { - x = x + tree->editing->box.x; - y = y + tree->editing->box.y; - if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON) - x += NODE_INSTEP; - textarea_redraw(tree->textarea, x, y, - plot_style_fill_tree_background. - fill_colour, - 1.0, &clip, &new_ctx); - } - } - - /* Rendering complete */ - if (ctx->plot->option_knockout) - knockout_plot_end(); -} - - -/** - * Finds a node element from a node with a specific user_type - * - * \param node the node to examine - * \param flag user assinged flag used is searches - * \param after if this is not NULL the search will start after the given - * node_element - * \return the corresponding element - */ -struct node_element *tree_node_find_element(struct node *node, - unsigned int flag, struct node_element *after) -{ - struct node_element *element; - - if (after == NULL) - element = &node->data; - else { - assert(after->parent == node); - element = after->next; - } - - for (; element != NULL; element = element->next) - if (element->flag == flag) return element; - - return NULL; -} - - -/** - * Deletes all selected nodes from the tree. - * - * \param tree the tree to delete from - * \param node the node to delete - */ -void tree_delete_selected_nodes(struct tree *tree, struct node *node) -{ - struct node *next; - int y = node->box.y; - int height = tree->height; - int width = tree->width; - bool redraw_setting = tree->redraw; - - tree->redraw = false; - - if (node == tree->root) { - if (node->child != NULL) - tree_delete_selected_nodes(tree, node->child); - - tree->redraw = redraw_setting; - - if (tree->redraw) - tree->callbacks->redraw_request(0, y, - width, height, - tree->client_data); - return; - } - - while (node != NULL) { - next = node->next; - if (node->selected) - tree_delete_node(tree, node, false); - else if (node->child != NULL) - tree_delete_selected_nodes(tree, node->child); - node = next; - } - - tree->redraw = redraw_setting; - - if (tree->redraw) - tree->callbacks->redraw_request(0, y, - width, height, - tree->client_data); -} - - -/** - * Returns the selected node, or NULL if multiple nodes are selected. - * - * \param node the node to search sibling and children - * \return the selected node, or NULL if multiple nodes are selected - */ -struct node *tree_get_selected_node(struct node *node) -{ - struct node *result = NULL; - struct node *temp; - - for (; node != NULL; node = node->next) { - if (node->selected) { - if (result != NULL) - return NULL; - result = node; - } - if ((node->child != NULL) && (node->expanded)) { - temp = tree_get_selected_node(node->child); - if (temp != NULL) { - if (result != NULL) - return NULL; - else - result = temp; - } - } - } - return result; -} - - -/** - * Finds a node element at a specific location. - * - * \param node the root node to check from - * \param x the x co-ordinate - * \param y the y co-ordinate - * \param expansion_toggle whether the coordinate was in an expansion toggle - * \return the node at the specified position, or NULL for none - */ -static struct node_element *tree_get_node_element_at(struct node *node, - int x, int y, bool *expansion_toggle) -{ - struct node_element *element; - int x0, x1, y0, y1; - - *expansion_toggle = false; - for (; node != NULL; node = node->next) { - if (node->box.y > y) return NULL; - if ((node->box.x - NODE_INSTEP < x) && (node->box.y < y) && - (node->box.x + node->box.width >= x) && - (node->box.y + node->box.height >= y)) { - if (node->expanded) { - for (element = &node->data; element != NULL; - element = element->next) { - x0 = element->box.x; - y0 = element->box.y; - x1 = element->box.x + - element->box.width; - y1 = element->box.y + - element->box.height; - if ((x0 < x) && (y0 < y) && (x1 >= x) - && (y1 >= y)) - return element; - } - } else { - x0 = node->data.box.x; - y0 = node->data.box.y; - x1 = node->data.box.x + node->data.box.width; - y1 = node->data.box.y + node->data.box.height; - if ((x0 < x) && (y0 < y) && (x1 >= x) && - (y1>= y)) - return &node->data; - } - if (((node->child != NULL) || - (node->data.next != NULL)) && - (node->data.box.x - NODE_INSTEP + 4 < x) - && (node->data.box.y + 4 < y) && - (node->data.box.x > x) && - (node->data.box.y + TREE_LINE_HEIGHT > y)) { - /* Node either has node children, or node - * has more than one element. - * Coordinate is over node expansion toggle area - */ - *expansion_toggle = true; - return &node->data; - } - } - - element = tree_get_node_element_at(node->child, x, y, - expansion_toggle); - if ((node->child != NULL) && (node->expanded) && - (element != NULL)) - return element; - } - return NULL; -} - - -/** - * Finds a node at a specific location. - * - * \param root the root node to check from - * \param x the x co-ordinate - * \param y the y co-ordinate - * \param expansion_toggle whether the coordinate was in an expansion toggle - * \return the node at the specified position, or NULL for none - */ -static struct node *tree_get_node_at(struct node *root, int x, int y, - bool *expansion_toggle) -{ - struct node_element *result; - - if ((result = tree_get_node_element_at(root, x, y, expansion_toggle))) - return result->parent; - return NULL; -} - - -/** - * Gets link characteristics to insert a node at a specified position. - * - * \param tree the tree to find link information for - * \param x the x co-ordinate - * \param y the y co-ordinate - * \param before set to whether the node should be linked before on exit - * \return the node to link with - */ -struct node *tree_get_link_details(struct tree *tree, int x, int y, - bool *before) -{ - struct node *node = NULL; - bool expansion_toggle; - - assert(tree != NULL); - assert(tree->root != NULL); - - *before = false; - if (tree->root->child != NULL) - node = tree_get_node_at(tree->root->child, x, y, - &expansion_toggle); - if ((node == NULL) || (expansion_toggle)) - return tree->root; - - if (y < (node->box.y + (node->box.height / 2))) { - *before = true; - } else if ((node->folder) && (node->expanded) && - (node->child != NULL)) { - node = node->child; - *before = true; - } - return node; -} - - -/** - * Launches all the selected nodes of the tree - * - * \param tree the tree for which all nodes will be launched - * \param node the node which will be checked together with its children - * \param tabs launch node in a new tab instead of a new window - */ -static void tree_launch_selected_internal(struct tree *tree, struct node *node, - bool tabs) -{ - struct node_msg_data msg_data; - - msg_data.data.bw = NULL; - - for (; node != NULL; node = node->next) { - if (node->selected && node->user_callback != NULL) { - msg_data.msg = NODE_LAUNCH; - if (tabs == true) { - msg_data.flag = TREE_ELEMENT_LAUNCH_IN_TABS; - } else { - msg_data.flag = TREE_ELEMENT_TITLE; - } - - msg_data.node = node; - node->user_callback(node->callback_data, &msg_data); - } - if (node->child != NULL) - tree_launch_selected_internal(tree, node->child, tabs); - } -} - - -/** - * Launches all the selected nodes of the tree - * - * \param tree the tree for which all nodes will be launched - * \param tabs launch nodes in new tabs instead of new windows - */ -void tree_launch_selected(struct tree *tree, bool tabs) -{ - if (tree->root->child != NULL) - tree_launch_selected_internal(tree, tree->root->child, tabs); -} - - -/** - * Updates the node at position x,y to a selected state. - * The required areas of the tree are redrawn. - * - * \param tree the tree to update nodes for, may be NULL - * \param x x position in tree - * \param y y position in tree - * \param selected the selection state to set - */ -void tree_set_node_selected_at(struct tree *tree, int x, int y, bool selected) -{ - bool expansion_toggle; - struct node *node; - - node = tree_get_node_at(tree->root, x, y, &expansion_toggle); - - if ((node == NULL) || (expansion_toggle == true)) - return; - - tree_set_node_selected(tree, node, false, selected); -} - - -/** - * Handles a mouse action for a tree - * - * \param tree the tree to handle a click for - * \param mouse the mouse state - * \param x X coordinate of mouse action - * \param y Y coordinate of mouse action - * \return whether the click was handled - */ -bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x, - int y) -{ - bool expansion_toggle; - struct node *node; - struct node *last; - struct node_element *element; - struct node_msg_data msg_data; - - bool double_click_1 = mouse & BROWSER_MOUSE_DOUBLE_CLICK && - mouse & BROWSER_MOUSE_CLICK_1; - bool double_click_2 = mouse & BROWSER_MOUSE_DOUBLE_CLICK && - mouse & BROWSER_MOUSE_CLICK_2; - - assert(tree != NULL); - assert(tree->root != NULL); - - if (treeview_test_mouse_action(tree, mouse, x, y)) { - return true; - } - - if (tree->root->child == NULL) - return true; - - element = tree_get_node_element_at(tree->root->child, x, y, - &expansion_toggle); - - /* pass in-textarea mouse action and drags which started in it - to the textarea */ - if (tree->editing != NULL) { - int x0, x1, y0, y1; - x0 = tree->editing->box.x; - if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON) - x0 += NODE_INSTEP; - x1 = tree->editing->box.x + tree->editing->box.width; - y0 = tree->editing->box.y; - y1 = tree->editing->box.y + tree->ta_height; - - if (tree->drag == TREE_TEXTAREA_DRAG && - (mouse & (BROWSER_MOUSE_HOLDING_1 | - BROWSER_MOUSE_HOLDING_2))) { - /* Track the drag path */ - textarea_mouse_action(tree->textarea, mouse, - x - x0, y - y0); - return true; - } - - if ((x >= x0) && (x < x1) && (y >= y0) && (y < y1)) { - textarea_mouse_action(tree->textarea, mouse, - x - x0, y - y0); - return true; - - } - } - - /* we are not interested in the drag path, return */ - if (mouse & (BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_HOLDING_2)) - return true; - - /* cancel edit */ - if (tree->editing != NULL) - tree_stop_edit(tree, false); - - /* no item either means cancel selection on (select) click or a drag */ - if (element == NULL) { - if (tree->flags & TREE_SINGLE_SELECT) { - tree_set_node_selected(tree, tree->root->child, true, - false); - return true; - } - if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_DRAG_1)) - tree_set_node_selected(tree, tree->root->child, true, - false); - if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { - - /** @todo the tree window has to scroll the tree when - * mouse reaches border while dragging this isn't - * solved for the browser window too. - */ - tree->drag = TREE_SELECT_DRAG; - } - return true; - } - - node = element->parent; - - /* A click on expansion toggle or double click on folder toggles node - * expansion */ - if (((expansion_toggle) && (mouse & (BROWSER_MOUSE_CLICK_1 | - BROWSER_MOUSE_CLICK_2))) || - (((!expansion_toggle) && (node->child != NULL)) && - (double_click_1 || double_click_2))) { - - /* clear any selection */ - tree_set_node_selected(tree, tree->root->child, true, false); - - /* expand / contract node and redraw */ - tree_set_node_expanded(tree, node, !node->expanded, - false, false); - - /* find the last child node if expanded */ - last = node; - if ((last->child != NULL) && (last->expanded)) { - last = last->child; - while ((last->next != NULL) || - ((last->child != NULL) && - (last->expanded))) { - if (last->next != NULL) - last = last->next; - else - last = last->child; - } - } - /* scroll to the bottom element then back to the top */ - element = &last->data; - if (last->expanded) - for (; element->next != NULL; element = element->next); - tree->callbacks->scroll_visible(element->box.y, - element->box.height, - tree->client_data); - tree->callbacks->scroll_visible(node->data.box.y, - node->data.box.height, - tree->client_data); - return true; - } - - /* no use for any other expansion toggle click */ - if (expansion_toggle) - return true; - - /* single/double ctrl+click or alt+click starts editing */ - if ((element->editable) && (!tree->editing) && - ((element->type == NODE_ELEMENT_TEXT) || - (element->type == NODE_ELEMENT_TEXT_PLUS_ICON)) && - (mouse & BROWSER_MOUSE_CLICK_1 || double_click_1) && - (mouse & BROWSER_MOUSE_MOD_2 || - mouse & BROWSER_MOUSE_MOD_3)) { - tree_set_node_selected(tree, tree->root->child, true, false); - tree_start_edit(tree, element); - return true; - } - - /* double click launches the leaf */ - if (double_click_1 || double_click_2) { - if (node->user_callback == NULL) - return false; - msg_data.msg = NODE_LAUNCH; - msg_data.flag = TREE_ELEMENT_TITLE; - msg_data.node = node; - if (node->user_callback(node->callback_data, &msg_data) != - NODE_CALLBACK_HANDLED) - return false; - - return true; - } - - /* single click (select) cancels current selection and selects item */ - if (mouse & BROWSER_MOUSE_CLICK_1 || (mouse & BROWSER_MOUSE_CLICK_2 && - tree->flags & TREE_SINGLE_SELECT)) { - if (tree->flags & TREE_NO_SELECT) - return true; - if (!node->selected) { - tree_set_node_selected(tree, tree->root->child, true, - false); - node->selected = true; - tree_handle_node_element_changed(tree, &node->data, false); - } - return true; - } - - /* single click (adjust) toggles item selection */ - if (mouse & BROWSER_MOUSE_CLICK_2) { - if (tree->flags & TREE_NO_SELECT) - return true; - node->selected = !node->selected; - tree_handle_node_element_changed(tree, &node->data, false); - return true; - } - - /* drag starts a drag operation */ - if ((!tree->editing) && (mouse & (BROWSER_MOUSE_DRAG_1 | - BROWSER_MOUSE_DRAG_2))) { - if (tree->flags & TREE_NO_DRAGS) - return true; - - if (!node->selected) { - tree_set_node_selected(tree, tree->root->child, true, - false); - node->selected = true; - tree_handle_node_element_changed(tree, &node->data, false); - } - - if (tree->flags & TREE_MOVABLE) - tree->drag = TREE_MOVE_DRAG; - else tree->drag = TREE_UNKNOWN_DRAG; - - return true; - } - - - return false; -} - - -/** - * Updates the selected state for a region of nodes. - * - * \param tree the tree to update - * \param node the node to update children and siblings of - * \param y the minimum y of the selection rectangle - * \param height the height of the selection rectangle - * \param invert whether to invert the selected state - */ -static void tree_handle_selection_area_node(struct tree *tree, - struct node *node, int y, int height, bool invert) -{ - struct node_element *element; - struct node *update; - int y_max; - int y0, y1; - - assert(tree != NULL); - assert(node != NULL); - - y_max = y + height; - - for (; node != NULL; node = node->next) { - if (node->box.y > y_max) return; - y0 = node->box.y; - y1 = node->box.y + node->box.height; - if ((y0 < y_max) && (y1 >= y)) { - update = NULL; - if (node->expanded) { - for (element = &node->data; element != NULL; - element = element->next) { - y0 = element->box.y; - y1 = element->box.y + - element->box.height; - if ((y0 < y_max) && (y1 >= y)) { - update = element->parent; - break; - } - } - } else { - y0 = node->data.box.y; - y1 = node->data.box.y + node->data.box.height; - if ((y0 < y_max) && (y1 >= y)) - update = node->data.parent; - } - if ((update) && (node != tree->root)) { - if (invert) { - node->selected = !node->selected; - tree_handle_node_element_changed(tree, - &node->data, false); - } else if (!node->selected) { - node->selected = true; - tree_handle_node_element_changed(tree, - &node->data, false); - } - } - } - if ((node->child != NULL) && (node->expanded)) - tree_handle_selection_area_node(tree, node->child, y, - height, invert); - } -} - - -/** - * Updates the selected state for a region of nodes. - * - * \param tree the tree to update - * \param y the minimum y of the selection rectangle - * \param height the height of the selection rectangle - * \param invert whether to invert the selected state - */ -static void tree_handle_selection_area(struct tree *tree, int y, int height, - bool invert) -{ - assert(tree != NULL); - assert(tree->root != NULL); - - if (tree->root->child == NULL) - return; - - if (height < 0) { - y += height; - height = -height; - } - tree_handle_selection_area_node(tree, tree->root->child, y, height, - invert); -} - - -/** - * Clears the processing flag. - * - * \param node the node to process siblings and children of - */ -static void tree_clear_processing(struct node *node) -{ - for (; node != NULL; node = node->next) { - node->processing = false; - if (node->child != NULL) - tree_clear_processing(node->child); - } -} - - -/** - * Sets the processing flag to the selection state. - * - * \param node the node to process siblings and children of - */ -static void tree_selected_to_processing(struct node *node) -{ - for (; node != NULL; node = node->next) { - node->processing = node->selected; - if ((node->child != NULL) && (node->expanded)) - tree_selected_to_processing(node->child); - } -} - - -/** - * Moves the first node in a tree with the processing flag set. - * - * \param tree the tree in which the move takes place - * \param node the node to move siblings/children of - * \param link the node to link before/as a child (folders) or before/after - * (link) - * \param before whether to link siblings before or after the supplied node - * \param first whether to always link after the supplied node (ie not - * inside of folders) - * \return the node moved - */ -static struct node *tree_move_processing_node(struct tree *tree, - struct node *node, struct node *link, bool before, bool first) -{ - struct node *result; - - bool folder = link->folder; - for (; node != NULL; node = node->next) { - if (node->processing) { - node->processing = false; - tree_delink_node(tree, node); - if (!first) - link->folder = false; - tree_link_node(tree, link, node, before); - if (!first) - link->folder = folder; - return node; - } - if (node->child != NULL) { - result = tree_move_processing_node(tree, node->child, - link, before, first); - if (result != NULL) - return result; - } - } - return NULL; -} - - -/** - * Moves nodes within a tree. - * - * \param tree the tree to process - * \param destination the node to link before/as a child (folders) - * or before/after (link) - * \param before whether to link siblings before or after the supplied - * node - */ -static void tree_move_selected_nodes(struct tree *tree, - struct node *destination, bool before) -{ - struct node *link; - struct node *test; - bool error; - - tree_clear_processing(tree->root); - tree_selected_to_processing(tree->root); - - /* the destination node cannot be a child of any node with - the processing flag set */ - error = destination->processing; - for (test = destination; test != NULL; test = test->parent) - error |= test->processing; - if (error) { - tree_clear_processing(tree->root); - return; - } - if ((destination->folder) && (!destination->expanded) && (!before)) { - tree_set_node_expanded(tree, destination, true, false, false); - } - link = tree_move_processing_node(tree, tree->root, destination, before, - true); - while (link != NULL) - link = tree_move_processing_node(tree, tree->root, link, false, - false); - - tree_clear_processing(tree->root); - tree_recalculate_node_positions(tree, tree->root); - if (tree->redraw) - tree->callbacks->redraw_request(0, 0, tree->width, tree->height, - tree->client_data); -} - - -/** - * Handle the end of a drag operation - * - * \param tree the tree on which the drag was performed - * \param mouse mouse state during drag end - * \param x0 x coordinate of drag start - * \param y0 y coordinate of drag start - * \param x1 x coordinate of drag end - * \param y1 y coordinate of drag end - */ -void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0, - int x1, int y1) -{ - - bool before; - struct node *node; - int x, y; - - if (treeview_test_mouse_action(tree, BROWSER_MOUSE_HOVER, x1, y1)) { - return; - } - - switch (tree->drag) { - case TREE_NO_DRAG: - case TREE_UNKNOWN_DRAG: - break; - - case TREE_TEXTAREA_DRAG: - x = tree->editing->box.x; - y = tree->editing->box.y; - if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON) - x += NODE_INSTEP; - textarea_mouse_action(tree->textarea, BROWSER_MOUSE_HOVER, - x1 - x, y1 - y); - break; - - case TREE_SELECT_DRAG: - tree_handle_selection_area(tree, y0, y1 - y0, - (mouse | BROWSER_MOUSE_HOLDING_2)); - break; - - case TREE_MOVE_DRAG: - if (!(tree->flags & TREE_MOVABLE)) - return; - node = tree_get_link_details(tree, x1, y1, &before); - tree_move_selected_nodes(tree, node, before); - break; - } - - tree->drag = TREE_NO_DRAG; -} - - -/** - * Key press handling for a tree. - * - * \param tree The tree which got the keypress - * \param key The ucs4 character codepoint - * \return true if the keypress is dealt with, false otherwise. - */ -bool tree_keypress(struct tree *tree, uint32_t key) -{ - if (treeview_test_keypress(tree, key)) { - return true; - } - - if (tree->editing != NULL) - switch (key) { - case KEY_ESCAPE: - tree_stop_edit(tree, false); - return true; - case KEY_NL: - case KEY_CR: - tree_stop_edit(tree, true); - return true; - default: - return textarea_keypress(tree->textarea, key); - } - - return false; -} - - -/** - * Alphabetical comparison function for nodes - * - * \param n1 first node to compare - * \param n2 first node to compare - * \return 0 if equal, greater then zero if n1 > n2, - * less then zero if n2 < n1 - */ -int tree_alphabetical_sort(struct node *n1, struct node *n2) -{ - return strcmp(n1->data.text, n2->data.text); -} - - -/** - * Redraw requests from the textarea are piped through this because we have to - * check the redraw flag of the tree before requesting a redraw and change the - * position to tree origin relative. - */ - -static void tree_textarea_callback(void *data, struct textarea_msg *msg) -{ - struct tree *tree = data; - int x, y; - - switch (msg->type) { - case TEXTAREA_MSG_DRAG_REPORT: - if (msg->data.drag == TEXTAREA_DRAG_NONE) { - /* Textarea drag finished */ - tree->drag = TREE_NO_DRAG; - } else { - /* Textarea drag started */ - tree->drag = TREE_TEXTAREA_DRAG; - } - break; - - case TEXTAREA_MSG_REDRAW_REQUEST: - x = msg->data.redraw.x0 + tree->editing->box.x; - y = msg->data.redraw.y0 + tree->editing->box.y; - - if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON) - x += NODE_INSTEP; - - /* Redraw the textarea */ - if (tree->redraw) - tree->callbacks->redraw_request(x, y, - msg->data.redraw.x1 - - msg->data.redraw.x0, - msg->data.redraw.y1 - - msg->data.redraw.y0, - tree->client_data); - break; - - default: - break; - } -} - - -/** - * Starts editing a node_element - * - * \param tree The tree to which element belongs - * \param element The element to start being edited - */ -void tree_start_edit(struct tree *tree, struct node_element *element) -{ - struct node *parent; - int width, height; - textarea_setup ta_setup; - textarea_flags ta_flags; - - assert(tree != NULL); - assert(element != NULL); - - if (tree->editing != NULL) - tree_stop_edit(tree, true); - - parent = element->parent; - if (&parent->data == element) - parent = parent->parent; - for (; parent != NULL; parent = parent->parent) { - if (!parent->expanded) { - tree_set_node_expanded(tree, parent, true, - false, false); - } - } - - tree->editing = element; - tree->callbacks->get_window_dimensions(&width, NULL, tree->client_data); - width -= element->box.x; - height = element->box.height; - if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON) - width -= NODE_INSTEP; - - tree->ta_height = height; - - ta_flags = TEXTAREA_INTERNAL_CARET; - - ta_setup.width = width; - ta_setup.height = tree->ta_height; - ta_setup.pad_top = 0; - ta_setup.pad_right = 4; - ta_setup.pad_bottom = 0; - ta_setup.pad_left = 4; - ta_setup.border_width = 1; - ta_setup.border_col = 0x000000; - ta_setup.selected_text = 0xffffff; - ta_setup.selected_bg = 0x000000; - ta_setup.text = plot_fstyle; - ta_setup.text.foreground = 0x000000; - ta_setup.text.background = 0xffffff; - - tree->textarea = textarea_create(ta_flags, &ta_setup, - tree_textarea_callback, tree); - if (tree->textarea == NULL) { - tree_stop_edit(tree, false); - return; - } - textarea_set_text(tree->textarea, element->text); - - tree_handle_node_element_changed(tree, element, true); - tree_recalculate_size(tree); - tree->callbacks->scroll_visible(element->box.y, element->box.height, - tree->client_data); -} - - -/** - * Callback for fetchcache(). Should be removed once bitmaps get loaded directly - * from disc - */ -static nserror tree_icon_callback(hlcache_handle *handle, - const hlcache_event *event, void *pw) -{ - return NSERROR_OK; -} - - -/** - * Tree utility function. Placed here so that this code doesn't have to be - * copied by each user. - * - * \param name the name of the loaded icon, if it's not a full path the icon is - * looked for in the directory specified by tree_icons_dir - * \return the icon in form of a content or NULL on failure - */ -hlcache_handle *tree_load_icon(const char *name) -{ - char *url = NULL; - const char *icon_url = NULL; - int len; - hlcache_handle *c; - nserror err; - nsurl *icon_nsurl; - - /** @todo something like bitmap_from_disc is needed here */ - - if (!strncmp(name, "file://", 7)) { - icon_url = name; - } else { - char *native_path; - - if (tree_icons_dir == NULL) - return NULL; - - /* path + separator + leafname + '\0' */ - len = strlen(tree_icons_dir) + 1 + strlen(name) + 1; - native_path = malloc(len); - if (native_path == NULL) { - LOG(("malloc failed")); - warn_user("NoMemory", 0); - return NULL; - } - - /* Build native path */ - memcpy(native_path, tree_icons_dir, - strlen(tree_icons_dir) + 1); - path_add_part(native_path, len, name); - - /* Convert native path to URL */ - url = path_to_url(native_path); - - free(native_path); - icon_url = url; - } - - err = nsurl_create(icon_url, &icon_nsurl); - if (err != NSERROR_OK) { - if (url != NULL) - free(url); - return NULL; - } - - /* Fetch the icon */ - err = hlcache_handle_retrieve(icon_nsurl, 0, 0, 0, - tree_icon_callback, 0, 0, - CONTENT_IMAGE, &c); - - nsurl_unref(icon_nsurl); - - /* If we built the URL here, free it */ - if (url != NULL) - free(url); - - if (err != NSERROR_OK) { - return NULL; - } - - return c; -} diff --git a/desktop/tree.h b/desktop/tree.h index 84d90f316..52bea7d13 100644 --- a/desktop/tree.h +++ b/desktop/tree.h @@ -28,48 +28,22 @@ #include #include "desktop/browser.h" -#include "image/bitmap.h" struct sslcert_session_data; extern struct sslcert_session_data *ssl_current_session; extern const char *tree_hotlist_path; - -struct hlcache_handle; - /* Tree flags */ enum tree_flags { - TREE_NO_FLAGS = 0, - TREE_NO_DRAGS = 1, - TREE_NO_FURNITURE = 2, - TREE_SINGLE_SELECT = 4, - TREE_NO_SELECT = 8, - TREE_MOVABLE = 16, - TREE_DELETE_EMPTY_DIRS = 32, /**< if the last child of a - * directory is deleted the - * directory will be deleted - * too. - */ - /* The following are to aid transition to new treeviews */ - TREE_HISTORY = 64, - TREE_COOKIES = 128, - TREE_SSLCERT = 256, - TREE_HOTLIST = 512 + TREE_HISTORY, + TREE_COOKIES, + TREE_SSLCERT, + TREE_HOTLIST }; -/** A "flag" value to indicate the element data contains title - * text. This value should be the first node_element in every - * node. All other values should be different than this one. The term - * flag is misused as it is actually a value used by the API consumer - * to indicate teh type of data a node element contains. - */ -#define TREE_ELEMENT_TITLE 0x00 -#define TREE_ELEMENT_LAUNCH_IN_TABS 0x05 /* Launch in tabs instead of windows */ struct tree; -struct node; -struct node_element; typedef enum { TREE_NO_DRAG = 0, @@ -79,45 +53,6 @@ typedef enum { TREE_UNKNOWN_DRAG /** < A drag the tree itself won't handle */ } tree_drag_type; -typedef enum { - NODE_ELEMENT_TEXT, /**< Text only */ - NODE_ELEMENT_TEXT_PLUS_ICON, /**< Text and icon */ - NODE_ELEMENT_BITMAP /**< Bitmap only */ -} node_element_type; - -typedef enum { - NODE_DELETE_ELEMENT_TXT, /**< The text of an element of the - * node is being deleted */ - NODE_DELETE_ELEMENT_IMG, /**< The bitmap or icon of a node is - * being deleted */ - NODE_LAUNCH, /**< The node has been launched */ - NODE_ELEMENT_EDIT_CANCELLED, /**< Editing opperation cancelled. */ - NODE_ELEMENT_EDIT_FINISHING, /**< New text has to be accepted - * or rejected. */ - NODE_ELEMENT_EDIT_FINISHED /**< Editing of a node_element has - * been finished. */ -} node_msg; - -typedef enum { - NODE_CALLBACK_HANDLED, - NODE_CALLBACK_NOT_HANDLED, - NODE_CALLBACK_REJECT, /**< reject new text for node element - * and leave editing mode. */ - NODE_CALLBACK_CONTINUE /**< don't leave editig mode. */ -} node_callback_resp; - -/** Internal node message. */ -struct node_msg_data { - node_msg msg; /**< The type of message. */ - unsigned int flag; /**< message flags. */ - struct node *node; /**< tree node messsage concerns. */ - union { - char *text; /**< textural data. */ - void *bitmap; /**< bitmap data. */ - struct browser_window *bw; /**< clone browser_window. */ - } data; /**< The message data. */ -}; - /** callbacks to perform necessary operations on treeview. */ struct treeview_table { void (*redraw_request)(int x, int y, int width, int height, @@ -128,123 +63,20 @@ struct treeview_table { void (*get_window_dimensions)(int *width, int *height, void *data); /**< get dimensions of window */ }; -/** - * Informs the client about any events requiring his action - * - * \param user_data the user data which was passed at tree creation - * \param msg_data structure containing all the message information - * \return the appropriate node_callback_resp response - */ -typedef node_callback_resp (*tree_node_user_callback)(void *user_data, - struct node_msg_data *msg_data); - -/* Non-platform specific code */ - -void tree_set_icon_dir(char *icon_dir); -void tree_setup_colours(void); - -/* Functions for creating/deleting tree primitives and for tree structure - manipulation */ struct tree *tree_create(unsigned int flags, const struct treeview_table *callbacks, void *client_data); -/** - * Creates a folder node with the specified title, and optionally links it into - * the tree. - * - * \param tree the owner tree of 'parent', may be NULL - * \param parent the parent node, or NULL not to link - * \param title the node title - * \param editable if true, the node title will be editable - * \param retain_in_memory if true, the node will stay in memory after deletion - * \param deleted if true, the node is created with the deleted flag - * \return the newly created node or NULL on error. - */ -struct node *tree_create_folder_node(struct tree *tree, struct node *parent, - const char *title, bool editable, bool retain_in_memory, - bool deleted); - -/** - * Creates a leaf node with the specified title, and optionally links it into - * the tree. - * - * \param tree the owner tree of 'parent', may be NULL - * \param parent the parent node, or NULL not to link - * \param title the node title. - * \param editable if true, the node title will be editable - * \param retain_in_memory if true, the node will stay in memory after deletion - * \param deleted if true, the node is created with the deleted flag - * \return the newly created node or NULL on error. - */ -struct node *tree_create_leaf_node(struct tree *tree, struct node *parent, - const char *title, bool editable, bool retain_in_memory, - bool deleted); - -struct node_element *tree_create_node_element(struct node *parent, - node_element_type type, unsigned int flag, bool editable); -void tree_link_node(struct tree *tree, struct node *link, struct node *node, - bool before); -void tree_delink_node(struct tree *tree, struct node *node); void tree_delete(struct tree *tree); -void tree_delete_node(struct tree *tree, struct node *node, bool siblings); - -/* setters and getters for properties and data */ -void tree_set_node_icon(struct tree *tree, struct node *node, - struct hlcache_handle *icon); -void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded, - bool folder, bool leaf); -void tree_set_node_selected(struct tree *tree, struct node *node, bool all, - bool selected); -void tree_set_node_selected_at(struct tree *tree, int x, int y, bool selected); -void tree_set_node_sort_function(struct tree *tree, struct node *node, - int (*sort) (struct node *, struct node *)); -void tree_set_node_user_callback(struct node *node, - tree_node_user_callback callback, void *data); -void tree_set_redraw(struct tree *tree, bool redraw); -bool tree_get_redraw(struct tree *tree); -bool tree_node_has_selection(struct node *node); -bool tree_node_is_deleted(struct node *node); -bool tree_node_is_folder(struct node *node); -bool tree_node_is_default(struct node *node); -void tree_update_node_element(struct tree *tree, struct node_element *element, - const char *text, void *bitmap); -bool tree_update_element_text(struct tree *tree, struct node_element *element, char *text); -const char *tree_node_element_get_text(struct node_element *element); -struct bitmap *tree_node_element_get_icon(struct node_element *element); -struct node *tree_get_root(struct tree *tree); -bool tree_is_edited(struct tree *tree); tree_drag_type tree_drag_status(struct tree *tree); - -struct node *tree_get_default_folder_node(struct tree *tree); -bool tree_set_default_folder_node(struct tree *tree, struct node *node); -void tree_clear_default_folder_node(struct tree *tree); - -/* functions for traversing the tree */ -struct node *tree_node_get_parent(struct node *node); -struct node *tree_node_get_child(struct node *node); -struct node *tree_node_get_next(struct node *node); - void tree_draw(struct tree *tree, int x, int y, int clip_x, int clip_y, int clip_width, int clip_height, const struct redraw_context *ctx); - -struct node_element *tree_node_find_element(struct node *node, - unsigned int flag, struct node_element *after); -void tree_delete_selected_nodes(struct tree *tree, struct node *node); -struct node *tree_get_selected_node(struct node *node); -struct node *tree_get_link_details(struct tree *tree, int x, int y, - bool *before); -void tree_launch_selected(struct tree *tree, bool tabs); - bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x, int y); void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0, int x1, int y1); bool tree_keypress(struct tree *tree, uint32_t key); -int tree_alphabetical_sort(struct node *, struct node *); -void tree_start_edit(struct tree *tree, struct node_element *element); -struct hlcache_handle *tree_load_icon(const char *name); #endif -- cgit v1.2.3