summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2013-08-17 19:39:17 +0100
committerMichael Drake <tlsa@netsurf-browser.org>2013-08-17 19:39:17 +0100
commit4ab7ad6bd21014f692503f2416782b533f5d2062 (patch)
tree0cd0d7eefadcc3a82ca2aed90d68aba939e1837f
parente5a4c3d4c9e7996225682f66121e1f1b93fce6c9 (diff)
downloadnetsurf-4ab7ad6bd21014f692503f2416782b533f5d2062.tar.gz
netsurf-4ab7ad6bd21014f692503f2416782b533f5d2062.tar.bz2
Implement node moving drags.
-rw-r--r--desktop/treeview.c438
1 files changed, 308 insertions, 130 deletions
diff --git a/desktop/treeview.c b/desktop/treeview.c
index 8f26b32aa..2db90cd4e 100644
--- a/desktop/treeview.c
+++ b/desktop/treeview.c
@@ -117,6 +117,7 @@ struct treeview_drag {
}; /**< Drag state */
struct treeview_move {
+ treeview_node *root; /** Head of yanked node list */
treeview_node *target; /**< Move target */
struct rect target_area; /**< Pos/size of target indicator */
enum treeview_target_pos target_pos; /**< Pos wrt render node */
@@ -189,6 +190,110 @@ static struct treeview_text treeview_furn[TREE_FURN_LAST] = {
};
+/* Walk a treeview subtree, calling a callback at each node (depth first)
+ *
+ * \param root Root to walk tree from (doesn't get a callback call)
+ * \param full Iff true, visit children of collapsed nodes
+ * \param callback_bwd Function to call on each node in backwards order
+ * \param callback_fwd Function to call on each node in forwards order
+ * \param ctx Context to pass to callback
+ * \return NSERROR_OK on success, or appropriate error otherwise
+ *
+ * Note: Any node deletion must happen in callback_bwd.
+ */
+static nserror treeview_walk_internal(treeview_node *root, bool full,
+ nserror (*callback_bwd)(treeview_node *n, void *ctx, bool *end),
+ nserror (*callback_fwd)(treeview_node *n, void *ctx,
+ bool *skip_children, bool *end),
+ void *ctx)
+{
+ treeview_node *node, *child, *parent, *next_sibling;
+ bool abort = false;
+ bool skip_children = false;
+ nserror err;
+
+ node = root;
+ parent = node->parent;
+ next_sibling = node->next_sib;
+ child = (!skip_children &&
+ (full || (node->flags & TREE_NODE_EXPANDED))) ?
+ node->children : NULL;
+
+ while (node != NULL) {
+
+ if (child != NULL) {
+ /* Down to children */
+ node = child;
+ } else {
+ /* No children. As long as we're not at the root,
+ * go to next sibling if present, or nearest ancestor
+ * with a next sibling. */
+
+ while (node != root &&
+ next_sibling == NULL) {
+ if (callback_bwd != NULL) {
+ /* Backwards callback */
+ err = callback_bwd(node, ctx, &abort);
+
+ if (err != NSERROR_OK) {
+ return err;
+
+ } else if (abort) {
+ /* callback requested early
+ * termination */
+ return NSERROR_OK;
+ }
+ }
+ node = parent;
+ parent = node->parent;
+ next_sibling = node->next_sib;
+ }
+
+ if (node == root)
+ break;
+
+ if (callback_bwd != NULL) {
+ /* Backwards callback */
+ err = callback_bwd(node, ctx, &abort);
+
+ if (err != NSERROR_OK) {
+ return err;
+
+ } else if (abort) {
+ /* callback requested early
+ * termination */
+ return NSERROR_OK;
+ }
+ }
+ node = next_sibling;
+ }
+
+ assert(node != NULL);
+ assert(node != root);
+
+ parent = node->parent;
+ next_sibling = node->next_sib;
+ child = (full || (node->flags & TREE_NODE_EXPANDED)) ?
+ node->children : NULL;
+
+ if (callback_fwd != NULL) {
+ /* Forwards callback */
+ err = callback_fwd(node, ctx, &skip_children, &abort);
+
+ if (err != NSERROR_OK) {
+ return err;
+
+ } else if (abort) {
+ /* callback requested early termination */
+ return NSERROR_OK;
+ }
+ }
+ child = skip_children ? NULL : child;
+ }
+ return NSERROR_OK;
+}
+
+
/**
* Create treeview's root node
*
@@ -230,6 +335,18 @@ static nserror treeview_create_node_root(treeview_node **root)
/**
+ * Set a node's inset from its parent (can be used as treeview walk callback)
+ */
+static nserror treeview_set_inset_from_parent(treeview_node *n, void *ctx,
+ bool *skip_children, bool *end)
+{
+ if (n->parent != NULL)
+ n->inset = n->parent->inset + tree_g.step_width;
+
+ *skip_children = false;
+ return NSERROR_OK;
+}
+/**
* Insert a treeview node into a treeview
*
* \param a parentless node to insert
@@ -272,6 +389,10 @@ static inline void treeview_insert_node(treeview_node *a,
assert(a->parent != NULL);
a->inset = a->parent->inset + tree_g.step_width;
+ if (a->children != NULL) {
+ treeview_walk_internal(a, true, NULL,
+ treeview_set_inset_from_parent, NULL);
+ }
if (a->parent->flags & TREE_NODE_EXPANDED) {
/* Parent is expanded, so inserted node will be visible and
@@ -544,110 +665,6 @@ static int treeview_node_y(treeview *tree, treeview_node *node)
}
-/* Walk a treeview subtree, calling a callback at each node (depth first)
- *
- * \param root Root to walk tree from (doesn't get a callback call)
- * \param full Iff true, visit children of collapsed nodes
- * \param callback_bwd Function to call on each node in backwards order
- * \param callback_fwd Function to call on each node in forwards order
- * \param ctx Context to pass to callback
- * \return NSERROR_OK on success, or appropriate error otherwise
- *
- * Note: Any node deletion must happen in callback_bwd.
- */
-static nserror treeview_walk_internal(treeview_node *root, bool full,
- nserror (*callback_bwd)(treeview_node *n, void *ctx, bool *end),
- nserror (*callback_fwd)(treeview_node *n, void *ctx,
- bool *skip_children, bool *end),
- void *ctx)
-{
- treeview_node *node, *child, *parent, *next_sibling;
- bool abort = false;
- bool skip_children = false;
- nserror err;
-
- node = root;
- parent = node->parent;
- next_sibling = node->next_sib;
- child = (!skip_children &&
- (full || (node->flags & TREE_NODE_EXPANDED))) ?
- node->children : NULL;
-
- while (node != NULL) {
-
- if (child != NULL) {
- /* Down to children */
- node = child;
- } else {
- /* No children. As long as we're not at the root,
- * go to next sibling if present, or nearest ancestor
- * with a next sibling. */
-
- while (node != root &&
- next_sibling == NULL) {
- if (callback_bwd != NULL) {
- /* Backwards callback */
- err = callback_bwd(node, ctx, &abort);
-
- if (err != NSERROR_OK) {
- return err;
-
- } else if (abort) {
- /* callback requested early
- * termination */
- return NSERROR_OK;
- }
- }
- node = parent;
- parent = node->parent;
- next_sibling = node->next_sib;
- }
-
- if (node == root)
- break;
-
- if (callback_bwd != NULL) {
- /* Backwards callback */
- err = callback_bwd(node, ctx, &abort);
-
- if (err != NSERROR_OK) {
- return err;
-
- } else if (abort) {
- /* callback requested early
- * termination */
- return NSERROR_OK;
- }
- }
- node = next_sibling;
- }
-
- assert(node != NULL);
- assert(node != root);
-
- parent = node->parent;
- next_sibling = node->next_sib;
- child = (full || (node->flags & TREE_NODE_EXPANDED)) ?
- node->children : NULL;
-
- if (callback_fwd != NULL) {
- /* Forwards callback */
- err = callback_fwd(node, ctx, &skip_children, &abort);
-
- if (err != NSERROR_OK) {
- return err;
-
- } else if (abort) {
- /* callback requested early termination */
- return NSERROR_OK;
- }
- }
- child = skip_children ? NULL : child;
- }
- return NSERROR_OK;
-}
-
-
struct treeview_walk_ctx {
treeview_walk_callback walk_cb;
void *ctx;
@@ -686,22 +703,14 @@ nserror treeview_walk(treeview *tree, treeview_node *root,
}
-struct treeview_node_delete {
- treeview *tree;
- int height_reduction;
- bool user_interaction;
-};
-/** Treewalk node callback deleting nodes. */
-static nserror treeview_delete_node_walk_cb(treeview_node *n,
- void *ctx, bool *end)
+/**
+ * Unlink a treeview node
+ *
+ * \param n Node to unlink
+ * \return true iff ancestor heights need to be reduced
+ */
+static inline bool treeview_unlink_node(treeview_node *n)
{
- struct treeview_node_delete *nd = (struct treeview_node_delete *)ctx;
- struct treeview_node_msg msg;
- msg.msg = TREE_MSG_NODE_DELETE;
- msg.data.delete.user = nd->user_interaction;
-
- assert(n->children == NULL);
-
/* Unlink node from tree */
if (n->parent != NULL && n->parent->children == n) {
/* Node is a first child */
@@ -719,11 +728,33 @@ static nserror treeview_delete_node_walk_cb(treeview_node *n,
/* Reduce ancestor heights */
if (n->parent != NULL && n->parent->flags & TREE_NODE_EXPANDED) {
- int height = (n->type == TREE_NODE_ENTRY) ? n->height :
- tree_g.line_height;
- nd->height_reduction += height;
+ return true;
}
+ return false;
+}
+
+
+struct treeview_node_delete {
+ treeview *tree;
+ int height_reduction;
+ bool user_interaction;
+};
+/** Treewalk node callback deleting nodes. */
+static nserror treeview_delete_node_walk_cb(treeview_node *n,
+ void *ctx, bool *end)
+{
+ struct treeview_node_delete *nd = (struct treeview_node_delete *)ctx;
+ struct treeview_node_msg msg;
+ msg.msg = TREE_MSG_NODE_DELETE;
+ msg.data.delete.user = nd->user_interaction;
+
+ assert(n->children == NULL);
+
+ if (treeview_unlink_node(n))
+ nd->height_reduction += (n->type == TREE_NODE_ENTRY) ?
+ n->height : tree_g.line_height;
+
/* Handle any special treatment */
switch (n->type) {
case TREE_NODE_ENTRY:
@@ -975,6 +1006,7 @@ nserror treeview_create(treeview **tree,
(*tree)->drag.prev.node_y = 0;
(*tree)->drag.prev.node_h = 0;
+ (*tree)->move.root = NULL;
(*tree)->move.target = NULL;
(*tree)->move.target_pos = TV_TARGET_NONE;
@@ -1391,7 +1423,8 @@ struct treeview_selection_walk_data {
TREEVIEW_WALK_SELECT_ALL,
TREEVIEW_WALK_COMMIT_SELECT_DRAG,
TREEVIEW_WALK_DELETE_SELECTION,
- TREEVIEW_WALK_PROPAGATE_SELECTION
+ TREEVIEW_WALK_PROPAGATE_SELECTION,
+ TREEVIEW_WALK_YANK_SELECTION
} purpose;
union {
bool has_selection;
@@ -1403,6 +1436,9 @@ struct treeview_selection_walk_data {
int sel_min;
int sel_max;
} drag;
+ struct {
+ treeview_node *prev;
+ } yank;
} data;
int current_y;
treeview *tree;
@@ -1469,6 +1505,36 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n,
n->flags ^= TREE_NODE_SELECTED;
}
return NSERROR_OK;
+
+ case TREEVIEW_WALK_YANK_SELECTION:
+ if (n->flags & TREE_NODE_SELECTED) {
+ treeview_node *p = n->parent;
+ int h = 0;
+
+ if (treeview_unlink_node(n))
+ h = n->height;
+
+ /* Reduce ancestor heights */
+ while (p != NULL && p->flags & TREE_NODE_EXPANDED) {
+ p->height -= h;
+ p = p->parent;
+ }
+ if (sw->data.yank.prev == NULL) {
+ sw->tree->move.root = n;
+ n->parent = NULL;
+ n->prev_sib = NULL;
+ n->next_sib = NULL;
+ } else {
+ n->parent = NULL;
+ n->prev_sib = sw->data.yank.prev;
+ n->next_sib = NULL;
+ sw->data.yank.prev->next_sib = n;
+ }
+ sw->data.yank.prev = n;
+
+ *skip_children = true;
+ }
+ break;
}
if (changed) {
@@ -1571,14 +1637,20 @@ static void treeview_commit_selection_drag(treeview *tree)
/**
- * Move a selection according to the current move drag.
+ * Yank a selection to the node move list.
*
- * \param tree Treeview object to move selected nodes in
+ * \param tree Treeview object to yank selection from
*/
-static nserror treeview_move_selection(treeview *tree)
+static void treeview_move_yank_selection(treeview *tree)
{
- /* TODO */
- return NSERROR_OK;
+ struct treeview_selection_walk_data sw;
+
+ sw.purpose = TREEVIEW_WALK_YANK_SELECTION;
+ sw.data.yank.prev = NULL;
+ sw.tree = tree;
+
+ treeview_walk_internal(tree->root, false, NULL,
+ treeview_node_selection_walk_cb, &sw);
}
@@ -1646,6 +1718,111 @@ static bool treeview_propagate_selection(treeview *tree, struct rect *rect)
}
+/**
+ * Move a selection according to the current move drag.
+ *
+ * \param tree Treeview object to move selected nodes in
+ * \param rect Redraw rectangle
+ */
+static nserror treeview_move_selection(treeview *tree, struct rect *rect)
+{
+ treeview_node *node, *next, *parent;
+ treeview_node *relation;
+ enum treeview_relationship relationship;
+ int height;
+
+ assert(tree != NULL);
+ assert(tree->root != NULL);
+ assert(tree->root->children != NULL);
+ assert(tree->move.target_pos != TV_TARGET_NONE);
+
+ height = tree->root->height;
+
+ /* Identify target location */
+ switch (tree->move.target_pos) {
+ case TV_TARGET_ABOVE:
+ if (tree->move.target == NULL) {
+ /* Target: After last child of root */
+ relation = tree->root->children;
+ while (relation->next_sib != NULL) {
+ relation = relation->next_sib;
+ }
+ relationship = TREE_REL_NEXT_SIBLING;
+
+ } else if (tree->move.target->prev_sib != NULL) {
+ /* Target: After previous sibling */
+ relation = tree->move.target->prev_sib;
+ relationship = TREE_REL_NEXT_SIBLING;
+
+ } else {
+ /* Target: Target: First child of parent */
+ assert(tree->move.target->parent != NULL);
+ relation = tree->move.target->parent;
+ relationship = TREE_REL_FIRST_CHILD;
+ }
+ break;
+
+ case TV_TARGET_INSIDE:
+ assert(tree->move.target != NULL);
+ relation = tree->move.target;
+ relationship = TREE_REL_FIRST_CHILD;
+ break;
+
+ case TV_TARGET_BELOW:
+ assert(tree->move.target != NULL);
+ relation = tree->move.target;
+ relationship = TREE_REL_NEXT_SIBLING;
+ break;
+
+ default:
+ LOG(("Bad drop target for move."));
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ if (relationship == TREE_REL_FIRST_CHILD) {
+ parent = relation;
+ } else {
+ parent = relation->parent;
+ }
+
+ /* The node that we're moving selection to can't itself be selected */
+ assert(!(relation->flags & TREE_NODE_SELECTED));
+
+ /* Move all selected nodes from treeview to tree->move.root */
+ treeview_move_yank_selection(tree);
+
+ /* Move all nodes on tree->move.root to target location */
+ next = node->next_sib;
+ for (node = tree->move.root; node != NULL; node = next) {
+ next = node->next_sib;
+
+ if (!(parent->flags & TREE_NODE_EXPANDED)) {
+ if (node->flags & TREE_NODE_EXPANDED)
+ treeview_node_contract(tree, node);
+ node->flags &= ~TREE_NODE_SELECTED;
+ }
+
+ treeview_insert_node(node, relation, relationship);
+
+ relation = node;
+ relationship = TREE_REL_NEXT_SIBLING;
+ }
+ tree->move.root = NULL;
+
+ /* Tell window, if height has changed */
+ if (height != tree->root->height)
+ tree->cw_t->update_size(tree->cw_h, -1, tree->root->height);
+
+ /* TODO: Deal with redraw area properly */
+ rect->x0 = 0;
+ rect->y0 = 0;
+ rect->x1 = REDRAW_MAX;
+ rect->y1 = REDRAW_MAX;
+
+ return NSERROR_OK;
+}
+
+
struct treeview_launch_walk_data {
int selected_depth;
treeview *tree;
@@ -2291,6 +2468,7 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx,
void treeview_mouse_action(treeview *tree,
browser_mouse_state mouse, int x, int y)
{
+ struct rect r;
bool redraw = false;
assert(tree != NULL);
@@ -2308,7 +2486,7 @@ void treeview_mouse_action(treeview *tree,
CORE_WINDOW_DRAG_NONE);
return;
case TV_DRAG_MOVE:
- treeview_move_selection(tree);
+ treeview_move_selection(tree, &r);
tree->drag.type = TV_DRAG_NONE;
tree->drag.start_node = NULL;
@@ -2317,6 +2495,7 @@ void treeview_mouse_action(treeview *tree,
tree->cw_t->drag_status(tree->cw_h,
CORE_WINDOW_DRAG_NONE);
+ tree->cw_t->redraw_request(tree->cw_h, r);
return;
default:
/* No drag to end */
@@ -2326,7 +2505,6 @@ void treeview_mouse_action(treeview *tree,
if (y > tree->root->height) {
/* Below tree */
- struct rect r;
r.x0 = 0;
r.x1 = REDRAW_MAX;