diff options
Diffstat (limited to 'desktop/tree.c')
-rw-r--r-- | desktop/tree.c | 1113 |
1 files changed, 1113 insertions, 0 deletions
diff --git a/desktop/tree.c b/desktop/tree.c new file mode 100644 index 000000000..d504ddcba --- /dev/null +++ b/desktop/tree.c @@ -0,0 +1,1113 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + */ + +/** \file + * Generic tree handling (implementation). + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "netsurf/desktop/tree.h" +#include "netsurf/desktop/options.h" +#include "netsurf/utils/log.h" +#include "netsurf/utils/utils.h" + +static void tree_recalculate_node(struct node *node, bool recalculate_sizes); +static void tree_recalculate_node_positions(struct node *root); +static void tree_draw_node(struct tree *tree, struct node *node, int clip_x, int clip_y, + int clip_width, int clip_height); +static struct node_element *tree_create_node_element(struct node *parent, int user_type); +static int tree_get_node_width(struct node *node); +static int tree_get_node_height(struct node *node); +static void tree_reset_URL_node(struct tree *tree, struct node *node); +static void tree_handle_selection_area_node(struct tree *tree, struct node *node, int x, int y, + int width, int height, bool invert); +static void tree_selected_to_processing(struct node *node); +struct node *tree_move_processing_node(struct node *node, struct node *link, bool before, + bool first); + +static int tree_initialising = 0; + + +/** + * Initialises a user-created tree + * + * \param tree the tree to initialise + */ +void tree_initialise(struct tree *tree) { + + assert(tree); + + tree_set_node_expanded(tree->root, true); + tree_initialise_nodes(tree->root); + tree_recalculate_node_positions(tree->root); + tree_set_node_expanded(tree->root, false); + tree->root->expanded = true; + tree_recalculate_node_positions(tree->root); + tree_recalculate_size(tree); +} + + +/** + * Initialises a user-created node structure + * + * \param root the root node to update from + */ +void tree_initialise_nodes(struct node *root) { + struct node *node; + + assert(root); + + tree_initialising++; + for (node = root; node; node = node->next) { + tree_recalculate_node(node, true); + if (node->child) { + tree_initialise_nodes(node->child); + } + } + tree_initialising--; + + if (tree_initialising == 0) + tree_recalculate_node_positions(root); +} + + +/** + * Recalculate the node data and redraw the relevant section of the tree. + * + * \param tree the tree to redraw + * \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 + */ +void tree_handle_node_changed(struct tree *tree, struct node *node, + bool recalculate_sizes, bool expansion) { + int width, height; + + assert(node); + + if ((expansion) && (node->expanded) && (node->child)) { + tree_set_node_expanded(node->child, false); + tree_set_node_selected(tree, node->child, false); + } + + width = node->box.width; + height = node->box.height; + if (recalculate_sizes) + tree_recalculate_node(node, true); + else if (expansion) + tree_recalculate_node(node, false); + if ((node->box.height != height) || (expansion)) { + tree_recalculate_node_positions(tree->root); + tree_redraw_area(tree, 0, node->box.y, 16384, 16384); + } else { + width = (width > node->box.width) ? width : node->box.width; + tree_redraw_area(tree, node->box.x, node->box.y, width, node->box.height); + } + if ((recalculate_sizes) || (expansion)) + tree_recalculate_size(tree); +} + + +/** + * Recalculate the node element and redraw the relevant section of the tree. + * The tree size is not updated. + * + * \param tree the tree to redraw + * \param element the node element to update + */ +void tree_handle_node_element_changed(struct tree *tree, struct node_element *element) { + int width, height; + + assert(element); + + width = element->box.width; + height = element->box.height; + tree_recalculate_node_element(element); + + if (element->box.height != height) { + tree_recalculate_node(element->parent, false); + tree_redraw_area(tree, 0, element->box.y, 16384, 16384); + } else { + if (element->box.width != width) + tree_recalculate_node(element->parent, false); + width = (width > element->box.width) ? width : + element->box.width; + tree_redraw_area(tree, element->box.x, element->box.y, width, element->box.height); + } +} + + +/** + * Recalculates the size of a node. + * + * \param node the node to update + * \param recalculate_sizes whether the node elements have changed + */ +void tree_recalculate_node(struct node *node, bool recalculate_sizes) { + struct node_element *element; + int width, height; + + assert(node); + + width = node->box.width; + height = node->box.height; + node->box.width = 0; + node->box.height = 0; + if (node->expanded) { + for (element = &node->data; element; element = element->next) { + if (recalculate_sizes) + tree_recalculate_node_element(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) + tree_recalculate_node_element(&node->data); + node->box.width = node->data.box.width; + node->box.height = node->data.box.height; + } + + if (height != node->box.height) { + for (; node->parent; node = node->parent); + if (tree_initialising == 0) + tree_recalculate_node_positions(node); + } +} + + +/** + * Recalculates the position of a node, its siblings and children. + * + * \param root the root node to update from + */ +void tree_recalculate_node_positions(struct node *root) { + struct node *parent; + struct node *node; + struct node *child; + struct node_element *element; + int y; + + for (node = root; node; node = node->next) { + if (node->previous) { + node->box.x = node->previous->box.x; + node->box.y = node->previous->box.y + + tree_get_node_height(node->previous); + } else if ((parent = node->parent)) { + 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 = 0; + node->box.y = -40; + } + if (node->expanded) { + if (node->folder) { + node->data.box.x = node->box.x; + node->data.box.y = node->box.y; + tree_recalculate_node_positions(node->child); + } else { + y = node->box.y; + for (element = &node->data; element; + element = element->next) { + if (element->type == NODE_ELEMENT_TEXT_PLUS_SPRITE) { + element->box.x = node->box.x; + } else { + element->box.x = node->box.x + NODE_INSTEP; + } + element->box.y = y; + y += element->box.height; + } + } + } else { + node->data.box.x = node->box.x; + node->data.box.y = node->box.y; + } + } +} + + +/** + * 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 + */ +int tree_get_node_width(struct node *node) { + int width = 0; + int child_width; + + assert(node); + + for (; node; node = node->next) { + if (width < (node->box.x + node->box.width)) + width = node->box.x + node->box.width; + if ((node->child) && (node->expanded)) { + child_width = tree_get_node_width(node->child); + if (width < child_width) + width = child_width; + } + } + return width; +} + + +/** + * 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 + */ +int tree_get_node_height(struct node *node) { + int y1; + + assert(node); + + if ((node->child) && (node->expanded)) { + y1 = node->box.y; + if (y1 < 0) + y1 = 0; + node = node->child; + while ((node->next) || ((node->child) && (node->expanded))) { + for (; node->next; node = node->next); + if ((node->child) && (node->expanded)) + node = node->child; + } + return node->box.y + node->box.height - y1; + } else { + return node->box.height; + } +} + + +/** + * Updates all siblinds and descendants of a node to an expansion state. + * No update is performed for the tree changes. + * + * \param node the node to set all siblings and descendants of + * \param expanded the expansion state to set + */ +void tree_set_node_expanded(struct node *node, bool expanded) { + for (; node; node = node->next) { + if (node->expanded != expanded) { + node->expanded = expanded; + tree_recalculate_node(node, false); + } + if ((node->child) && (node->expanded)) + tree_set_node_expanded(node->child, expanded); + } +} + + +/** + * Updates all siblinds 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 + * \param leaf whether to update leaves + * \return whether any changes were made + */ +bool tree_handle_expansion(struct tree *tree, struct node *node, bool expanded, bool folder, + bool leaf) { + struct node *entry = node; + bool redraw = false; + + for (; node; node = node->next) { + if ((node->expanded != expanded) && (node != tree->root) && + ((folder && (node->folder)) || (leaf && (!node->folder)))) { + node->expanded = expanded; + if (node->child) + tree_set_node_expanded(node->child, false); + tree_recalculate_node(node, false); + redraw = true; + } + if ((node->child) && (node->expanded)) + redraw |= tree_handle_expansion(tree, node->child, expanded, folder, leaf); + } + if ((entry == tree->root) && (redraw)) { + tree_recalculate_node_positions(tree->root); + tree_redraw_area(tree, 0, 0, 16384, 16384); + tree_recalculate_size(tree); + } + return redraw; +} + + +/** + * Updates all siblinds and descendants of a node to an selected state. + * The required areas of the tree are redrawn. + * + * \param tree the tree to update nodes for + * \param node the node to set all siblings and descendants of + * \param selected the selection state to set + */ +void tree_set_node_selected(struct tree *tree, struct node *node, bool selected) { + for (; node; node = node->next) { + if ((node->selected != selected) && (node != tree->root)) { + node->selected = selected; + tree_redraw_area(tree, node->box.x, node->box.y, node->box.width, + node->data.box.height); + } + if ((node->child) && (node->expanded)) + tree_set_node_selected(tree, node->child, selected); + } +} + + +/** + * 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 furniture whether the returned area was in an elements furniture + * \return the node at the specified position, or NULL for none + */ +struct node *tree_get_node_at(struct node *root, int x, int y, bool *furniture) { + struct node_element *result; + + if ((result = tree_get_node_element_at(root, x, y, furniture))) + return result->parent; + return NULL; +} + + +/** + * 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 furniture whether the returned area was in an elements furniture + * \return the node at the specified position, or NULL for none + */ +struct node_element *tree_get_node_element_at(struct node *node, int x, int y, + bool *furniture) { + struct node_element *element; + + *furniture = false; + for (; node; 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; + element = element->next) { + if ((element->box.x < x) && (element->box.y < y) && + (element->box.x + element->box.width >= x) && + (element->box.y + element->box.height >= y)) + return element; + } + } else if ((node->data.box.x < x) && + (node->data.box.y < y) && + (node->data.box.x + node->data.box.width >= x) && + (node->data.box.y + node->data.box.height >= y)) + return &node->data; + if (((node->child) || (node->data.next)) && + (node->data.box.x - NODE_INSTEP + 8 < x) && + (node->data.box.y + 8 < y) && + (node->data.box.x > x) && + (node->data.box.y + 32 > y)) { + *furniture = true; + return &node->data; + } + } + + if ((node->child) && (node->expanded) && + ((element = tree_get_node_element_at(node->child, x, y, + furniture)))) + return element; + } + return NULL; +} + + +/** + * Finds a node element from a node with a specific user_type + * + * \param node the node to examine + * \param user_type the user_type to check for + * \return the corresponding element + */ +struct node_element *tree_find_element(struct node *node, int user_type) { + struct node_element *element; + for (element = &node->data; element; element = element->next) + if (element->user_type == user_type) return element; + return NULL; +} + + +/** + * Moves nodes within a tree. + * + * \param tree the tree to process + * \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 + */ +void tree_move_selected_nodes(struct tree *tree, struct node *destination, bool before) { + struct node *link; + + tree_selected_to_processing(tree->root); + if (destination->processing) + return; + if ((destination->folder) && (!destination->expanded) && (!before)) { + destination->expanded = true; + tree_handle_node_changed(tree, destination, false, true); + } + link = tree_move_processing_node(tree->root, destination, before, true); + while (link) + link = tree_move_processing_node(tree->root, link, false, false); + + tree_recalculate_node_positions(tree->root); + tree_redraw_area(tree, 0, 0, 16384, 16384); +} + + +/** + * Sets the processing flag to the selection state. + * + * \param node the node to process siblings and children of + */ +void tree_selected_to_processing(struct node *node) { + for (; node; node = node->next) { + node->processing = node->selected; + if (node->child) + tree_selected_to_processing(node->child); + } +} + + +/** + * Moves the first node in a tree with the processing flag set. + * + * \param tree 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 + */ +struct node *tree_move_processing_node(struct node *node, struct node *link, bool before, + bool first) { + struct node *result; + + bool folder = link->folder; + for (; node; node = node->next) { + if (node->processing) { + node->processing = false; + tree_delink_node(node); + if (!first) + link->folder = false; + tree_link_node(link, node, before); + if (!first) + link->folder = folder; + return node; + } + if (node->child) { + result = tree_move_processing_node(node->child, link, before, first); + if (result) + return result; + } + } + return NULL; +} + +/** + * Checks whether a node, its siblings or any children are selected. + * + * \param node the root node to check from + */ +bool tree_has_selection(struct node *node) { + for (; node; node = node->next) { + if (node->selected) + return true; + if ((node->child) && (node->expanded) && + (tree_has_selection(node->child))) + return true; + } + return false; +} + + +/** + * Updates the selected state for a region of nodes. + * + * \param tree the tree to update + * \param x the minimum x of the selection rectangle + * \param y the minimum y of the selection rectangle + * \param width the width of the selection rectangle + * \param height the height of the selection rectangle + * \param invert whether to invert the selected state + */ +void tree_handle_selection_area(struct tree *tree, int x, int y, int width, int height, + bool invert) { + assert(tree); + assert(tree->root); + + if (!tree->root->child) return; + + if (width < 0) { + x += width; + width =- width; + } + if (height < 0) { + y += height; + height =- height; + } + + tree_handle_selection_area_node(tree, tree->root->child, x, y, width, height, invert); +} + + +/** + * 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 x the minimum x of the selection rectangle + * \param y the minimum y of the selection rectangle + * \param width the width of the selection rectangle + * \param height the height of the selection rectangle + * \param invert whether to invert the selected state + */ +void tree_handle_selection_area_node(struct tree *tree, struct node *node, int x, int y, + int width, int height, bool invert) { + + struct node_element *element; + struct node *update; + int x_max, y_max; + + assert(tree); + assert(node); + + x_max = x + width; + y_max = y + height; + + for (; node; node = node->next) { + if (node->box.y > y_max) return; + if ((node->box.x < x_max) && (node->box.y < y_max) && + (node->box.x + node->box.width + NODE_INSTEP >= x) && + (node->box.y + node->box.height >= y)) { + update = NULL; + if (node->expanded) { + for (element = &node->data; element; + element = element->next) { + if ((element->box.x < x_max) && (element->box.y < y_max) && + (element->box.x + element->box.width >= x) && + (element->box.y + element->box.height >= y)) { + update = element->parent; + break; + } + } + } else if ((node->data.box.x < x_max) && + (node->data.box.y < y_max) && + (node->data.box.x + node->data.box.width >= x) && + (node->data.box.y + node->data.box.height >= y)) + update = node->data.parent; + if ((update) && (node != tree->root)) { + if (invert) { + node->selected = !node->selected; + tree_handle_node_element_changed(tree, &node->data); + } else if (!node->selected) { + node->selected = true; + tree_handle_node_element_changed(tree, &node->data); + } + } + } + if ((node->child) && (node->expanded)) + tree_handle_selection_area_node(tree, node->child, x, y, width, height, + invert); + } +} + + +/** + * Redraws a tree. + * + * \param tree the tree to draw + * \param clip_x the minimum x of the clipping rectangle + * \param clip_y the minimum y of the clipping rectangle + * \param clip_width the width of the clipping rectangle + * \param clip_height the height of the clipping rectangle + */ +void tree_draw(struct tree *tree, int clip_x, int clip_y, int clip_width, + int clip_height) { + assert(tree); + assert(tree->root); + + if (!tree->root->child) return; + + tree_initialise_redraw(tree); + tree_draw_node(tree, tree->root->child, clip_x, + clip_y, clip_width, clip_height); +} + + +/** + * Redraws a node. + * + * \param tree the tree to draw + * \param node the node to draw children and siblings of + * \param clip_x the minimum x of the clipping rectangle + * \param clip_y the minimum y of the clipping rectangle + * \param clip_width the width of the clipping rectangle + * \param clip_height the height of the clipping rectangle + */ +void tree_draw_node(struct tree *tree, struct node *node, int clip_x, int clip_y, + int clip_width, int clip_height) { + + struct node_element *element; + int x_max, y_max; + + assert(tree); + assert(node); + + x_max = clip_x + clip_width + NODE_INSTEP; + y_max = clip_y + clip_height; + + if ((node->parent->next) && (node->parent->next->box.y < clip_y)) + return; + + for (; node; node = node->next) { + if (node->box.y > y_max) return; + if (node->next) + tree_draw_line(tree, node->box.x - (NODE_INSTEP / 2), + node->box.y + (40 / 2), 0, + node->next->box.y - node->box.y); + if ((node->box.x < x_max) && (node->box.y < y_max) && + (node->box.x + node->box.width + NODE_INSTEP >= clip_x) && + (node->box.y + node->box.height >= clip_y)) { + if ((node->expanded) && (node->child)) + tree_draw_line(tree, node->box.x + (NODE_INSTEP / 2), + node->data.box.y + node->data.box.height, 0, + (40 / 2)); + tree_draw_line(tree, node->box.x - (NODE_INSTEP / 2), + node->data.box.y + + node->data.box.height - (40 / 2), + (NODE_INSTEP / 2) - 4, 0); + tree_draw_node_expansion(tree, node); + if (node->expanded) + for (element = &node->data; element; + element = element->next) + tree_draw_node_element(tree, element); + else + tree_draw_node_element(tree, &node->data); + } + if ((node->child) && (node->expanded)) + tree_draw_node(tree, node->child, clip_x, clip_y, clip_width, + clip_height); + } +} + + +/** + * 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 furniture; + + assert(tree); + assert(tree->root); + + *before = false; + if (tree->root->child) + node = tree_get_node_at(tree->root->child, x, y, &furniture); + if ((!node) || (furniture)) + return tree->root; + + if (y < (node->box.y + (node->box.height / 2))) { + *before = true; + } else if ((node->folder) && (node->expanded) && (node->child)) { + node = node->child; + *before = true; + } + return node; +} + + +/** + * Links a node into the tree. + * + * \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 + */ +void tree_link_node(struct node *link, struct node *node, bool before) { + struct node *sibling; + + assert(link); + assert(node); + + if ((!link->folder) || (before)) { + node->parent = link->parent; + if (before) { + node->next = link; + node->previous = link->previous; + if (link->previous) link->previous->next = node; + link->previous = node; + if ((link->parent) && (link->parent->child == link)) + link->parent->child = node; + } else { + node->previous = link; + node->next = link->next; + if (link->next) link->next->previous = node; + link->next = node; + } + } else { + sibling = link->child; + if (!sibling) { + link->child = node; + node->previous = NULL; + } else { + while (sibling->next) + sibling = sibling->next; + sibling->next = node; + node->previous = sibling; + } + node->parent = link; + node->next = NULL; + } +} + + +/** + * Delinks a node from the tree. + * + * \param node the node to delink + */ +void tree_delink_node(struct node *node) { + assert(node); + + if (node->parent) { + if (node->parent->child == node) + node->parent->child = node->next; + if (node->parent->child == NULL) + node->parent->expanded = false; + node->parent = NULL; + } + if (node->previous) + node->previous->next = node->next; + if (node->next) + node->next->previous = node->previous; + node->previous = NULL; + node->next = NULL; +} + + +/** + * Deletes all selected node 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; + + while (node) { + next = node->next; + if ((node->selected) && (node != tree->root)) + tree_delete_node(tree, node, false); + if (node->child) + tree_delete_selected_nodes(tree, node->child); + node = next; + } +} + + +/** + * Deletes a node from the tree. + * + * \param tree the tree to delete from + * \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) { + struct node *next; + struct node *parent; + struct node_element *element; + + assert(node); + + while (node) { + next = node->next; + if (node->child) + tree_delete_node(tree, node->child, true); + parent = node->parent; + tree_delink_node(node); + for (element = &node->data; element; element = element->next) { + if (element->text) + free(element->text); + if (element->sprite) + free(element->sprite); /* \todo platform specific bits */ + } + while (node->data.next) { + element = node->data.next->next; + free(node->data.next); + node->data.next = element; + } + free(node); + + if (!siblings) + node = NULL; + else + node = next; + } + tree_recalculate_node_positions(tree->root); + tree_redraw_area(tree, 0, 0, 16384, 16384); /* \todo correct area */ + tree_recalculate_size(tree); +} + + +/** + * Creates a folder node with the specified title, and links it into the tree. + * + * \param parent the parent node, or NULL not to link + * \param title the node title (copied) + * \return the newly created node. + */ +struct node *tree_create_folder_node(struct node *parent, const char *title) { + struct node *node; + + assert(title); + + node = calloc(sizeof(struct node), 1); + if (!node) return NULL; + node->editable = true; + node->folder = true; + node->data.parent = node; + node->data.type = NODE_ELEMENT_TEXT; + node->data.text = squash_whitespace(title); + tree_set_node_sprite_folder(node); + if (parent) + tree_link_node(parent, node, false); + tree_recalculate_node(node, true); + return node; +} + + +/** + * Creates a leaf node with the specified title, and links it into the tree. + * + * \param parent the parent node, or NULL not to link + * \param title the node title (copied) + * \return the newly created node. + */ +struct node *tree_create_leaf_node(struct node *parent, const char *title) { + struct node *node; + + assert(title); + + node = calloc(sizeof(struct node), 1); + if (!node) return NULL; + node->folder = false; + node->data.parent = node; + node->data.type = NODE_ELEMENT_TEXT; + node->data.text = squash_whitespace(title); + if (parent) + tree_link_node(parent, node, false); + return node; +} + + +/** + * Creates a tree entry for a URL, and links it into the tree + * + * \param parent the node to link to + * \param title the node title + * \param url the node URL + * \param filetype the node filetype + * \param add_date the date added + * \param last_date the last visited date + * \param visits the number of visits + * \return the node created, or NULL for failure + */ +struct node *tree_create_URL_node(struct node *parent, const char *title, + const char *url, int filetype, int add_date, int last_date, int visits) { + struct node *node; + struct node_element *element; + + assert(title); + assert(url); + + node = tree_create_leaf_node(parent, title); + if (!node) + return NULL; + node->editable = true; + + element = tree_create_node_element(node, TREE_ELEMENT_URL); + if (element) { + element->user_data = filetype; + element->type = NODE_ELEMENT_TEXT; + element->text = squash_whitespace(url); + } + element = tree_create_node_element(node, TREE_ELEMENT_ADDED); + if (element) { + element->type = NODE_ELEMENT_TEXT; + element->user_data = add_date; + } + element = tree_create_node_element(node, TREE_ELEMENT_LAST_VISIT); + if (element) { + element->type = NODE_ELEMENT_TEXT; + element->user_data = last_date; + } + element = tree_create_node_element(node, TREE_ELEMENT_VISITS); + if (element) { + element->type = NODE_ELEMENT_TEXT; + element->user_data = visits; + } + + tree_update_URL_node(node); + + node->expanded = true; + tree_recalculate_node(node, true); + node->expanded = false; + + return node; +} + + +/** + * Resets all selected URL nodes from the tree. + * + * \param tree the tree to reset from + * \param node the node to reset + * \param selected whether to only reset selected nodes + */ +void tree_reset_URL_nodes(struct tree *tree, struct node *node, bool selected) { + for (; node; node = node->next) { + if (((node->selected) || (!selected)) && (!node->folder)) + tree_reset_URL_node(tree, node); + if (node->child) + tree_reset_URL_nodes(tree, node->child, + !((node->selected) | selected)); + } +} + + +/** + * Resets a tree entry for a URL + */ +void tree_reset_URL_node(struct tree *tree, struct node *node) { + struct node_element *element; + + assert(tree); + assert(node); + + element = tree_find_element(node, TREE_ELEMENT_LAST_VISIT); + if (element) + element->user_data = -1; + element = tree_find_element(node, TREE_ELEMENT_VISITS); + if (element) + element->user_data = 0; + tree_update_URL_node(node); + tree_recalculate_node(node, true); + + if (node->expanded) + tree_redraw_area(tree, node->box.x, node->box.y + node->data.box.height, + node->box.width, node->box.height - node->data.box.height); + +} + + +/** + * Creates an empty node element and links it to a node. + * + * \param parent the parent node + * \param user_type the required user_type + * \return the newly created element. + */ +struct node_element *tree_create_node_element(struct node *parent, int user_type) { + struct node_element *element; + struct node_element *link; + + assert(parent); + + element = calloc(sizeof(struct node_element), 1); + if (!element) return NULL; + element->parent = parent; + element->user_type = user_type; + + for (link = parent->data.next; ((link) && (link->user_type < user_type)); + link = link->next); + if (link) { + element->next = link->next; + link->next = element; + } else { + for (link = &parent->data; link->next; link = link->next); + link->next = element; + } + return element; +} + + +/** + * Recalculates the size of a tree. + * + * \param tree the tree to recalculate + */ +void tree_recalculate_size(struct tree *tree) { + int width, height; + + assert(tree); + + if (!tree->handle) + return; + 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_resized(tree); +} + + +/** + * 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; node = node->next) { + if (node->selected) { + if (result) + return NULL; + result = node; + } + if ((node->child) && (node->expanded)) { + temp = tree_get_selected_node(node->child); + if (temp) { + if (result) + return NULL; + else + result = temp; + } + } + } + return result; +} |