From: Mattia B. <mb...@ds...> - 2002-09-06 16:39:32
|
> On Thu, 5 Sep 2002, Mattia Barbon wrote: > > (BTW subclassing wxMenuBar is unusual, why do you need it? > > just curious); > > I just did it to separate the menubar code in its own module. > For example, to do a minimal application with a menubar to > trigger a dialog, I would have 1) a main script, 2) MyApp.pm, > 3) MyFrame.pm, 4) MyMenuBar.pm, 5) MyDialog.pm, 6) MyPanel.pm, > with Frame the parent of MenuBar and Dialog, and Dialog the > parent of Panel. Maybe it's a little awkward if I have a dialog > triggered by a menu event, though, because the dialog has the > Frame for its parent and I end up doing things like this from > the MenuBar subclass > > no strict 'refs'; > EVT_MENU($parent, '...', \&{ref($parent) . 'OnMenuDialog'}); $parent->can( 'OnMenuDialog' ) looks cleaner to me, BTW > (so MyFrame isn't hardcoded, though still we have OnMenuDialog > hardcoded..) > > to pass it back to the Frame class anyway. So maybe this is > why it's unusual to subclass MenuBar. Exactly, basically, once you start putting data in the menubar, you realize that it would be much easier to put it in the frame directly. > Anyway, MenuBar is not the only scalar class I tried to > subclass. I also tried some dialogs like TextEntryDialog > and SingleChoiceDialog. You might again say it's unusual > to subclass these, as they are small and specific, but I like > to treat all the dialog as objects when I do something like > > $d = MyDialog->new(); > $d->Destroy(); > > then inside of MyDialog I did something like (it's from memory, > hopefully you get the idea though) > > sub new { > ... > > if ($self->ShowModal() == wxOK) { > $self->{textfield} = $self->GetStringSelection(); > ... > } > return $self; > } > > Basically I mean I treat the Dialog as an object which had some > attributes set by the user (as a CGI programmer, I think of the > form submit button and $q->param() to get the CGI form values), > then I use $d from above like > > if ($d->{textfield} =~ /whatever.../) { > .... > } > > and so on. (Really I use Class::Accessor::Fast and Class::Fields > modules to do easy AUTOLOAD of private and public attributes, > so instead of $self->{textfield} = ... I have $self->textfield(...) > and access simply with $self->textfield - so from the perspective > the dialog parent it seems as if $d->textfield is an accessor > method of an object.) Well, de gustibus disputandum non est... > > I can move wxMenuBar (or any other class) to subclassable (or > > to not subclassable) it just requires some small work. > > Speaking of this.. what is the trick? > > I'm trying to understand what makes the objects be > either a HASH or SCALAR reference. I grepped for 'bless' > in the source code, and I think the only relevant case is > function wxPli_make_object in ./cpp/helpers.cpp. > It creates a new HASH, but how is a SCALAR object created? it is never created, see below. > I looked at XS/Menu.xs for MenuBar which is a SCALAR, > and its `new' is barebones, just translates the C++ directly. > On the other hand, XS/Frame.xs, XS/TreeCtrl.xs, XS/Panel.xs, > those have RETVAL = new wxPliSOMETHING where SOMETHING is Frame, > TreeCtrl, Panel, etc. I search around a little -- panel.h, > helpers.h -- find these WXPLI_DEFAULT_CONSTRUCTOR and so on > all call wxPli_make_object. Aha. But still, what about the > SCALAR ones? > > There is a comment in cpp/helpers.cpp of the wxPli_sv_2_object > function in wxHashModule class that says "get 'this' pointer > from a blessed scalar/hash reference". I grepped wxPli_sv_2_object > and found it in ./typemap, but it was in the INPUT section. In the > TYPEMAP section, I found both wxMenuBar and wxFrame are O_WXOBJECT, > and in the OUTPUT section, O_WXOBJECT uses wxPli_object_2_sv. > It's hurting my mind now. :-) create a class like this (this is for wxListCtrl): class wxPliListCtrl:public wxListCtrl { WXPLI_DECLARE_DYNAMIC_CLASS( wxPliListCtrl ); WXPLI_DECLARE_SELFREF(); public: WXPLI_DEFAULT_CONSTRUCTOR( wxPliListCtrl, "Wx::ListCtrl", TRUE ); WXPLI_CONSTRUCTOR_7( wxPliListCtrl, "Wx::ListCtrl", TRUE, wxWindow*, wxWindowID, const wxPoint&, const wxSize&, long, const wxValidator&, const wxString& ); }; For wxMenuBar (which hasn't a default ctor, just 1-arg and 3-arg ones), you should use CONSTRUCTOR_1 and CONSTRUCTOR_3 (they should be added to helpers.h, it's a matter of cut'n'paste'n'modify. There are handy macros (WXPLI_DECLARE_CLASS_X) that do this in the case of a default ctor + X-args ctor, BTW. Now, why does this work? WXPLI_CONSTRUCTOR_X calls wxPli_make_object (create the hashref) and stores it in the m_self attribute of the class (declared by WXPLI_DECLARE_SELFREF() or WXPLI_DECLARE_V_CBACK()); in addition WXPLI_DECLARE_DYNAMIC_CLASS( wxPliListCtrl ); adds the class information for this class to the RTTI system used by wxWindows. Now, when wxPli_object_2_sv comes to decide how to convert the wxObject to a Perl-thing, it gets the RTTI class information and sees that the class name starts with wxPl, it assumes it is a wxPerl thing, and uses the additional information stored in the wxPliClassInfo object to locate the m_self memeber, get the hashref stored here, and return it. If that fails, it uses sv_setref_pv to create a new SCALAR reference and returns it. If you post a list of classes that you need to be HASH-ified, I think I could do that for 0.12 . Regards Mattia |