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
This commit is contained in:
Matthias Melcher 2024-09-14 01:10:35 +02:00
parent bb917628ff
commit e7f1247552
11 changed files with 368 additions and 136 deletions

View File

@ -206,12 +206,17 @@ Fl_Function_Type::~Fl_Function_Type() {
\return the new node \return the new node
*/ */
Fl_Type *Fl_Function_Type::make(Strategy strategy) { Fl_Type *Fl_Function_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && !p->is_decl_block()) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && !p->is_decl_block()) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
Fl_Function_Type *o = new Fl_Function_Type(); Fl_Function_Type *o = new Fl_Function_Type();
o->name("make_window()"); o->name("make_window()");
o->return_type = 0; o->return_type = 0;
o->add(p, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
o->public_ = 1; o->public_ = 1;
o->cdecl_ = 0; o->cdecl_ = 0;
@ -590,15 +595,20 @@ Fl_Code_Type::Fl_Code_Type() :
\return new Code node \return new Code node
*/ */
Fl_Type *Fl_Code_Type::make(Strategy strategy) { Fl_Type *Fl_Code_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && !p->is_code_block()) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && !p->is_code_block()) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
if (!p) { if (!p) {
fl_message("Please select a function"); fl_message("Please select a function");
return 0; return 0;
} }
Fl_Code_Type *o = new Fl_Code_Type(); Fl_Code_Type *o = new Fl_Code_Type();
o->name("printf(\"Hello, World!\\n\");"); o->name("printf(\"Hello, World!\\n\");");
o->add(p, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
return o; return o;
} }
@ -752,8 +762,13 @@ Fl_CodeBlock_Type::~Fl_CodeBlock_Type() {
\return new CodeBlock \return new CodeBlock
*/ */
Fl_Type *Fl_CodeBlock_Type::make(Strategy strategy) { Fl_Type *Fl_CodeBlock_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && !p->is_code_block()) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && !p->is_code_block()) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
if (!p) { if (!p) {
fl_message("Please select a function"); fl_message("Please select a function");
return 0; return 0;
@ -761,7 +776,7 @@ Fl_Type *Fl_CodeBlock_Type::make(Strategy strategy) {
Fl_CodeBlock_Type *o = new Fl_CodeBlock_Type(); Fl_CodeBlock_Type *o = new Fl_CodeBlock_Type();
o->name("if (test())"); o->name("if (test())");
o->after = 0; o->after = 0;
o->add(p, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
return o; return o;
} }
@ -888,13 +903,18 @@ int Fl_Decl_Type::is_public() const
\return new Declaration node \return new Declaration node
*/ */
Fl_Type *Fl_Decl_Type::make(Strategy strategy) { Fl_Type *Fl_Decl_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && !p->is_decl_block()) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && !p->is_decl_block()) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
Fl_Decl_Type *o = new Fl_Decl_Type(); Fl_Decl_Type *o = new Fl_Decl_Type();
o->public_ = 0; o->public_ = 0;
o->static_ = 1; o->static_ = 1;
o->name("int x;"); o->name("int x;");
o->add(p, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
return o; return o;
} }
@ -1110,15 +1130,20 @@ Fl_Data_Type::~Fl_Data_Type() {
\return new inline data node \return new inline data node
*/ */
Fl_Type *Fl_Data_Type::make(Strategy strategy) { Fl_Type *Fl_Data_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && !p->is_decl_block()) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && !p->is_decl_block()) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
Fl_Data_Type *o = new Fl_Data_Type(); Fl_Data_Type *o = new Fl_Data_Type();
o->public_ = 1; o->public_ = 1;
o->static_ = 1; o->static_ = 1;
o->filename_ = 0; o->filename_ = 0;
o->text_mode_ = 0; o->text_mode_ = 0;
o->name("myInlineData"); o->name("myInlineData");
o->add(p, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
return o; return o;
} }
@ -1448,13 +1473,18 @@ int Fl_DeclBlock_Type::is_public() const {
\return new Declaration Block node \return new Declaration Block node
*/ */
Fl_Type *Fl_DeclBlock_Type::make(Strategy strategy) { Fl_Type *Fl_DeclBlock_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && !p->is_decl_block()) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && !p->is_decl_block()) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
Fl_DeclBlock_Type *o = new Fl_DeclBlock_Type(); Fl_DeclBlock_Type *o = new Fl_DeclBlock_Type();
o->name("#if 1"); o->name("#if 1");
o->write_map_ = CODE_IN_SOURCE; o->write_map_ = CODE_IN_SOURCE;
o->after = fl_strdup("#endif"); o->after = fl_strdup("#endif");
o->add(p, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
return o; return o;
} }
@ -1675,14 +1705,19 @@ Fl_Comment_Type::Fl_Comment_Type() :
\return new Comment node \return new Comment node
*/ */
Fl_Type *Fl_Comment_Type::make(Strategy strategy) { Fl_Type *Fl_Comment_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && !p->is_code_block()) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && !p->is_code_block()) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
Fl_Comment_Type *o = new Fl_Comment_Type(); Fl_Comment_Type *o = new Fl_Comment_Type();
o->in_c_ = 1; o->in_c_ = 1;
o->in_h_ = 1; o->in_h_ = 1;
o->style_ = 0; o->style_ = 0;
o->name("my comment"); o->name("my comment");
o->add(p, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
return o; return o;
} }
@ -1949,14 +1984,19 @@ void Fl_Class_Type::prefix(const char*p) {
\return new Class node \return new Class node
*/ */
Fl_Type *Fl_Class_Type::make(Strategy strategy) { Fl_Type *Fl_Class_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && !p->is_decl_block()) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && !p->is_decl_block()) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
Fl_Class_Type *o = new Fl_Class_Type(); Fl_Class_Type *o = new Fl_Class_Type();
o->name("UserInterface"); o->name("UserInterface");
o->class_prefix = NULL; o->class_prefix = NULL;
o->subclass_of = NULL; o->subclass_of = NULL;
o->public_ = 1; o->public_ = 1;
o->add(p, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
return o; return o;
} }

View File

@ -160,15 +160,16 @@ Fl_Type *Fl_Menu_Item_Type::make(Strategy strategy) {
\return new Menu Item node \return new Menu Item node
*/ */
Fl_Type* Fl_Menu_Item_Type::make(int flags, Strategy strategy) { Fl_Type* Fl_Menu_Item_Type::make(int flags, Strategy strategy) {
// Find the current menu item: // Find a good insert position based on the current marked node
Fl_Type* q = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
Fl_Type* p = q; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
if (p) { while (p && !(p->is_a(ID_Menu_Manager_) || p->is_a(ID_Submenu))) {
if ( (force_parent && q->is_a(ID_Menu_Item)) || !q->can_have_children()) p = p->parent; anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
} }
force_parent = 0; if (!p) {
if (!p || !(p->is_a(ID_Menu_Manager_) || p->is_a(ID_Submenu))) { fl_message("Please select a menu widget or a menu item");
fl_message("Please select a menu to add to");
return 0; return 0;
} }
if (!o) { if (!o) {
@ -184,7 +185,7 @@ Fl_Type* Fl_Menu_Item_Type::make(int flags, Strategy strategy) {
t->o = new Fl_Button(0,0,100,20); t->o = new Fl_Button(0,0,100,20);
t->o->type(flags); t->o->type(flags);
t->factory = this; t->factory = this;
t->add(p, strategy); t->add(anchor, strategy);
if (!reading_file) { if (!reading_file) {
if (flags==FL_SUBMENU) { if (flags==FL_SUBMENU) {
t->label("submenu"); t->label("submenu");

View File

@ -130,6 +130,143 @@ Fl_Type *in_this_only; // set if menu popped-up in window
// ---- various functions // ---- 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 *) { void select_all_cb(Fl_Widget *,void *) {
Fl_Type *p = Fl_Type::current ? Fl_Type::current->parent : 0; Fl_Type *p = Fl_Type::current ? Fl_Type::current->parent : 0;
if (in_this_only) { 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) { void update_visibility_flag(Fl_Type *p) {
Fl_Type *t = p; Fl_Type *t = p;
for (;;) { 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; else t->visible = 1;
t = t->next; t = t->next;
if (!t || t->level <= p->level) break; 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] p insert \c this tree as a child of \c p
\param[in] strategy is kAddAsLastChild or kAddAfterCurrent \param[in] strategy is kAddAsLastChild or kAddAfterCurrent
*/ */
void Fl_Type::add(Fl_Type *p, Strategy strategy) { void Fl_Type::add(Fl_Type *anchor, Strategy strategy) {
if (p && parent == p) return; #if 0
undo_checkpoint(); #ifndef NDEBUG
parent = p; // print_project_tree();
// 'this' is not in the Widget_Browser, so we must run the linked list to find the last entry // 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; Fl_Type *end = this;
while (end->next) end = end->next; while (end->next) end = end->next;
// run the list again to set the future node levels
Fl_Type *q; // insert 'this' before q // Everything is prepared, now insert ourself in front of the target node
int newlevel; undo_checkpoint();
if (p) {
// find the last node that is a child or grandchild of p // Walk the tree to update parent pointers and levels
for (q = p->next; q && q->level > p->level; q = q->next) {/*empty*/} int source_level = level;
newlevel = p->level+1; for (Fl_Type *t = this; t; t = t->next) {
} else { t->level += (target_level-source_level);
q = 0; if (t->level == target_level)
newlevel = 0; t->parent = target_parent;
} }
for (Fl_Type *t = this->next; t; t = t->next) t->level += (newlevel-level);
level = newlevel; // Now link ourselves and our children before 'target', or last, if 'target' is NULL
// now link 'this' and its children before 'q', or last, if 'q' is NULL if (target) {
if (q) { prev = target->prev;
prev = q->prev; target->prev = end;
prev->next = this; end->next = target;
q->prev = end;
end->next = q;
} else if (first) {
prev = last;
prev->next = this;
end->next = 0;
last = end;
} else { } else {
first = this; prev = Fl_Type::last;
last = end; end->next = NULL;
prev = end->next = 0; Fl_Type::last = end;
} }
if (prev) {
prev->next = this;
} else {
Fl_Type::first = this;
}
#if 0
{ // make sure that we have no duplicate uid's { // make sure that we have no duplicate uid's
Fl_Type *tp = this; Fl_Type *tp = this;
do { do {
@ -513,28 +693,24 @@ void Fl_Type::add(Fl_Type *p, Strategy strategy) {
tp = tp->next; tp = tp->next;
} while (tp!=end && tp!=NULL); } while (tp!=end && tp!=NULL);
} }
#endif
// 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);
}
// 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); 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);
}
widget_browser->redraw(); 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");
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"); 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")) else if (!strcmp(c,"comment"))
comment(f.read_word()); comment(f.read_word());
else if (!strcmp(c,"open")) else if (!strcmp(c,"open"))
open_ = 1; folded_ = 0;
else if (!strcmp(c,"selected")) else if (!strcmp(c,"selected"))
select(this,1); select(this,1);
else if (!strcmp(c,"parent_properties")) else if (!strcmp(c,"parent_properties"))

View File

@ -46,7 +46,8 @@ class Fd_Project_Writer;
int Fd_Project_Reader::read_project(const char *filename, int merge, Strategy strategy) int Fd_Project_Reader::read_project(const char *filename, int merge, Strategy strategy)
*/ */
typedef enum { typedef enum {
kAddAsLastChild = 0, kAddAsFirstChild = 0,
kAddAsLastChild,
kAddAfterCurrent kAddAfterCurrent
} Strategy; } Strategy;
@ -92,6 +93,13 @@ void select_none_cb(Fl_Widget *,void *);
void earlier_cb(Fl_Widget*,void*); void earlier_cb(Fl_Widget*,void*);
void later_cb(Fl_Widget*,void*); void later_cb(Fl_Widget*,void*);
#ifndef NDEBUG
void print_project_tree();
bool validate_project_tree();
bool validate_independent_branch(class Fl_Type *root);
bool validate_branch(class Fl_Type *root);
#endif
/** /**
\brief This is the base class for all elements in the project tree. \brief This is the base class for all elements in the project tree.
@ -150,7 +158,7 @@ public: // things that should not be public:
(see `haderror`). It seems that this is often confused with new_selected (see `haderror`). It seems that this is often confused with new_selected
which seems to hold the true and visible selection state. */ which seems to hold the true and visible selection state. */
char selected; // copied here by selection_changed() char selected; // copied here by selection_changed()
char open_; // state of triangle in browser char folded_; // if set, children are not shown in browser
char visible; // true if all parents are open char visible; // true if all parents are open
int level; // number of parents over this int level; // number of parents over this
static Fl_Type *first, *last; static Fl_Type *first, *last;

View File

@ -87,19 +87,20 @@ Fl_Widget_Type::ideal_size(int &w, int &h) {
\return new node \return new node
*/ */
Fl_Type *Fl_Widget_Type::make(Strategy strategy) { Fl_Type *Fl_Widget_Type::make(Strategy strategy) {
// Find the current widget, or widget to copy: Fl_Type *anchor = Fl_Type::current, *pp = anchor;
Fl_Type *qq = Fl_Type::current; if (pp && (strategy == kAddAfterCurrent)) pp = pp->parent;
while (qq && (!qq->is_true_widget() || !qq->can_have_children())) qq = qq->parent; while (pp && !pp->is_a(ID_Group)) {
if (!qq) { anchor = pp;
strategy = kAddAfterCurrent;
pp = pp->parent;
}
if (!pp || !pp->is_true_widget() || !anchor->is_true_widget()) {
fl_message("Please select a group widget or window"); fl_message("Please select a group widget or window");
return 0; return 0;
} }
Fl_Widget_Type* q = (Fl_Widget_Type*)qq;
// find the parent widget: Fl_Widget_Type* p = (Fl_Widget_Type*)pp;
Fl_Widget_Type* p = q; Fl_Widget_Type* q = (Fl_Widget_Type*)anchor;
if ((force_parent || !p->is_a(ID_Group)) && p->parent && p->parent->is_widget())
p = (Fl_Widget_Type*)(p->parent);
force_parent = 0;
// Figure out a border between widget and window: // Figure out a border between widget and window:
int B = p->o->w()/2; if (p->o->h()/2 < B) B = p->o->h()/2; if (B>25) B = 25; int B = p->o->w()/2; if (p->o->h()/2 < B) B = p->o->h()/2; if (B>25) B = 25;
@ -146,7 +147,7 @@ Fl_Type *Fl_Widget_Type::make(Strategy strategy) {
// Put it in the parent: // Put it in the parent:
// ((Fl_Group *)(p->o))->add(t->o); (done by Fl_Type::add()) // ((Fl_Group *)(p->o))->add(t->o); (done by Fl_Type::add())
// add to browser: // add to browser:
t->add(p, strategy); t->add(anchor, strategy);
t->redraw(); t->redraw();
return t; return t;
} }

View File

@ -227,8 +227,13 @@ int Overlay_Window::handle(int e) {
\return new node \return new node
*/ */
Fl_Type *Fl_Window_Type::make(Strategy strategy) { Fl_Type *Fl_Window_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && (!p->is_code_block() || p->is_a(ID_Widget_Class))) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && (!p->is_code_block() || p->is_a(ID_Widget_Class))) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
if (!p) { if (!p) {
fl_message("Please select a function"); fl_message("Please select a function");
return 0; return 0;
@ -245,7 +250,7 @@ Fl_Type *Fl_Window_Type::make(Strategy strategy) {
w->size_range(10, 10); w->size_range(10, 10);
w->window = myo; w->window = myo;
myo->o = w; myo->o = w;
myo->add(p, strategy); myo->add(anchor, strategy);
myo->modal = 0; myo->modal = 0;
myo->non_modal = 0; myo->non_modal = 0;
return myo; return myo;
@ -1363,8 +1368,13 @@ Fl_Widget_Class_Type *current_widget_class = 0;
\return new node \return new node
*/ */
Fl_Type *Fl_Widget_Class_Type::make(Strategy strategy) { Fl_Type *Fl_Widget_Class_Type::make(Strategy strategy) {
Fl_Type *p = Fl_Type::current; Fl_Type *anchor = Fl_Type::current, *p = anchor;
while (p && (!p->is_decl_block() || (p->is_widget() && p->is_class()))) p = p->parent; if (p && (strategy == kAddAfterCurrent)) p = p->parent;
while (p && (!p->is_decl_block() || (p->is_widget() && p->is_class()))) {
anchor = p;
strategy = kAddAfterCurrent;
p = p->parent;
}
Fl_Widget_Class_Type *myo = new Fl_Widget_Class_Type(); Fl_Widget_Class_Type *myo = new Fl_Widget_Class_Type();
myo->name("UserInterface"); myo->name("UserInterface");
@ -1379,7 +1389,7 @@ Fl_Type *Fl_Widget_Class_Type::make(Strategy strategy) {
w->size_range(10, 10); w->size_range(10, 10);
w->window = myo; w->window = myo;
myo->o = w; myo->o = w;
myo->add(p, strategy); myo->add(anchor, strategy);
myo->modal = 0; myo->modal = 0;
myo->non_modal = 0; myo->non_modal = 0;
myo->wc_relative = 0; myo->wc_relative = 0;

View File

@ -1342,11 +1342,11 @@ Fl_Type *add_new_widget_from_user(const char *inName, Strategy strategy, bool an
} }
/** /**
Callback for all menu items. Callback for all non-widget menu items.
*/ */
static void cbf(Fl_Widget *, void *v) { static void cbf(Fl_Widget *, void *v) {
Fl_Type *t = NULL; Fl_Type *t = NULL;
if (Fl_Type::current && Fl_Type::current->is_a(ID_Group)) if (Fl_Type::current && Fl_Type::current->can_have_children())
t = ((Fl_Type*)v)->make(kAddAsLastChild); t = ((Fl_Type*)v)->make(kAddAsLastChild);
else else
t = ((Fl_Type*)v)->make(kAddAfterCurrent); t = ((Fl_Type*)v)->make(kAddAfterCurrent);
@ -1354,11 +1354,11 @@ static void cbf(Fl_Widget *, void *v) {
} }
/** /**
Callback for all menu items. Callback for all widget menu items.
*/ */
static void cb(Fl_Widget *, void *v) { static void cb(Fl_Widget *, void *v) {
Fl_Type *t = NULL; Fl_Type *t = NULL;
if (Fl_Type::current && Fl_Type::current->is_a(ID_Group)) if (Fl_Type::current && Fl_Type::current->can_have_children())
t = add_new_widget_from_user((Fl_Type*)v, kAddAsLastChild); t = add_new_widget_from_user((Fl_Type*)v, kAddAsLastChild);
else else
t = add_new_widget_from_user((Fl_Type*)v, kAddAfterCurrent); t = add_new_widget_from_user((Fl_Type*)v, kAddAfterCurrent);

View File

@ -227,6 +227,7 @@ int Fd_Project_Reader::read_quoted() { // read whatever character is after
Fl_Type *Fd_Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, char skip_options) { Fl_Type *Fd_Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, char skip_options) {
Fl_Type::current = p; Fl_Type::current = p;
Fl_Type *last_child_read = NULL; Fl_Type *last_child_read = NULL;
Fl_Type *t = NULL;
for (;;) { for (;;) {
const char *c = read_word(); const char *c = read_word();
REUSE_C: REUSE_C:
@ -354,7 +355,7 @@ Fl_Type *Fd_Project_Reader::read_children(Fl_Type *p, int merge, Strategy strate
} }
} }
{ {
Fl_Type *t = add_new_widget_from_file(c, strategy); t = add_new_widget_from_file(c, strategy);
if (!t) { if (!t) {
read_error("Unknown word \"%s\"", c); read_error("Unknown word \"%s\"", c);
continue; continue;
@ -377,7 +378,7 @@ Fl_Type *Fd_Project_Reader::read_children(Fl_Type *p, int merge, Strategy strate
goto REUSE_C; goto REUSE_C;
} }
t->open_ = 0; t->folded_ = 1;
for (;;) { for (;;) {
const char *cc = read_word(); const char *cc = read_word();
if (!cc || !strcmp(cc,"}")) break; if (!cc || !strcmp(cc,"}")) break;
@ -390,7 +391,7 @@ Fl_Type *Fd_Project_Reader::read_children(Fl_Type *p, int merge, Strategy strate
read_error("Missing child list for %s\n",t->title()); read_error("Missing child list for %s\n",t->title());
goto REUSE_C; goto REUSE_C;
} }
read_children(t, 0, strategy, skip_options); read_children(t, 0, kAddAsLastChild, skip_options);
t->postprocess_read(); t->postprocess_read();
// FIXME: this has no business in the file reader! // FIXME: this has no business in the file reader!
// TODO: this is called whenever something is pasted from the top level into a grid // TODO: this is called whenever something is pasted from the top level into a grid
@ -405,8 +406,11 @@ Fl_Type *Fd_Project_Reader::read_children(Fl_Type *p, int merge, Strategy strate
t->layout_widget(); t->layout_widget();
} }
if (strategy == kAddAfterCurrent) {
Fl_Type::current = t;
} else {
Fl_Type::current = p; Fl_Type::current = p;
}
CONTINUE:; CONTINUE:;
} }

View File

@ -106,11 +106,6 @@ int G_debug = 0;
char G_external_editor_command[512]; char G_external_editor_command[512];
/// If set, if the `current` node is a group, and a new group is added, it will
/// be added as sibling to the first group instead of inside the group.
/// \todo Needs to be verified.
int force_parent = 0;
/// This is set to create different labels when creating new widgets. /// This is set to create different labels when creating new widgets.
/// \todo Details unclear. /// \todo Details unclear.
int reading_file = 0; int reading_file = 0;
@ -1411,13 +1406,16 @@ void delete_cb(Fl_Widget *, void *) {
User chose to paste the widgets from the cut buffer. User chose to paste the widgets from the cut buffer.
*/ */
void paste_cb(Fl_Widget*, void*) { void paste_cb(Fl_Widget*, void*) {
//if (ipasteoffset) force_parent = 1;
pasteoffset = ipasteoffset; pasteoffset = ipasteoffset;
undo_checkpoint(); undo_checkpoint();
undo_suspend(); undo_suspend();
Strategy strategy = kAddAfterCurrent; Strategy strategy = kAddAfterCurrent;
if (Fl_Type::current && Fl_Type::current->is_a(ID_Group)) if (Fl_Type::current && Fl_Type::current->is_a(ID_Group)) {
Fl_Group_Type *current_group = static_cast<Fl_Group_Type*>(Fl_Type::current);
if (current_group->folded_ == 0) {
strategy = kAddAsLastChild; strategy = kAddAsLastChild;
}
}
if (!read_file(cutfname(), 1, strategy)) { if (!read_file(cutfname(), 1, strategy)) {
widget_browser->rebuild(); widget_browser->rebuild();
fl_message("Can't read %s: %s", cutfname(), strerror(errno)); fl_message("Can't read %s: %s", cutfname(), strerror(errno));
@ -1427,7 +1425,6 @@ void paste_cb(Fl_Widget*, void*) {
widget_browser->rebuild(); widget_browser->rebuild();
pasteoffset = 0; pasteoffset = 0;
ipasteoffset += 10; ipasteoffset += 10;
force_parent = 0;
} }
/** /**
@ -1447,7 +1444,6 @@ void duplicate_cb(Fl_Widget*, void*) {
} }
pasteoffset = 0; pasteoffset = 0;
force_parent = 1;
undo_checkpoint(); undo_checkpoint();
undo_suspend(); undo_suspend();
@ -1458,8 +1454,6 @@ void duplicate_cb(Fl_Widget*, void*) {
widget_browser->display(Fl_Type::current); widget_browser->display(Fl_Type::current);
widget_browser->rebuild(); widget_browser->rebuild();
undo_resume(); undo_resume();
force_parent = 0;
} }
/** /**

View File

@ -51,8 +51,6 @@ typedef enum {
// ---- global variables // ---- global variables
extern int force_parent;
extern Fl_Preferences fluid_prefs; extern Fl_Preferences fluid_prefs;
extern Fl_Menu_Item Main_Menu[]; extern Fl_Menu_Item Main_Menu[];
extern Fl_Menu_Bar *main_menubar; extern Fl_Menu_Bar *main_menubar;

View File

@ -120,8 +120,8 @@ void reveal_in_browser(Fl_Type *t) {
Fl_Type *p = t->parent; Fl_Type *p = t->parent;
if (p) { if (p) {
for (;;) { for (;;) {
if (!p->open_) if (p->folded_)
p->open_ = 1; p->folded_ = 0;
if (!p->parent) break; if (!p->parent) break;
p = p->parent; p = p->parent;
} }
@ -340,7 +340,7 @@ void Widget_Browser::item_draw(void *v, int X, int Y, int, int) const {
if (l->can_have_children()) { if (l->can_have_children()) {
X = X - 18 - 13; X = X - 18 - 13;
if (!l->next || l->next->level <= l->level) { if (!l->next || l->next->level <= l->level) {
if (l->open_!=(l==pushedtitle)) { if (l->folded_==(l==pushedtitle)) {
// an outlined triangle to the right indicates closed item, no children // an outlined triangle to the right indicates closed item, no children
fl_loop(X,Y+7,X+5,Y+12,X+10,Y+7); fl_loop(X,Y+7,X+5,Y+12,X+10,Y+7);
} else { } else {
@ -348,7 +348,7 @@ void Widget_Browser::item_draw(void *v, int X, int Y, int, int) const {
fl_loop(X+2,Y+2,X+7,Y+7,X+2,Y+12); fl_loop(X+2,Y+2,X+7,Y+7,X+2,Y+12);
} }
} else { } else {
if (l->open_!=(l==pushedtitle)) { if (l->folded_==(l==pushedtitle)) {
// a filled triangle to the right indicates closed item, with children // a filled triangle to the right indicates closed item, with children
fl_polygon(X,Y+7,X+5,Y+12,X+10,Y+7); fl_polygon(X,Y+7,X+5,Y+12,X+10,Y+7);
} else { } else {
@ -538,15 +538,15 @@ int Widget_Browser::handle(int e) {
l = pushedtitle; l = pushedtitle;
title = pushedtitle = 0; title = pushedtitle = 0;
if (l) { if (l) {
if (l->open_) { if (!l->folded_) {
l->open_ = 0; l->folded_ = 1;
for (Fl_Type*k = l->next; k&&k->level>l->level; k = k->next) for (Fl_Type*k = l->next; k&&k->level>l->level; k = k->next)
k->visible = 0; k->visible = 0;
} else { } else {
l->open_ = 1; l->folded_ = 0;
for (Fl_Type*k=l->next; k&&k->level>l->level;) { for (Fl_Type*k=l->next; k&&k->level>l->level;) {
k->visible = 1; k->visible = 1;
if (k->can_have_children() && !k->open_) { if (k->can_have_children() && k->folded_) {
Fl_Type *j; Fl_Type *j;
for (j = k->next; j && j->level>k->level; j = j->next) {/*empty*/} for (j = k->next; j && j->level>k->level; j = j->next) {/*empty*/}
k = j; k = j;