diff --git a/FL/Fl_Tree_Item.H b/FL/Fl_Tree_Item.H
index 4cd9027ca..7941fa379 100644
--- a/FL/Fl_Tree_Item.H
+++ b/FL/Fl_Tree_Item.H
@@ -271,6 +271,13 @@ public:
#endif
Fl_Tree_Item *insert(const Fl_Tree_Prefs &prefs, const char *new_label, int pos=0);
Fl_Tree_Item *insert_above(const Fl_Tree_Prefs &prefs, const char *new_label);
+ Fl_Tree_Item* deparent(int index);
+ int reparent(Fl_Tree_Item *newchild, int index);
+ int move(int to, int from);
+ int move(Fl_Tree_Item *item, int op=0, int pos=0);
+ int move_above(Fl_Tree_Item *item);
+ int move_below(Fl_Tree_Item *item);
+ int move_into(Fl_Tree_Item *item, int pos=0);
int depth() const;
Fl_Tree_Item *prev();
Fl_Tree_Item *next();
diff --git a/FL/Fl_Tree_Item_Array.H b/FL/Fl_Tree_Item_Array.H
index ff3eb0ea5..f876458cb 100644
--- a/FL/Fl_Tree_Item_Array.H
+++ b/FL/Fl_Tree_Item_Array.H
@@ -84,6 +84,9 @@ public:
_items[bx] = asave;
}
#endif /*FLTK_ABI_VERSION*/
+ int move(int to, int from);
+ int deparent(int pos);
+ int reparent(Fl_Tree_Item *item, Fl_Tree_Item *newparent, int pos);
void clear();
void add(Fl_Tree_Item *val);
void insert(int pos, Fl_Tree_Item *new_item);
diff --git a/src/Fl_Tree_Item.cxx b/src/Fl_Tree_Item.cxx
index 74c68438b..a5ec92d27 100644
--- a/src/Fl_Tree_Item.cxx
+++ b/src/Fl_Tree_Item.cxx
@@ -453,6 +453,142 @@ Fl_Tree_Item *Fl_Tree_Item::insert_above(const Fl_Tree_Prefs &prefs, const char
return(0);
}
+/// Deparent child at index position \p 'pos'.
+/// This creates an "orphaned" item that is still allocated,
+/// but has no parent or siblings. Normally the caller would
+/// want to immediately reparent the orphan elsewhere.
+///
+/// A successfully orphaned item will have its parent()
+/// and prev_sibling()/next_sibling() set to NULL.
+///
+/// \returns
+/// - pointer to orphaned item on success
+/// - NULL on error (could not deparent the item)
+///
+Fl_Tree_Item* Fl_Tree_Item::deparent(int pos) {
+ Fl_Tree_Item *orphan = _children[pos];
+ if ( _children.deparent(pos) < 0 ) return NULL;
+ return orphan;
+}
+
+/// Reparent specified item as a child of ourself at position \p 'pos'.
+/// Typically 'newchild' was recently orphaned with deparent().
+///
+/// \returns
+/// - 0: on success
+/// - -1: on error (e.g. if \p 'pos' out of range)
+///
+int Fl_Tree_Item::reparent(Fl_Tree_Item *newchild, int pos) {
+ int ret;
+ if ( (ret = _children.reparent(newchild, this, pos)) < 0 ) return ret;
+ newchild->parent(this); // take custody
+ return 0;
+}
+
+/// Move the item 'to' to sibling position of 'from'.
+///
+/// \returns
+/// - 0: Success
+/// - -1: range error (e.g. if \p 'to' or \p 'from' out of range).
+/// - (Other return values reserved for future use)
+///
+int Fl_Tree_Item::move(int to, int from) {
+ return _children.move(to, from);
+}
+
+/// Move the current item above/below/into the specified 'item',
+/// where \p 'op' determines the type of move:
+///
+/// - 0: move above \p 'item' (\p 'pos' ignored)
+/// - 1: move below \p 'item' (\p 'pos' ignored)
+/// - 2: move into \p 'item' as a child (at optional position \p 'pos')
+///
+/// \returns 0 on success. a negative number on error:
+/// - -1: one of the items has no parent
+/// - -2: item's index could not be determined
+/// - -3: bad 'op'
+/// - -4: index range error
+/// - -5: could not deparent
+/// - -6: could not reparent at \p 'pos'
+/// - (Other return values reserved for future use.)
+///
+int Fl_Tree_Item::move(Fl_Tree_Item *item, int op, int pos) {
+ Fl_Tree_Item *from_parent, *to_parent;
+ int from, to;
+ switch (op) {
+ case 0: // "above"
+ from_parent = this->parent();
+ to_parent = item->parent();
+ from = from_parent->find_child(this);
+ to = to_parent->find_child(item);
+ break;
+ case 1: // "below"
+ from_parent = this->parent();
+ to_parent = item->parent();
+ from = from_parent->find_child(this);
+ to = to_parent->find_child(item);
+ break;
+ case 2: // "into"
+ from_parent = this->parent();
+ to_parent = item;
+ from = from_parent->find_child(this);
+ to = pos;
+ break;
+ default:
+ return -3;
+ }
+ if ( !from_parent || !to_parent ) return -1;
+ if ( from < 0 || to < 0 ) return -2;
+ if ( op == 1 ) to++; // 'below'? apply +1 offset for 'to'
+ if ( from_parent == to_parent ) { // same parent?
+ if ( from_parent->move(to, from) < 0 ) // simple move among siblings
+ return -4;
+ } else { // different parent?
+ if ( to > to_parent->children() ) // try to prevent a reparent() error
+ return -4;
+ if ( from_parent->deparent(from) < 0 ) // deparent self from current parent
+ return -5;
+ if ( to_parent->reparent(this, to) < 0 ) { // reparent self to new parent at position 'to'
+ to_parent->reparent(this, 0); // failed? shouldn't happen, reparent at 0
+ return -6;
+ }
+ }
+ return 0;
+}
+
+/// Move the current item above the specified 'item'.
+/// This is the equivalent of calling move(item,0,0).
+///
+/// \returns 0 on success.
+/// On error returns a negative value;
+/// see move(Fl_Tree_Item*,int,int) for possible error codes.
+///
+int Fl_Tree_Item::move_above(Fl_Tree_Item *item) {
+ return move(item, 0, 0);
+}
+
+/// Move the current item below the specified 'item'.
+/// This is the equivalent of calling move(item,1,0).
+///
+/// \returns 0 on success.
+/// On error returns a negative value;
+/// see move(Fl_Tree_Item*,int,int) for possible error codes.
+///
+int Fl_Tree_Item::move_below(Fl_Tree_Item *item) {
+ return move(item, 1, 0);
+}
+
+/// Parent the current item as a child of the specified \p 'item'.
+/// This is the equivalent of calling move(item,2,pos).
+///
+/// \returns 0 on success.
+/// On error returns a negative value;
+/// see move(Fl_Tree_Item*,int,int) for possible error codes.
+///
+int Fl_Tree_Item::move_into(Fl_Tree_Item *item, int pos) {
+ return move(item, 2, pos);
+}
+
#if FLTK_ABI_VERSION >= 10303
/// Return the parent tree's prefs.
/// \returns a reference to the parent tree's Fl_Tree_Prefs
@@ -1519,11 +1655,18 @@ Fl_Tree_Item *Fl_Tree_Item::prev_sibling() {
/// Update our _prev_sibling and _next_sibling pointers to point to neighbors
/// given \p index as being our current position in the parent's item array.
/// Call this whenever items in the array are added/removed/moved/swapped/etc.
-/// \param[in] index Our index# in the parent
+/// \param[in] index Our index# in the parent.
+/// Special case if index=-1: become an orphan; null out all parent/sibling associations.
///
void Fl_Tree_Item::update_prev_next(int index) {
#if FLTK_ABI_VERSION >= 10301
// NEW
+ if ( index == -1 ) { // special case: become an orphan
+ _parent = 0;
+ _prev_sibling = 0;
+ _next_sibling = 0;
+ return;
+ }
int pchildren = parent() ? parent()->children() : 0;
int index_prev = index-1;
int index_next = index+1;
diff --git a/src/Fl_Tree_Item_Array.cxx b/src/Fl_Tree_Item_Array.cxx
index d83b0be12..2610bb2a6 100644
--- a/src/Fl_Tree_Item_Array.cxx
+++ b/src/Fl_Tree_Item_Array.cxx
@@ -233,6 +233,78 @@ void Fl_Tree_Item_Array::swap(int ax, int bx) {
}
#endif /* FLTK_ABI_VERSION */
+/// Move item at 'from' to new position 'to' in the array.
+///
+/// \returns 0 on success, -1 on range error (e.g. if \p 'to' or \p 'from' out of range)
+///
+int Fl_Tree_Item_Array::move(int to, int from) {
+ if ( from == to ) return 0; // nop
+ if ( to<0 || to>=_total || from<0 || from>=_total ) return -1;
+ Fl_Tree_Item *item = _items[from];
+ Fl_Tree_Item *prev = item->prev_sibling();
+ Fl_Tree_Item *next = item->next_sibling();
+ // Remove item..
+ if ( from < to )
+ for ( int t=from; tto; t-- )
+ _items[t] = _items[t-1];
+ // Move to new position
+ _items[to] = item;
+ // Adjust for new siblings
+ _items[to]->update_prev_next(to);
+ _items[from]->update_prev_next(from);
+ // Adjust old siblings
+ if ( prev ) prev->update_prev_next(from-1);
+ if ( next ) next->update_prev_next(from);
+ return 0;
+}
+
+/// Deparent item at \p 'pos' from our list of children.
+/// Similar to a remove() without the destruction of the item.
+/// This creates an orphaned item (still allocated, has no parent)
+/// which soon after is typically reparented elsewhere.
+///
+/// \returns 0 on success, -1 on error (e.g. if \p 'pos' out of range)
+///
+int Fl_Tree_Item_Array::deparent(int pos) {
+ if ( pos>=_total || pos<0 ) return -1;
+ // Save item being deparented, and its two nearest siblings
+ Fl_Tree_Item *item = _items[pos];
+ Fl_Tree_Item *prev = item->prev_sibling();
+ Fl_Tree_Item *next = item->next_sibling();
+ // Remove from parent's list of children
+ _total -= 1;
+ for ( int t=pos; t<_total; t++ )
+ _items[t] = _items[t+1]; // delete, no destroy
+ // Now an orphan: remove association with old parent and siblings
+ item->update_prev_next(-1); // become an orphan
+ // Adjust bereaved siblings
+ if ( prev ) prev->update_prev_next(pos-1);
+ if ( next ) next->update_prev_next(pos);
+ return 0;
+}
+
+/// Reparent specified item as a child of ourself.
+/// Typically 'newchild' was recently orphaned with deparent().
+///
+/// \returns 0 on success, -1 on error (e.g. if \p 'pos' out of range)
+///
+int Fl_Tree_Item_Array::reparent(Fl_Tree_Item *item, Fl_Tree_Item* newparent, int pos) {
+ if ( pos<0 || pos>_total ) return -1;
+ // Add item to new parent
+ enlarge(1);
+ _total += 1;
+ for ( int t=_total-1; t>pos; --t ) // shuffle array to make room for new entry
+ _items[t] = _items[t-1];
+ _items[pos] = item; // insert new entry
+ // Attach to new parent and siblings
+ _items[pos]->parent(newparent); // reparent (update_prev_next() needs this)
+ _items[pos]->update_prev_next(pos); // find new siblings
+ return 0;
+}
+
//
// End of "$Id$".
//