I anticipated in a message of 2 days ago that I did some work on HMG3 compatibility and that I was going to commit it at the end of the month.
Today with my family and friends I was doing a walk on a steep track on the Alps and while walking I was thinking how to make HMG4 TABs compatible with HMG3 ones when, not being concetrated on the walk, I lose the grip and put my leg into the water...
Later, after lunch, I was having a little sleep and perhaps due to too much food I had, I started to dream about me at my office pc changing HMG code...
... so I decided it was too much...
I decided to stop further study and coding on HMG3 compatibility and concentrate to make HMG4 a stable and realiable tool. I don't have HMG3 code after all....
I had committed a bunch of changes and I ask everybody to have a look at it. There are some issues open and some closed, both about HMG3 compatibility and HMG4. I'm going to list them, adding some considerations... this list is a way to document my studies and my ideas, at least for my own memory.
a) HMG3 is a preprocessor matter
HMG3 strenght is based on heavy preprocessor use. When a window is created
Code: Select all
DEFINE WINDOW xyz ...
Code: Select all
xyz.widget.setfocus
Code: Select all
doMethod( "xyz", "widget", "setfocus" )
Code: Select all
xyz:widget:setfocus()
Code: Select all
eval( {||xyz:widget:setfocus()} )
I left it in hmg.ch just to document it in a commit and can be probably removed later.
With doMethod, HMG3 uses also set/getProperty and again the preprocessor code created on the fly should be changed to comply with HMG4 style.
In include/hmg3.ch file in svn (that originates from HMG3) you will find the code used as template used at runtime and will find some defines updated and some not yet.
b) HMG3 has "alternative" commands
It seems that every widget in HMG3 has at least a couple of possible ways to be defined... with strange situations... I didn't check if it was an error of the people who ported DEFINE WINDOW from 3 to 4 but in HMG4 if you want a MODAL window you must use ONINIT, if you want a MAIN window you must use ON INIT....
Let's see an example, for LABELs, in HMG3 we have (I copy only the first line) only two ways, the xBase style and "oneliner" style:
Code: Select all
// xBase style, command on one row
#command @ <nRow>,<nCol> LABEL <oObj> ;
// "oneliner" style, every command is on a line of its own
#xcommand DEFINE LABEL <name> =>;
In HMG4 we have a new way I was not able to find in HMG3 and the OOP way... the OOP way is the "internal" way to create the objects but since it is OOP, easy to use, and present in almost all the samples, it was actually made public.
Code: Select all
// new way to create a label
#command @ <nRow>,<nCol> SAY <oObj> ;
// OOP way
With Object Label():New( x,y )
c) a bit of HMG4 history
When HMG4 started widget creation followed this pattern:
With Object Label():New( ) created a Label object,
:row = 20
the setget method checked the ::lCreated variable and depending on its value stored 20 in a member variable (::nRow) or actually called hbQt method to pass the value to the object.
When the enclosing window was activated, each widget :create() method was called and in this method the hbQt object was created and the values stored in DATA members were used to call again the setget method but now the ::lCreated value was different and the value actually used to configure the widget. It was a 2 step process that had some issues and seemed to be full of redundant, difficult to mantain code...
So there was a change: now the hbQt object is created during the New() method and almost all parameters are applied directly to the object, only in few special cases the :create() method is overloaded from basic.prg to handle special situations (see textbox.prg)
Probably dropping usage of ::create() and ::lCreated was an error....
d) give me :create() back
I don't want fulle :create() back, but only part to handle certain situations...
let's see why
In HMG3 using xBase or "oneliner" definitions was almost the same, infact both loaded _HMG_SYSDATA array with parameters value and then called the proper _DefineXXX function, for example, _DefineCombo. Inside this function the library author was able to do all the thinks needed IN THE CORRECT SEQUENCE...
Actual HMG4 code, like explained in point c), directly operates on hbQt objects, so that NO CORRECT SEQUENCE IS GUARANTEED when using oneliner or OOP style !
Example (just the needed code shown):
Code: Select all
DEFINE COMBOBOX cb_country
ON CHANGE {|| lbl_descr:text = aCountry[ cb_country:value ] }
ITEMS aCountry
END COMBOBOX
DEFINE LABEL lbl_descr
I already described this problem in the past because in the first implementation of HMG4 the create() method first applied the onchange codeblock and then assigned the items.. it was just a matter of changing order to solve the problem. Now with the new implementation I was forced to check for ::lCreated = .T. before calling the codebock...
A similar problem happens with SPLITBOX, where using the PLACEMENT command the splitbox is moved based on the first children... so using this command before creating the child gives error... Splitbox already has a create() method and so this parameter must be moved there..
e) about TAB and TAB PAGE
There are several differences between HMG3 and 4 on TAB and TAB PAGEs.
The first problem arise from the command definition
Code: Select all
// HMG3
#command PAGE <caption> [ IMAGE <image> ] ;
//HMG4
#command PAGE <oObj> [ IMAGE <cImage> ] [ <dummy1: OF, PARENT> <oParent> ];
=>;
With Object &(<oObj>) := TabPage():New( <oObj>[ , <oParent> ] ) ;;
Code: Select all
DEFINE WINDOW w
DEFINE TAB t
DEFINE PAGE p
DEFINE LABEL l
// HMG3 accessing label l
w:l:text = "text"
// HMG4 accessing label l
w:t:p:l:text = "text"
I was thinking how to have both possibilities when I put my leg into the water...
For example using oParent:oParent:oParent:addData(...) when HMG3 legacy code is compiled (-DHMG3) and proper definition of PAGE <par> depending on it, so that in HMG4 you must specify a name in TAB and PAGE.
I just put the titles and summary of next points
f) HASH and ON ERROR
now we use ON ERROR and a HASH to keep track of childrens. it solves some kind of problems but it may be a bit slower.
see in basic.prg
g) killing aControls
it was used to keep (another) reference to object children for some special pourpose. Now the Hash also handle this.
h) don't show that window (or, long life to ::lCreated)
if VISIBLE .T. (or :visible = .T.) was present in the window definition of a HMG4 program it appeared on the screen immediately but the children widgets created when the window was on screen were not made visible by Qt and you are forced to add a :show() to have them visible...
This is another aspect of point d): there are some cases where methods or values may be set only in a specific sequence
i) About object names, killing LOCALs, and reference counts...