FLUID: fixes crash bug in Fl_Sys_Men_Bar inside a Class. #977

This commit is contained in:
Matthias Melcher 2024-07-27 20:22:50 +02:00
parent 8cffbd6941
commit d265009b66
4 changed files with 148 additions and 12 deletions

View File

@ -290,19 +290,49 @@ void Fl_Menu_Item_Type::write_static(Fd_Code_Writer& f) {
f.write_c("\n");
// Matt: disabled f.tag(FD_TAG_MENU_CALLBACK, get_uid());
f.write_c("}\n");
// If the menu item is part of a Class or Widget Class, FLUID generates
// a dummy static callback which retrieves a pointer to the class and then
// calls the original callback from within the class context.
// k is the name of the enclosing class (or classes)
if (k) {
// Implement the callback as a static member function
f.write_c("void %s::%s(Fl_Menu_* o, %s v) {\n", k, cn, ut);
f.write_c("%s((%s*)(o", f.indent(1), k);
// Find the Fl_Menu_ container for this menu item
Fl_Type* t = parent; while (t->is_a(ID_Menu_Item)) t = t->parent;
Fl_Type *q = 0;
// Go up one more level for Fl_Input_Choice, as these are groups themselves
if (t && t->is_a(ID_Input_Choice))
f.write_c("->parent()");
for (t = t->parent; t && t->is_widget() && !is_class(); q = t, t = t->parent)
f.write_c("->parent()");
if (!q || !q->is_a(ID_Widget_Class))
f.write_c("->user_data()");
f.write_c("))->%s_i(o,v);\n}\n", cn);
if (t) {
Fl_Widget_Type *tw = (t->is_widget()) ? static_cast<Fl_Widget_Type*>(t) : NULL;
Fl_Type *q = NULL;
// Generate code to call the callback
if (tw->is_a(ID_Menu_Bar) && ((Fl_Menu_Bar_Type*)tw)->is_sys_menu_bar()) {
// Fl_Sys_Menu_Bar removes itself from any parent on macOS, so we
// wrapped it in a class and remeber the parent class in a new
// class memeber variable.
Fl_Menu_Bar_Type *tmb = (Fl_Menu_Bar_Type*)tw;
f.write_c("%s%s* sys_menu_bar = ((%s*)o);\n", f.indent(1),
tmb->sys_menubar_proxy_name(), tmb->sys_menubar_proxy_name());
f.write_c("%s%s* parent_class = ((%s*)sys_menu_bar->_parent_class);\n",
f.indent(1), k, k);
f.write_c("%sparent_class->%s_i(o,v);\n}\n",
f.indent(1), cn);
} else {
f.write_c("%s((%s*)(o", f.indent(1), k);
// The class pointer is in the user_data field of the top widget
if (t && t->is_a(ID_Input_Choice)) {
// Go up one more level for Fl_Input_Choice, as these are groups themselves
f.write_c("->parent()");
}
// Now generate code to find the topmost widget in this class
for (t = t->parent; t && t->is_widget() && !is_class(); q = t, t = t->parent)
f.write_c("->parent()");
// user_data is cast into a pointer to the
if (!q || !q->is_a(ID_Widget_Class))
f.write_c("->user_data()");
f.write_c("))->%s_i(o,v);\n}\n", cn);
}
} else {
f.write_c("#error Enclosing Fl_Menu_* not found\n");
}
}
}
if (image) {
@ -700,10 +730,80 @@ Fl_Type* Fl_Input_Choice_Type::click_test(int, int) {
Fl_Menu_Bar_Type Fl_Menu_Bar_type;
Fl_Menu_Item menu_bar_type_menu[] = {
{"Fl_Menu_Bar",0,0,(void*)0},
{"Fl_Sys_Menu_Bar",0,0,(void*)1},
{0}};
Fl_Menu_Bar_Type::Fl_Menu_Bar_Type()
: _proxy_name(NULL)
{
}
Fl_Menu_Bar_Type::~Fl_Menu_Bar_Type() {
if (_proxy_name)
::free(_proxy_name);
}
/**
\brief Return true if this is an Fl_Sys_Menu_Bar.
This test fails if subclass() is the name of a class that the user may have
derived from Fl_Sys_Menu_Bar.
*/
bool Fl_Menu_Bar_Type::is_sys_menu_bar() {
if (o->type()==1) return true;
return ( subclass() && (strcmp(subclass(), "Fl_Sys_Menu_Bar")==0) );
}
const char *Fl_Menu_Bar_Type::sys_menubar_name() {
if (subclass())
return subclass();
else
return "Fl_Sys_Menu_Bar";
}
const char *Fl_Menu_Bar_Type::sys_menubar_proxy_name() {
if (!_proxy_name)
_proxy_name = (char*)::malloc(128);
::snprintf(_proxy_name, 63, "%s_Proxy", sys_menubar_name());
return _proxy_name;
}
void Fl_Menu_Bar_Type::write_static(Fd_Code_Writer& f) {
super::write_static(f);
if (is_sys_menu_bar()) {
f.write_h_once("#include <FL/Fl_Sys_Menu_Bar.H>");
if (is_in_class()) {
// Make room for a pointer to the enclosing class.
f.write_c_once( // must be less than 1024 bytes!
"\nclass %s: public %s {\n"
"public:\n"
" %s(int x, int y, int w, int h, const char *l=NULL)\n"
" : %s(x, y, w, h, l) { }\n"
" void *_parent_class;\n"
"};\n",
sys_menubar_proxy_name(), sys_menubar_name(),
sys_menubar_proxy_name(), sys_menubar_name()
);
}
}
}
void Fl_Menu_Bar_Type::write_code1(Fd_Code_Writer& f) {
super::write_code1(f);
if (is_sys_menu_bar() && is_in_class()) {
f.write_c("%s((%s*)%s)->_parent_class = (void*)this;\n",
f.indent(), sys_menubar_proxy_name(), name() ? name() : "o");
}
}
//void Fl_Menu_Bar_Type::write_code2(Fd_Code_Writer& f) {
// super::write_code2(f);
//}
////////////////////////////////////////////////////////////////
// Shortcut entry item in panel:
void shortcut_in_cb(Fl_Shortcut_Button* i, void* v) {
if (v == LOAD) {
if (current_widget->is_button())

View File

@ -35,6 +35,7 @@
extern Fl_Menu_Item dummymenu[];
extern Fl_Menu_Item button_type_menu[];
extern Fl_Menu_Item menu_item_type_menu[];
extern Fl_Menu_Item menu_bar_type_menu[];
/**
\brief Manage all types on menu items.
@ -261,13 +262,24 @@ public:
class Fl_Menu_Bar_Type : public Fl_Menu_Base_Type
{
typedef Fl_Menu_Base_Type super;
Fl_Menu_Item *subtypes() FL_OVERRIDE {return menu_bar_type_menu;}
public:
Fl_Menu_Bar_Type();
~Fl_Menu_Bar_Type() FL_OVERRIDE;
const char *type_name() FL_OVERRIDE {return "Fl_Menu_Bar";}
const char *alt_type_name() FL_OVERRIDE {return "fltk::MenuBar";}
Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE {return new Fl_Menu_Bar(X,Y,W,H);}
Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Menu_Bar_Type();}
void write_static(Fd_Code_Writer& f) FL_OVERRIDE;
void write_code1(Fd_Code_Writer& f) FL_OVERRIDE;
// void write_code2(Fd_Code_Writer& f) FL_OVERRIDE;
ID id() const FL_OVERRIDE { return ID_Menu_Bar; }
bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Bar) ? true : super::is_a(inID); }
bool is_sys_menu_bar();
const char *sys_menubar_name();
const char *sys_menubar_proxy_name();
protected:
char *_proxy_name;
};

View File

@ -1006,6 +1006,19 @@ const char *Fl_Type::callback_name(Fd_Code_Writer& f) {
return f.unique_id(this, "cb", name(), label());
}
/**
\brief Return the class name if this type is inside a Class or Widget Class.
This methods traverses up the hirarchy to find out if this Type is located
inside a Class or Widget Class. It then return the name of that class. If
need_nest is set, class_name searches all the way up the tree and concatenates
the names of classes within classes, separated by a "::".
\param need_nest if clear, search up one level to the first enclosing class.
If set, recurse all the way up to the top node.
\return the name of the enclosing class, or names of the enclosing classes
in a static buffe (don't call free), or NULL if this Type is not inside a class
*/
const char* Fl_Type::class_name(const int need_nest) const {
Fl_Type* p = parent;
while (p) {

View File

@ -54,6 +54,11 @@ int Fl_Widget_Type::is_widget() const {return 1;}
int Fl_Widget_Type::is_public() const {return public_;}
const char* subclassname(Fl_Type* l) {
if (l->is_a(ID_Menu_Bar)) {
Fl_Menu_Bar_Type *mb = static_cast<Fl_Menu_Bar_Type*>(l);
if (mb->is_sys_menu_bar())
return mb->sys_menubar_name();
}
if (l->is_widget()) {
Fl_Widget_Type* p = (Fl_Widget_Type*)l;
const char* c = p->subclass();
@ -2991,6 +2996,12 @@ void Fl_Widget_Type::write_code1(Fd_Code_Writer& f) {
f.write_c("new %s(0, 0, %d, %d", t, o->w(), o->h());
else
f.write_c("new %s(%d, %d", t, o->w(), o->h());
} else if (is_a(ID_Menu_Bar)
&& ((Fl_Menu_Bar_Type*)this)->is_sys_menu_bar()
&& is_in_class()) {
f.write_c("(%s*)new %s(%d, %d, %d, %d",
t, ((Fl_Menu_Bar_Type*)this)->sys_menubar_proxy_name(),
o->x(), o->y(), o->w(), o->h());
} else {
f.write_c("new %s(%d, %d, %d, %d", t, o->x(), o->y(), o->w(), o->h());
}