netsurf/desktop/tree.c
Daniel Silverstone 6807b4208a Remove the netsurf/ from the include paths and rationalise use of <> vs "" in includes
NetSurf includes are now done with ""s and other system includes with <>s as C intended.
The scandeps tool has been updated to only look for ""ed includes, and to verify that the
files exist in the tree before adding them to the dependency lines. The depend rule has
therefore been augmented to make sure the autogenerated files are built before it is run.

This is untested under self-hosted RISC OS builds. All else tested and works.


svn path=/trunk/netsurf/; revision=3307
2007-05-30 22:39:54 +00:00

1315 lines
36 KiB
C

/*
* This file is part of NetSurf, http://netsurf-browser.org/
* 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 "content/urldb.h"
#include "desktop/tree.h"
#include "desktop/options.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/utils.h"
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,
node_element_data data);
static void tree_delete_node_internal(struct tree *tree, struct node *node, bool siblings);
static int tree_get_node_width(struct node *node);
static int tree_get_node_height(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);
void tree_clear_processing(struct node *node);
struct node *tree_move_processing_node(struct node *node, struct node *link,
bool before, bool first);
struct node *tree_create_leaf_node_shared(struct node *parent, const char *title);
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, tree->root, true);
tree_initialise_nodes(tree, tree->root);
tree_recalculate_node_positions(tree, tree->root);
tree_set_node_expanded(tree, tree->root, false);
tree->root->expanded = true;
tree_recalculate_node_positions(tree, 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 tree *tree, struct node *root) {
struct node *node;
assert(root);
tree_initialising++;
for (node = root; node; node = node->next) {
tree_recalculate_node(tree, node, true);
if (node->child) {
tree_initialise_nodes(tree, node->child);
}
}
tree_initialising--;
if (tree_initialising == 0)
tree_recalculate_node_positions(tree, 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(tree, node->child, false);
tree_set_node_selected(tree, node->child, false);
}
width = node->box.width;
height = node->box.height;
if ((recalculate_sizes) || (expansion))
tree_recalculate_node(tree, node, true);
if ((node->box.height != height) || (expansion)) {
tree_recalculate_node_positions(tree, 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(tree, element->parent, false);
tree_redraw_area(tree, 0, element->box.y, 16384, 16384);
} else {
if (element->box.width != width)
tree_recalculate_node(tree, 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 tree *tree, 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)
for (element = &node->data; element; element = element->next)
tree_recalculate_node_element(element);
else
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(tree, 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 tree *tree, 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 = tree->no_furniture ? -NODE_INSTEP + 4 : 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(tree, 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 tree *tree, struct node *node, bool expanded) {
for (; node; node = node->next) {
if (node->expanded != expanded) {
node->expanded = expanded;
tree_recalculate_node(tree, node, false);
}
if ((node->child) && (node->expanded))
tree_set_node_expanded(tree, 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(tree, node->child, false);
if ((node->data.next) && (node->data.next->box.height == 0))
tree_recalculate_node(tree, node, true);
else
tree_recalculate_node(tree, 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, 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, node_element_data data) {
struct node_element *element;
for (element = &node->data; element; element = element->next)
if (element->data == data) 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;
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; test = test->parent)
error |= test->processing;
if (error) {
tree_clear_processing(tree->root);
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_clear_processing(tree->root);
tree_recalculate_node_positions(tree, 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) && (node->expanded))
tree_selected_to_processing(node->child);
}
}
/**
* Clears the processing flag.
*
* \param node the node to process siblings and children of
*/
void tree_clear_processing(struct node *node) {
for (; node; node = node->next) {
node->processing = false;
if (node->child)
tree_clear_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->no_furniture))
tree_draw_line(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 (!tree->no_furniture) {
if ((node->expanded) && (node->child))
tree_draw_line(node->box.x + (NODE_INSTEP / 2),
node->data.box.y + node->data.box.height, 0,
(40 / 2));
if ((node->parent) && (node->parent != tree->root) &&
(node->parent->child == node))
tree_draw_line(node->parent->box.x + (NODE_INSTEP / 2),
node->parent->data.box.y +
node->parent->data.box.height, 0,
(40 / 2));
tree_draw_line(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) {
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 {
if (!link->child) {
link->child = link->last_child = node;
node->previous = NULL;
} else {
link->last_child->next = node;
node->previous = link->last_child;
link->last_child = node;
}
node->parent = link;
node->next = NULL;
}
node->deleted = false;
}
/**
* 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->last_child == node)
node->parent->last_child = node->previous;
if (node->parent->child == NULL) {
/* don't contract top-level node */
if (node->parent->parent)
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);
else 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) {
tree_delete_node_internal(tree, node, siblings);
if (tree->root)
tree_recalculate_node_positions(tree, tree->root);
tree_redraw_area(tree, 0, 0, 16384, 16384); /* \todo correct area */
tree_recalculate_size(tree);
}
/**
* 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_internal(struct tree *tree, struct node *node, bool siblings) {
struct node *next, *child;
struct node_element *e, *f, *domain, *path;
char *domain_t, *path_t, name_t;
char *space;
assert(node);
if (tree->temp_selection == node)
tree->temp_selection = NULL;
if (tree->root == node)
tree->root = NULL;
next = node->next;
tree_delink_node(node);
child = node->child;
node->child = NULL;
if (child)
tree_delete_node_internal(tree, child, true);
if (!node->retain_in_memory) {
node->retain_in_memory = true;
for (e = &node->data; e; e = f) {
if (e->text) {
/* we don't free non-editable titles or URLs */
if ((node->editable) || (node->folder))
free(e->text);
else {
/* only reset non-deleted items */
if (!node->deleted) {
if (e->data == TREE_ELEMENT_URL) {
/* reset URL characteristics */
urldb_reset_url_visit_data(e->text);
} else if (e->data == TREE_ELEMENT_NAME) {
/* get the rest of the cookie data */
domain = tree_find_element(node,
TREE_ELEMENT_DOMAIN);
path = tree_find_element(node,
TREE_ELEMENT_PATH);
if (domain && path) {
domain_t = domain->text +
strlen(messages_get(
"TreeDomain")) - 4;
space = strchr(domain_t, ' ');
if (space)
*space = '\0';
path_t = path->text +
strlen(messages_get(
"TreePath")) - 4;
space = strchr(path_t, ' ');
if (space)
*space = '\0';
name_t = e->text;
urldb_delete_cookie(
domain_t,
path_t,
e->text);
}
}
}
if (e->data != TREE_ELEMENT_TITLE &&
e->data != TREE_ELEMENT_URL) {
free(e->text);
e->text = NULL;
}
}
}
if (e->sprite) {
free(e->sprite); /* \todo platform specific bits */
e->sprite = NULL;
}
f = e->next;
if (e != &node->data)
free(e);
}
free(node);
} else {
node->deleted = true;
}
if (siblings && next)
tree_delete_node_internal(tree, next, true);
}
/**
* 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);
node->data.data = TREE_ELEMENT_TITLE;
tree_set_node_sprite_folder(node);
if (parent)
tree_link_node(parent, node, false);
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 = strdup(squash_whitespace(title));
node->data.data = TREE_ELEMENT_TITLE;
node->editable = true;
if (parent)
tree_link_node(parent, node, false);
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
* \return the newly created node.
*/
struct node *tree_create_leaf_node_shared(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 = title;
node->data.data = TREE_ELEMENT_TITLE;
node->editable = false;
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 url the URL (copied)
* \param data the URL data to use
* \param title the custom title to use
* \return the node created, or NULL for failure
*/
struct node *tree_create_URL_node(struct node *parent,
const char *url, const struct url_data *data,
const char *title) {
struct node *node;
struct node_element *element;
assert(data);
node = tree_create_leaf_node(parent, title ? title : url);
if (!node)
return NULL;
element = tree_create_node_element(node, TREE_ELEMENT_THUMBNAIL);
if (element)
element->type = NODE_ELEMENT_THUMBNAIL;
tree_create_node_element(node, TREE_ELEMENT_VISITS);
tree_create_node_element(node, TREE_ELEMENT_LAST_VISIT);
element = tree_create_node_element(node, TREE_ELEMENT_URL);
if (element)
element->text = strdup(url);
tree_update_URL_node(node, url, NULL);
return node;
}
/**
* Creates a tree entry for a URL, and links it into the tree.
*
* All information is used directly from the url_data, and as such cannot be
* edited and should never be freed.
*
* \param parent the node to link to
* \param url the URL
* \param data the URL data to use
* \return the node created, or NULL for failure
*/
struct node *tree_create_URL_node_shared(struct node *parent,
const char *url, const struct url_data *data) {
struct node *node;
struct node_element *element;
char *title;
assert(url && data);
if (data->title)
title = data->title;
else
title = url;
node = tree_create_leaf_node_shared(parent, title);
if (!node)
return NULL;
element = tree_create_node_element(node, TREE_ELEMENT_THUMBNAIL);
if (element)
element->type = NODE_ELEMENT_THUMBNAIL;
tree_create_node_element(node, TREE_ELEMENT_VISITS);
tree_create_node_element(node, TREE_ELEMENT_LAST_VISIT);
element = tree_create_node_element(node, TREE_ELEMENT_URL);
if (element)
element->text = url;
tree_update_URL_node(node, url, data);
return node;
}
/**
* Creates a tree entry for a cookie, and links it into the tree.
*
* All information is copied from the cookie_data, and as such can
* be edited and should be freed.
*
* \param parent the node to link to
* \param url the URL
* \param data the cookie data to use
* \return the node created, or NULL for failure
*/
struct node *tree_create_cookie_node(struct node *parent,
const struct cookie_data *data) {
struct node *node;
struct node_element *element;
char buffer[256];
char buffer2[16];
node = tree_create_leaf_node(parent, data->name);
if (!node)
return NULL;
node->data.data = TREE_ELEMENT_NAME;
node->editable = false;
element = tree_create_node_element(node, TREE_ELEMENT_PERSISTENT);
if (element) {
snprintf(buffer, 256, messages_get("TreePersistent"),
data->no_destroy ? messages_get("Yes") : messages_get("No"));
element->text = strdup(buffer);
}
element = tree_create_node_element(node, TREE_ELEMENT_VERSION);
if (element) {
snprintf(buffer2, 16, "TreeVersion%i", data->version);
snprintf(buffer, 256, messages_get("TreeVersion"), messages_get(buffer2));
element->text = strdup(buffer);
}
element = tree_create_node_element(node, TREE_ELEMENT_SECURE);
if (element) {
snprintf(buffer, 256, messages_get("TreeSecure"),
data->secure ? messages_get("Yes") : messages_get("No"));
element->text = strdup(buffer);
}
element = tree_create_node_element(node, TREE_ELEMENT_LAST_USED);
if (element) {
snprintf(buffer, 256, messages_get("TreeLastUsed"),
(data->last_used > 0) ?
ctime(&data->last_used) : messages_get("TreeUnknown"));
if (data->last_used > 0)
buffer[strlen(buffer) - 1] = '\0';
element->text = strdup(buffer);
}
element = tree_create_node_element(node, TREE_ELEMENT_EXPIRES);
if (element) {
snprintf(buffer, 256, messages_get("TreeExpires"),
(data->expires > 0)
? (data->expires == 1)
? messages_get("TreeSession")
: ctime(&data->expires)
: messages_get("TreeUnknown"));
if (data->expires > 0 && data->expires != 1)
buffer[strlen(buffer) - 1] = '\0';
element->text = strdup(buffer);
}
element = tree_create_node_element(node, TREE_ELEMENT_PATH);
if (element) {
snprintf(buffer, 256, messages_get("TreePath"), data->path,
data->path_from_set ? messages_get("TreeHeaders") : "");
element->text = strdup(buffer);
}
element = tree_create_node_element(node, TREE_ELEMENT_DOMAIN);
if (element) {
snprintf(buffer, 256, messages_get("TreeDomain"), data->domain,
data->domain_from_set ? messages_get("TreeHeaders") : "");
element->text = strdup(buffer);
}
if ((data->comment) && (strcmp(data->comment, ""))) {
element = tree_create_node_element(node, TREE_ELEMENT_COMMENT);
if (element) {
snprintf(buffer, 256, messages_get("TreeComment"), data->comment);
element->text = strdup(buffer);
}
}
element = tree_create_node_element(node, TREE_ELEMENT_VALUE);
if (element) {
snprintf(buffer, 256, messages_get("TreeValue"),
data->value ? data->value : messages_get("TreeUnused"));
element->text = strdup(buffer);
}
tree_set_node_sprite(node, "small_xxx", "small_xxx");
return node;
}
/**
* Creates an empty text 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, node_element_data data) {
struct node_element *element;
element = calloc(sizeof(struct node_element), 1);
if (!element) return NULL;
element->parent = parent;
element->data = data;
element->type = NODE_ELEMENT_TEXT;
element->next = parent->data.next;
parent->data.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;
if (tree->root) {
tree->width = tree_get_node_width(tree->root);
tree->height = tree_get_node_height(tree->root);
} else {
tree->width = 0;
tree->height = 0;
}
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;
}