diff options
| author | Matthias Melcher <github@matthiasm.com> | 2024-09-14 01:10:35 +0200 |
|---|---|---|
| committer | Matthias Melcher <github@matthiasm.com> | 2024-09-14 01:10:41 +0200 |
| commit | e7f1247552fbb9d056d4c68eb51564a504df3161 (patch) | |
| tree | cbaa15d7d58359d0fd59e86711881179ad34e134 /fluid/Fl_Type.cxx | |
| parent | bb917628ffa8ffc6cafc79da80344c8c9c653969 (diff) | |
FLUID: type node placement in scene graph revised
- fixes copy/paste operation that would place pasted types wrong
- improves paste into folded and unfolded groups
- improves duplication of multiple types
- much improved placement of types that don;t fit at the
requested position
- some more testing will follow in the next days
Diffstat (limited to 'fluid/Fl_Type.cxx')
| -rw-r--r-- | fluid/Fl_Type.cxx | 278 |
1 files changed, 227 insertions, 51 deletions
diff --git a/fluid/Fl_Type.cxx b/fluid/Fl_Type.cxx index c4127967b..5847fbddc 100644 --- a/fluid/Fl_Type.cxx +++ b/fluid/Fl_Type.cxx @@ -130,6 +130,143 @@ Fl_Type *in_this_only; // set if menu popped-up in window // ---- various functions +#if 0 +#ifndef NDEBUG +/** + Print the current project tree to stderr. + */ +void print_project_tree() { + fprintf(stderr, "---- %s --->\n", g_project.projectfile_name().c_str()); + for (Fl_Type *t = Fl_Type::first; t; t = t->next) { + for (int i = t->level; i > 0; i--) + fprintf(stderr, ". "); + fprintf(stderr, "%s\n", subclassname(t)); + } +} +#endif + +#ifndef NDEBUG +/** + Check the validity of the project tree. + + Write problems with the project tree to stderr. + + \return true if the project tree is valid + */ +bool validate_project_tree() { + // Validate `first` and `last` + if (Fl_Type::first == NULL) { + if (Fl_Type::last == NULL) { + return true; + } else { + fprintf(stderr, "ERROR: `first` is NULL, but `last` is not!\n"); + return false; + } + } + if (Fl_Type::last == NULL) { + fprintf(stderr, "ERROR: `last` is NULL, but `first` is not!\n"); + return false; + } + // Validate the branch linkage, parent links, etc. + return validate_branch(Fl_Type::first); +} +#endif + +#ifndef NDEBUG +/** + Check the validity of a Type branch that is not connected to the project. + + Write problems with the branch to stderr. + + \param[in] root the first node in a branch + \return true if the branch is correctly separated and valid + */ +bool validate_independent_branch(class Fl_Type *root) { + // Make sure that `first` and `last` do not point at any node in this branch + if (Fl_Type::first) { + for (Fl_Type *t = root; t; t = t->next) { + if (Fl_Type::first == t) { + fprintf(stderr, "ERROR: Branch is not independent, `first` is pointing to branch member!\n"); + return false; + } + } + } + if (Fl_Type::last) { + for (Fl_Type *t = root; t; t = t->next) { + if (Fl_Type::last == t) { + fprintf(stderr, "ERROR: Branch is not independent, `last` is pointing to branch member!\n"); + return false; + } + } + } + // Validate the branch linkage, parent links, etc. + return validate_branch(root); +} +#endif + +#ifndef NDEBUG +/** + Check the validity of a Type branch. + + Write problems with the branch to stderr. + + \param[in] root the first node in a branch + \return true if the branch is valid + */ +bool validate_branch(class Fl_Type *root) { + // Only check real branches + if (!root) { + fprintf(stderr, "WARNING: Branch is empty!\n"); + return false; + } + // Check relation between this and next node + for (Fl_Type *t = root; t; t = t->next) { + if (t->level < root->level) { + fprintf(stderr, "ERROR: Node in tree is above root level!\n"); + return false; + } + if (t->next) { + // Make sure that all `next` types have the `prev` member link back + if (t->next->prev != t) { + fprintf(stderr, "ERROR: Doubly linked list broken!\n"); + return false; + } + if (t->next->level > t->level) { + // Validate `level` changes + if (t->next->level - t->level > 1) { + fprintf(stderr, "ERROR: Child level increment greater than one!\n"); + return false; + } + // Ensure that this node can actually have children + if (!t->can_have_children()) { + fprintf(stderr, "ERROR: This parent must not have children!\n"); + return false; + } + } + } + // Validate the `parent` entry + for (Fl_Type *p = t->prev; ; p = p->prev) { + if (p == NULL) { + if (t->parent != NULL) { + fprintf(stderr, "ERROR: `parent` pointer should be NULL!\n"); + return false; + } + break; + } + if (p->level < t->level) { + if (t->parent != p) { + fprintf(stderr, "ERROR: `parent` points to wrong parent!\n"); + return false; + } + break; + } + } + } + return true; +} +#endif +#endif + void select_all_cb(Fl_Widget *,void *) { Fl_Type *p = Fl_Type::current ? Fl_Type::current->parent : 0; if (in_this_only) { @@ -317,7 +454,7 @@ int storestring(const char *n, const char * & p, int nostrip) { void update_visibility_flag(Fl_Type *p) { Fl_Type *t = p; for (;;) { - if (t->parent) t->visible = t->parent->visible && t->parent->open_; + if (t->parent) t->visible = t->parent->visible && !t->parent->folded_; else t->visible = 1; t = t->next; if (!t || t->level <= p->level) break; @@ -470,42 +607,85 @@ Fl_Group_Type *Fl_Type::group() { \param[in] p insert \c this tree as a child of \c p \param[in] strategy is kAddAsLastChild or kAddAfterCurrent */ -void Fl_Type::add(Fl_Type *p, Strategy strategy) { - if (p && parent == p) return; - undo_checkpoint(); - parent = p; - // 'this' is not in the Widget_Browser, so we must run the linked list to find the last entry +void Fl_Type::add(Fl_Type *anchor, Strategy strategy) { +#if 0 +#ifndef NDEBUG + // print_project_tree(); + // fprintf(stderr, "Validating project\n"); + validate_project_tree(); + // fprintf(stderr, "Validating branch\n"); + validate_independent_branch(this); +#endif +#endif + + Fl_Type *target = NULL; // insert self before target node, if NULL, insert last + Fl_Type *target_parent = NULL; // this will be the new parent for branch + int target_level = 0; // adjust self to this new level + + // Find the node after our insertion position + switch (strategy) { + case kAddAsFirstChild: + if (anchor == NULL) { + target = Fl_Type::first; + } else { + target = anchor->next; + target_level = anchor->level + 1; + target_parent = anchor; + } + break; + case kAddAsLastChild: + if (anchor == NULL) { + /* empty */ + } else { + for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} + target_level = anchor->level + 1; + target_parent = anchor; + } + break; + case kAddAfterCurrent: + if (anchor == NULL) { + target = Fl_Type::first; + } else { + for (target = anchor->next; target && target->level > anchor->level; target = target->next) {/*empty*/} + target_level = anchor->level; + target_parent = anchor->parent; + } + break; + } + + + // Find the last node of our tree Fl_Type *end = this; while (end->next) end = end->next; - // run the list again to set the future node levels - Fl_Type *q; // insert 'this' before q - int newlevel; - if (p) { - // find the last node that is a child or grandchild of p - for (q = p->next; q && q->level > p->level; q = q->next) {/*empty*/} - newlevel = p->level+1; + + // Everything is prepared, now insert ourself in front of the target node + undo_checkpoint(); + + // Walk the tree to update parent pointers and levels + int source_level = level; + for (Fl_Type *t = this; t; t = t->next) { + t->level += (target_level-source_level); + if (t->level == target_level) + t->parent = target_parent; + } + + // Now link ourselves and our children before 'target', or last, if 'target' is NULL + if (target) { + prev = target->prev; + target->prev = end; + end->next = target; } else { - q = 0; - newlevel = 0; + prev = Fl_Type::last; + end->next = NULL; + Fl_Type::last = end; } - for (Fl_Type *t = this->next; t; t = t->next) t->level += (newlevel-level); - level = newlevel; - // now link 'this' and its children before 'q', or last, if 'q' is NULL - if (q) { - prev = q->prev; - prev->next = this; - q->prev = end; - end->next = q; - } else if (first) { - prev = last; + if (prev) { prev->next = this; - end->next = 0; - last = end; } else { - first = this; - last = end; - prev = end->next = 0; + Fl_Type::first = this; } + +#if 0 { // make sure that we have no duplicate uid's Fl_Type *tp = this; do { @@ -513,28 +693,24 @@ void Fl_Type::add(Fl_Type *p, Strategy strategy) { tp = tp->next; } while (tp!=end && tp!=NULL); } +#endif - // tell this that it was added, so it can update itself - if (p) p->add_child(this,0); - open_ = 1; - update_visibility_flag(this); - set_modflag(1); - - if (strategy==kAddAfterCurrent && current) { - // we have current, t is the new node, p is the parent - // find the next child of the parent after current - //t->add(p); // add as a last child - Fl_Type *cc; - for (cc = current->next; cc; cc = cc->next) { - if (cc->level <= this->level) - break; - } - if (cc && cc->level==this->level && cc!=this) { - this->move_before(cc); - } - select(this, 1); + // Give the widgets in our tree a chance to update themselves + for (Fl_Type *t = this; t && t!=end->next; t = t->next) { + if (target_parent && (t->level == target_level)) + target_parent->add_child(t, 0); + update_visibility_flag(t); } + + set_modflag(1); widget_browser->redraw(); + +#if 0 +#ifndef NDEBUG + // fprintf(stderr, "Validating project after adding branch\n"); + validate_project_tree(); +#endif +#endif } /** @@ -757,7 +933,7 @@ void Fl_Type::write_properties(Fd_Project_Writer &f) { f.write_word("comment"); f.write_word(comment()); } - if (can_have_children() && open_) f.write_word("open"); + if (can_have_children() && !folded_) f.write_word("open"); if (selected) f.write_word("selected"); } @@ -779,7 +955,7 @@ void Fl_Type::read_property(Fd_Project_Reader &f, const char *c) { else if (!strcmp(c,"comment")) comment(f.read_word()); else if (!strcmp(c,"open")) - open_ = 1; + folded_ = 0; else if (!strcmp(c,"selected")) select(this,1); else if (!strcmp(c,"parent_properties")) |
