Implement node moving drags.
This commit is contained in:
parent
e5a4c3d4c9
commit
4ab7ad6bd2
|
@ -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
|
||||
*
|
||||
|
@ -229,6 +334,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
|
||||
*
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue