To all hmg4 core developers, an important proposal

Moderator: Rathinagiri

Post Reply
mrduck
Posts: 497
Joined: Fri Sep 10, 2010 5:22 pm

To all hmg4 core developers, an important proposal

Post by mrduck »

I already told that I think that release method should be studied a bit upon.

To me, release() should never delete the window itselft and its
objects recursively. It should never call __objDelData (if I previusly
said that it should do, I was wrong).

I try to explain why:

Imagine you have class window and 2 istances, like in:

W1 := Window():New()
W2 := Window():New()

If we add a label called "label01" to W1 internally hmg4 calls
__objAddData( w1, "label01")...

unfortunately, __objAddData (that is a internal function and should
not be called directly) adds label01 TO THE CLASS and not to the
instance !!!
You can check this with ? W2:label01 that returns NIL and doen't raise
an error !!!!!!!!!!

So, this must be taken care of, it's not really a problem, everything
is working... but programmers should know that it is unreliable to
test for method/data existance to understand which object we are
dealing...

Suppose that W2 goes out of scope earlier than W1... when it goes out
of scope, and we call __objDelData recursively... ooopss, label01
disappears ?!
mrduck
Posts: 497
Joined: Fri Sep 10, 2010 5:22 pm

Re: To all hmg4 core developers, an important proposal

Post by mrduck »

What happens if I have a program with 100 windows, each one 100
objects each one named uniquely ? If I can't use delData the class
keeps growing up to 1000 members !!!!

I think that we should take a little pause and think about it for a
moment. I ask to check what I'm saying to verify - I may be wrong...

Be able to write code with syntax like
oWindow:oLabel:value := "text"
is very nice, oop style, etc etc


Be able to write code with syntax like
oWindow:oLabel:value := "text"
is very nice, oop style, etc etc

adding oLabel as a real member to oWindow has drawbacks... the only
other solution I know of is to use the ERROR HANDLER trick.
Here a snippet from hbxbp code:


Be able to write code with syntax like
oWindow:oLabel:value := "text"
is very nice, oop style, etc etc

adding oLabel as a real member to oWindow has drawbacks... the only
other solution I know of is to use the ERROR HANDLER trick.
Here a snippet from hbxbp code:

THE FORUM SOFTWARE GIVES ERROR WHEN I PAST CODE !!!! LOOK FOR
METHOD XbpWindow:onError( ... )
IN HBXBP CONTRIB DIR

when we call

Code: Select all

oWindow:oLabel:value := "text"
Harbour VM retrieves the oWindow object, then looks for "OLABEL"
inside it. Since it is not present, it calls the CLASS error handler.
In the XbpWindow:onError( ... ) method the "message" is retrieved
(message is "OLABEL") and in this specific case the message is used as
method on another object, it works as a layer...

Since we have the "message" we can do with it what we need. My
proposal (not really mine since Luigi already did some tests
indipendently with nice results using Hash to store objects and
something I already saw used in other situations) is to use an Hash to
store the children objects.
As we said in other threads, the objects (label) is stored in 3
places. IN a variable, in a list of objects in the parent, as a data
member.
With my proposal the data member is "emulated" and the list of objects
converted from array to Hash. oWindow:oLabel syntax is still valid.
With Hashes, every object members count doesn't grow. And it's
probably way easier to handle the children objects.

There is still one problem to solve... the release() stuff...

If we want to DELETE a window we can do it without problem, Harbour VM
should take care of everything and there should be no necessity for
recursive deleting... when VM deletes the window object tha hash (that
is a member) should be deleted item by item by the VM... if it's not
the case, a destructor method will be needed...

So

Code: Select all

oWindow := NIL
should be valid and everything should made work without problems.


If we want to destroy a single (and we can't just hide it but we
should absolutely "delete it now!"), like in

Code: Select all

oWindow:oLabel := NIL
in our error handler we should intercept the NIL value and do the equivalent of:

Code: Select all

::aHash[ "OLABEL" ] := NIL // to force object orderly delete
HDel( ::aHash, "OLABEL" )  // delete the key from the hash

I know these changes will give us a little speed penalty... but they
will give us more power and control...


What do you think ?
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: To all hmg4 core developers, an important proposal

Post by Rathinagiri »

Hi Francesco,

The forum software has some bugs regarding posting some codes. Especially s u b s t r () and other functions.

I think it is not allowed to post that code because of PHP security reasons !!!
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: To all hmg4 core developers, an important proposal

Post by Rathinagiri »

I understand the problem.

The child objects should be added to instance only and not to the class.

I think it can be done via an array of child objects as DATA.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
mrduck
Posts: 497
Joined: Fri Sep 10, 2010 5:22 pm

Re: To all hmg4 core developers, an important proposal

Post by mrduck »

Today I and Luigi started working on this idea and Luigi produced some nice code. This evening we refined it a bit and implemented in hmg4 the new system based on HASHes...

I attach a basic.prg that you should AS A TEST copy over the one in your directory. Then add the line

Code: Select all

 ::StartHash()
to window():New() method, as the first line.

You will find the changes in addData and delData. Hash based implementation is at the bottom of basic.prg.
In this sample the added methods are public but they will be made protected.

The ::StartHash() line should be added to all widget source code whose New() method is overloaded and the super:new() method from basic.prg is not called (like in window.prg, where super:new() is not called...).

I tested it only on samples\slider\demo_1.prg and it is too late to do other tests. It should work on several other simple samples whose widgets call the new() of basic class, otherwise you need to add the ::StartHash() line...

Please read the code, test and report.

thanks
Francesco and Luigi
Attachments
basic.zip
(5.05 KiB) Downloaded 200 times
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: To all hmg4 core developers, an important proposal

Post by Rathinagiri »

Thanks a lot Francesco. I will check and come back.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
l3whmg
Posts: 694
Joined: Mon Feb 23, 2009 8:46 pm
Location: Italy
Contact:

Re: To all hmg4 core developers, an important proposal

Post by l3whmg »

Hi my friends.
For work reasons, I can spend little time to HMG and I'm very disappointed but I would like to leave a constructive contribution.
I hope that my words are not misinterpreted and that my English can rise to the occasion
I believe that the structure, stability and performance of the current HMG4 version is not satisfactory.

As all of you know, to propose solutions for HMG4, I developed a small fork.
It has limitations too and has various problems, but I hope that these ideas/suggestions are useful to improve HMG4.

first item: the layout of classes
I believe we must carefully observe the structure of Qt. create the necessary classes, but maintaining a flexible structure that adapts to our project. For example, I remind you what we found - Francesco and me - about the replacement of aControls.
I believe that the structure of the classes should be like or about or very similar to this
class layout
class layout
ClassLayout.jpg (65.62 KiB) Viewed 4541 times
There are many similarities with the current, but be careful "BasicQt", the two groups "A" and "B", and groups of classes based on some similarities
I believe that, in the class "BasicQt" must be included (some and useful) methods and properties of QObject and QWidget.
see( http://doc.qt.nokia.com/latest/qobject.html e http://doc.qt.nokia.com/latest/qwidget.html )
for example: the data nCol, but its method is simply "RETURN:: oQtObject: x ()". The data lEnabled, in this case QT considers this property fundamental and then the method will be

Code: Select all

METHOD Enabled( lValue ) CLASS BASICQT

   IF PCOUNT() == 0
      ::lEnabled := ::oQtObject:isEnabled()
      RETURN ::lEnabled
   ELSEIF hb_IsLogical( lValue ) == .T.
      ::lEnabled := lValue
      ::oQtObject:setEnabled( ::lEnabled )
   ENDIF

RETURN Self
second element: maintain a consistent programming style. Or use OOP or XBase classic style.
Examples currently included with HMG4 are a mixture of these styles and I believe no one thinks to write their own programs as well.
This topic is complex and takes time to be described, I believe that the examples will clarify my thoughts: they are all real.

HMG4 StatusBar

Code: Select all

FUNCTION Main

    LOCAL oButton1
    LOCAL oButton2
    LOCAL oButton3
    LOCAL oButton4
    LOCAL oStatusBar

    PRIVATE oWindow

    HBQT_ERRORSYS()

    WITH OBJECT oWindow := Window():New()
       :Row    := 10
       :Col    := 10
       :Width  := 500
       :Height := 500
       :Title  := 'Status Bar management'
       :Type   := WND_MAIN
       :OnInit := { || oWindow:Center() }

       WITH OBJECT oButton1 := Button():New()
          :Row     := 40
          :Col     := 40
          :Width   := 250
          :Caption := 'Click TO change text'
          :OnClick := { || Button1Click( 1 ) }
       END WITH

       WITH OBJECT oButton2 := Button():New()
          :Row     := 80
          :Col     := 40
          :Width   := 250
          :Caption := 'Click TO change icon'
          :OnClick := { || Button1Click( 2 ) }
       END WITH

       WITH OBJECT oButton3 := Button():New()
          :Row     := 120
          :Col     := 40
          :Width   := 250
          :Caption := 'Click TO remove icon'
          :OnClick := { || Button1Click( 3 ) }
       END WITH

       WITH OBJECT oButton4 := Button():New()
          :Row     := 160
          :Col     := 40
          :Width   := 250
          :Caption := 'Click TO add icon'
          :OnClick := { || Button1Click( 4 ) }
       END WITH

       WITH OBJECT oStatusBar := StatusBar():New("oStatusBar")
          :FontName := "Arial"
          :FontSize := 12 ; ;
          :StatusItemAdd("Example OF test",200,,":BTN_LOGIN",.F.,.T.,)
          :StatusBarTimersAdd(100,,.F.,.F.,,"C")
          :StatusBarTimersAdd(100,,.F.,.F.,,"D")
       END

    END WITH

    oWindow:Activate()
My fork StatusBar: no local vars are declared

Code: Select all

   WITH OBJECT MainForm := WINDOW():New( "MainForm" )
      :Row           := 10
      :Col           := 10
      :Width         := 600
      :Height        := 600
      :Title         := "Form title: OOP syntax"
      :Icon          := ":L3W_ICO"
      :WinType       := WND_MAIN
      :OnInit        := {|| MainFrmOnInit() }
      :ToolTip       := "This is MainForm tooltip"

      WITH OBJECT StatusBar():New( "StatusBar1" )
         :FontFamily    := "Arial"
         :FontSize      := 12

         WITH OBJECT StatusItem():New( "StatusItem1" )
            :Width      := 150
            :Caption    := "Put Text Here"
            :ToolTip    := "Click hover me"
            :OnClick    := {|| MsgStop("Clicked me") }
         END WITH

         WITH OBJECT StatusItem():New( "StatusItem2" )
            :Width      := 150
            :ToolTip    := "ToolTip Item2"
            :Caption    := "texture"
            :Height     := 18          // this is required to merge icon with text, but unusefull about height of statusbar
            :IconHeight := 12
            :IconWidth  := 12
            :Icon       := ":L3W_IMOVNEXT"
         END WITH

         WITH OBJECT StatusKeyb():New( "StatusKeyBoard" )
            :Width      := 100
            :ToolTip    := "KeyBoard Status"
         END WITH

         WITH OBJECT StatusDate():New( "StatusDate" )
            :Width      := 100
            :ToolTip    := "Current Date"
            :OnClick    := {|| InstantDate() }
         END WITH

         WITH OBJECT StatusClock():New( "StatusClock" )
            :Width      := 100
            :ToolTip    := "Current Time"
            :OnClick    := {|| InstantTime() }
         END WITH

      END WITH

   END WITH

   MainForm:Activate()
HMG4 MainMenu

Code: Select all

   LOCAL oMain
   LOCAL oMainMenu
   LOCAL oPopupFile
   LOCAL oPopupHelp
   LOCAL oPopupMore
   LOCAL oPopupEdit
   LOCAL oItemOpen
   LOCAL oItemClose
   LOCAL oItemMore1
   LOCAL oItemMore2
   LOCAL oItemMore3
   LOCAL oItemCut
   LOCAL oItemCopy
   LOCAL oItemPaste
   LOCAL oItemAbout
   LOCAL oItemSave
   LOCAL oButton1
   LOCAL oButton2
   LOCAL oButton3
   LOCAL oButton4
   LOCAL oButton5
   LOCAL oButton6
   LOCAL oButton7
   LOCAL oButton8
   LOCAL oButton9
   LOCAL oButton10

   HbQt_ErrorSys()

   WITH OBJECT oMain := Window():New()
      :Row    := 10
      :Col    := 10
      :Width  := 400
      :Height := 400
      :Title  := 'Nice OOP Demo!!!'
      :Type   := WND_MAIN
      :OnInit := { || oMain:Center() }

      * TO make the code more compact you can alternatively SET
      * MenuItem properties as parameters ON 'new' method.
      * Here the two ways will be shown
      * MenuItem 'New' method parameters:
      * ( cCaption , bOnClick , cImage , lChecked , lEnabled , lVisible )

      WITH OBJECT oMainMenu := MainMenu():New()

         WITH OBJECT oPopupFile := MenuPopup():New( 'oPopupFile',, 'File' )
            oItemOpen  := MenuItem():New( 'oItemOpen' ,, 'Open'  , { || MsgInfo( 'Open'  ) } )
            oItemClose := MenuItem():New( 'oItemClose',, 'Close' , { || MsgInfo( 'Close' ) } )

            WITH OBJECT oPopupMore := MenuPopup():New( 'oPopupMore',, 'More...' )
               oItemMore1 := MenuItem():New( 'oItemMore1',, 'More 1!' , { || MsgInfo( 'More 1' ) } )
               oItemMore2 := MenuItem():New( 'oItemMore2',, 'More 2!' , { || MsgInfo( 'More 2' ) } )
               oItemMore3 := MenuItem():New( 'oItemMore3',, 'More 3!' , { || MsgInfo( 'More 3' ) } )
            END WITH

            oItemSave  := MenuItem():New( 'oItemSave',, 'Save'  , { || MsgInfo( 'Save' ) } , , .T. )
         END WITH

         WITH OBJECT oPopupEdit := MenuPopup():New( 'oPopupEdit',, 'Edit' )
            oItemCut    := MenuItem():New( 'oItemCut'  ,, 'Cut'   , { || MsgInfo( 'Cut' )  } , 'cut.png'           )
            oItemCopy   := MenuItem():New( 'oItemCopy' ,, 'Copy'  , { || MsgInfo( 'Copy' )  } , 'copy.png'          )
            oItemPaste  := MenuItem():New( 'oItemPaste',, 'Paste' , { || MsgInfo( 'Paste' ) } , 'paste.png' , , .F. )
         END WITH

         WITH OBJECT oPopupHelp := MenuPopup():New( 'oPopupHelp',, 'Help' )
            WITH OBJECT oItemAbout := MenuItem():New( 'oItemAbout' )
               :Caption := 'Help'
               :Image   := 'Help.bmp'
               :OnClick := { || MsgInfo( 'About' ) }
            END WITH
         END WITH

      END WITH
My fork MainMenu: no local vars are declared

Code: Select all

  WITH OBJECT MainForm := WINDOW():New( "MainForm" )
      :Row           := 10
      :Col           := 10
      :Width         := 600
      :Height        := 600
      :Title         := "Form title: OOP syntax"
      :Icon          := ":L3W_ICO"
      :WinType       := WND_MAIN
      :ToolTip       := "This is MainForm tooltip"
      :OnInit        := {|| MainFrmOnInit() }
//     :EnterLikeTab  := .T.  don't work properly

      WITH OBJECT MAINMENU():New( "MainMenu" )
         :FontSize      := 12

         WITH OBJECT MENUPOPUP():New( "PopUp1" )
            :Title      := "&File"
            :ToolTip    := "PopUp1 tooltip"

            WITH OBJECT MENUITEM():New( "Action1" )
               :Caption    := "&1 Action"
               :Icon       := ":L3W_IMOVFW"
               :OnClick    := {|| MsgStop("QUI") }
            END WITH

            :AddSeparator()

            WITH OBJECT MENUPOPUP():New( "PopUp2" )
               :Title      := "&Altro"
               :Icon       := ":L3W_IMOVNEXT"
               :ToolTip    := "Altro tooltip"

               WITH OBJECT MENUITEM():New( "Action3" )
                  :Caption    := "&Close"
                  :Icon       := ":L3W_IMOVEJECT"
                  :OnClick    := {|| MsgStop("QUA") }
               END WITH

               WITH OBJECT MENUITEM():New( "Action5" )
                  :Caption    := "&Reset"
                  :OnClick    := {|| MsgStop("XXX") }
                  :Enabled    := .F.
               END WITH

               WITH OBJECT MENUITEM():New( "Action6" )
                  :Caption    := "&6 Action"
                  :OnClick    := {|| OnAction6() }
               END WITH

               WITH OBJECT MENUITEM():New( "Action7" )
                  :Caption    := "&Print"
                  :OnClick    := {|| MsgStop("ZZZ") }
                  :Visible    := .F.
               END WITH

               :EndMenu()
            END WITH    // end PopUp2

            :AddSeparator()

            WITH OBJECT MENUITEM():New( "Action2" )
               :Caption    := "&Open"
               :Icon       := ":L3W_IMOVBW"
               :OnClick    := {|| MsgStop("QUO") }
            END WITH

            :AddSeparator()

            WITH OBJECT MENUITEM():New( "Action4" )
               :Caption    := "e&Xit"
               :Icon       := ":L3W_IEXIT"
//               :OnClick    := {|| THISWINDOW:Close() }
               :OnClick    := {|| THISWINDOW:Release() }
            END WITH

            :EndMenu()
         END WITH    // end PopUp1

      END WITH

   END WITH

   MainForm:Activate()
HMG4 include file

Code: Select all

// Main Menu

   #xcommand DEFINE MAIN MENU [ <dummy1: OF, PARENT> <oParent> ]   ;
   =>                     ;
   With Object :s_oMainMenu := MainMenu():New( "s_oMainMenu" [, <oParent> ] )

   #xcommand DEFINE MAINMENU [ <dummy1: OF, PARENT> <oParent> ]   ;
   =>                     ;
   With Object :s_oMainMenu := MainMenu():New( "s_oMainMenu" [, <oParent> ] )

   #xcommand END MENU ;
   => ;
   End With

   // Popup - not named

   #xcommand [ DEFINE ] [ MENU ] POPUP [ CAPTION ] [ <cCaption> ]  ;
   => ;
   :s_cNewPopUpMenuName := :s_oMainMenu:GetPopupVarName() ;;
   With Object MEMVAR->&(:s_cNewPopUpMenuName) := MenuPopup():New( :s_cNewPopUpMenuName, , [<cCaption>] )
My fork include file: no vars are used

Code: Select all

  // BarMenu
   #xcommand DEFINE MAINMENU <oObj>[ <dummy1: OF, PARENT> <oParent> ] =>         ;
      WITH OBJECT MAINMENU():New( <"oObj"> [, <oParent> ] )
   #xcommand END MAINMENU                 => END WITH

   // Menu
   #xcommand DEFINE POPUP <oObj>[ <dummy1: OF, PARENT> <oParent> ] =>        ;
      WITH OBJECT MENUPOPUP():New( <"oObj"> [, <oParent> ] )
   #xcommand END POPUP                     => :EndMenu() ; END WITH

   // Action
   #xcommand DEFINE MENUITEM <oObj>[ <dummy1: OF, PARENT> <oParent> ] =>        ;
      WITH OBJECT MENUITEM():New( <"oObj"> [, <oParent> ] )
   #xcommand END MENUITEM                 => END WITH
These are brief examples of both: HMG4 and a different pow.

I repeat.
My words must be considered as constructive criticism for the good of the project HMG4, to which many of us have contributed and spent their free time.
please, think about what I have given you before you shoot me


Viva HMGx, viva Clipper

Cheers
Luigi
Luigi from Italy
www.L3W.it
mrduck
Posts: 497
Joined: Fri Sep 10, 2010 5:22 pm

Re: To all hmg4 core developers, an important proposal

Post by mrduck »

l3whmg wrote: second element: maintain a consistent programming style. Or use OOP or XBase classic style.
Luigi is right. We can freely mix OOP and xBase style.

OOP style is WITH OBJECT xx := window():New()
xBase style is DEFINE WINDOW xx
variant of xBase style is the one with ; at the end of the line and one without... so we need to have multiple definition... and keep them in sync !!!!

This can lead to problems. For example look at DEFINE WINDOW in hmg.ch... it is translate to (partial)

Code: Select all

With Object <oObj> := Window():New( <"oObj"> [, <oParent> ] ) ; :s_oParentWindow := <oObj>  ;;
PLEASE NOTE the assignment to :s_oParentWindow... this assignment is then used in DEFINE STATUSBAR that translate to

Code: Select all

With Object :s_oParentWindow:oStatusBar := StatusBar():New( "oStatusBar" [, <oParent> ] );;
What happens if the window is defined with OOP style and statusbar using xBase style ????


Luigi is not 100% correct on the need of variables to keep intermediate value. I don't remember why they started to appear. There were probably problems in the past, or it was just a shortcut to be able to reference them just with variable/object name...
With recent changes done (and for the reason that started this exact thread), New() automatically adds the object name as a data member of the parent and stores a reference to self there, and also adds itself to parent aControls.
So we don't need to have a local variable to keep the object "live" since there are at least 2 references to it stored in the parent... long life to the parent... !

Do windows need to have a variable ? short answer: no. Their New() adds them to hmgapp() object... so we may retrieve them starting from hmgapp().

hmgapp() iwanted to be a singleton but it was a not-really-a-singleton. I just committed a patch to make it a real singleton. Until now every time hmgapp() was called a new instance was created and this was ok was shared data members inherited from GLOBSHARED but was a pity for the standard data members from hmgapp itself... they reset at each call !
There were a couple of ways to solve the problem but I think that a singleton is the best way, it can help in the future.


I want to thank Luigi that shared his time with me in this week trying to understand how to improve hmg4., doing some tests, experimenting solutions, writing code, providing ideas and sharing his fork with me.
User avatar
l3whmg
Posts: 694
Joined: Mon Feb 23, 2009 8:46 pm
Location: Italy
Contact:

Re: To all hmg4 core developers, an important proposal

Post by l3whmg »

Hi to everyone, my friends.
Many thanks to MrDuck for his words. For reasons about space on this forum, I can't add a lot of examples or words.
To better explain my opinion, please feel this examples.

I consider the current demo programs like a "this is the fundamental of HMG4" and used to test HMG4 library and this is a little brief:

Code: Select all

WITH OBJECT Button():New("button1") 
or 
WITH OBJECT Button():New()
or 
WITH OBJECT Button1 := Button():New() this need a local declaration var
or
WITH OBJECT Button1 := Button():New("button1") this need a local declaration var

and
:row := 10
.....
END WITH

But the same is the real program written with "XBase command style"

Code: Select all

DEFINE BUTTON Button1
ROW 10
....
END BUTTON
OOP style it's very different. Please, put a little imagination to follow this example

Code: Select all

FUNCTION Main
   LOCAL oMainForm := MyMainWindow():New()
   QUIT
RETURN NIL

CLASS MyMainWindow
   DATA oForm
   DATA oButton1
   DATA oFormRow           INIT 10
   DATA oButton1Row           INIT 40
  ....
   METHOD New
   METHOD Run
END CLASS
METHOD New() CLASS MyMainWindow
   WITH OBJECT oForm := Window():New()
   or
   WITH OBJECT oForm := Window():New("myform") this is an example, but with OOP unusefull
       :Row := oFormRow
.........
      WITH OBJECT oButton1 := Button():New() or.....but is unusefull
        :Row  := oButton1Row
        ..........
      END WITH
...........
   END WITH

   ::Run()
RETURN Self

METHOD Run()
::oForm:Activate()
RETURN Self
So what I mean is: we can't mix OOP and XBase because they need a different approach. IMHO, OOP it's very powerfull, but for a long time I write with XBase style: I like it.

Everyone can see the difference between a real OOP program (very brief explanation) and demos and differences with XBase style.

So if we want preserve the ability to write programs (end program) with XBase style using HMG4, we can't introduce any var with LOCAL or within hmg.ch and we must change the class/core code approach. If we want loose the ability to write programs with XBase style using HMG4, I think the real example is the last one not the first.

About class layout it's only a suggestion, because I found it very useful to create this forest even if it is complex to administer and can create misunderstandings or problems.

Cheers
Luigi from Italy
www.L3W.it
Post Reply