unit wWindowBase0;

{ Feel free to use this unit as you want.
  First developped by Bernard Gisin

  To group what is common to every window.
  For example the handle of the window

  Treatments of Child object windows
  """"""""""""""""""""""""""""""""""
  We know that when a window is destroyed (having closed it or with
  DestroyWindow(...)), Windows automatically destroys its child windows.
  It would be natural that all its child object windows would be
  disposed also.
  The parent has pointer on all of its children
  in the property : parpChild, a pointer on an array of children.

  But some pointers on child object windows will have been allocated with
  the  new(poW..., Init)  procedure, either before or after the Parent
  object window, other could have been defined as global variable, other
  could have been defined as member (= property) of the parent object,
  and finally, other could have been defined as member of another
  object.
  We should  dispose  the child object window only if it was created
  with the  new(...)  procedure.
  How can we distinguish between all these cases ?
  First it's usually better to have pointer on objects than only objects.
  We will see that we can easily know that a child object is a member
  of its parent, and thus should not be  disposed.
  I don't know how to automatically distinguish the other cases with
  certitude. Thus you MUST avoid using child object not being members of
  their parent, or not being created with the  new(...)  procedure.
  ----------------------------------------------------------------}

// To tell FreePascal that the application is a Graphical User Interface
// i.e. is running in windows, not in text mode.
{$APPTYPE GUI}

INTERFACE
//"""""""

Uses wGlobals2, Windows;

//  Parents will maintain a list of pointers on their child.
type
  PWindowBase0 = ^TWindowBase0;
  ArrayChild = array[1..10000] of PWindowBase0; // Only pointer on it will be used
  PArrayChild = ^ArrayChild;

  TWindowBase0 = object
  //"""""""""""""""""""
  wIsDone  : word;  // with a boolean type, it does not work well !?!
  // the "IsBadWritePtr" function is not sure. If it fails, the second
  // security is testing the wIsDone, to know if
  // the object was already disposed or done.
  // set to  1  at init, to  0  at done.

  hWin     : HWND;  // Handle on the window

  wWindowType : word;
  // defines the type of the window, in a descendent. It can be
  // GGLCTE_MAIN_WINDOW, GGLCTE_MAIN_MDI_WINDOW, GGLCTE_CLIENT_MDI_WINDOW,
  // GGLCTE_CHILD_MDI_WINDOW, or
  // if in [10..20], it defines a dialog window.
  // 50 is the status line.  see the definition of the constant in wGlobals2.pp
  // if bigger than 200, it defines a control window.
  // they are defined in wGlobals0.pp

  pUserData : pointer;
  // This property (=variable) is reserved for the application developper.
  // It can be used by final application.
  // It is set to NIL in the .Init constructor, and will never
  // be modified or used by the library.

  strWindowName : TSTRWinName;
  // This is used to give a name to the window if wanted. ='' by default.
  // It will be used for dialog box window, to memorise in the parameters
  // file, the position and size of the dialog box corresponding to that name.

  hParent : HWND;
  // Handle on the parent.
  // = 0 for the main window or when it has no parent.

  poParent : PWindowBase0;
  // Pointer on the parent.
  // NIL 0 for the main window.
  // It might be NIL, and have a parent.
  // Thus test hParent to be sure if there is no parent.
  // For example, for the moment, there are no MDI Client objects.
  // Thus MDI child window have no pointer on its parent,
  // only a handle on its parent.
  // It has a pointer on its grand-parent.
  // poParent^.hWin = hParent always, if poParent <> NIL

  nMaxArrayOfChild, // Maximum index of papoChild. Memory has not been allocated for more, yet
  nNumberOfChild : integer;  // = 0 by default, (the case for controls)
  // Number of child object windows
  // It could be lower than the number of child windows, because some
  // could have been created by Windows.

  papoChild : PArrayChild;
  // = NIL if no children, as is the case for controls.
  // Point on an array of pointer on children

  constructor Init(poParentW:PWindowBase0);
  destructor  Done; virtual;

  procedure WindowDestroy; virtual;
  // This destroy the associated window, but keeps the object.

  procedure WMFromParent(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM); virtual;
  // Messages sent from a parent to all its children.

  procedure WMToAllChildren(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM); virtual;
  // In some cases, the Windows Messages are sended to their
  // Children. For example, WM_Size messages

  procedure UpdateFromVar; virtual;
  // If you want that each control Update its content with the value of its
  // linked variable, just call this method.
  // If the control receive it, it will update itself.
  // If the dialog receive it, it will update all its child control.
  // If the window owning the dialg receive it, all its dialog will
  // ask their child control to be updated.

  procedure InitDialogBegin(poDlg:PWindowBase0); virtual;
  // At the entrence of the dialog initialisation, its handle has been created,
  // but its child objects has still not created their window, and
  // thus has no handle.
  // the dialog calls this method with a pointer on itself. (it's his parent method)
  // Its a good place to modify any control before its creation.
  // Is destinated to be overriden, does nothing by default.

  procedure InitDialogEnd(poDlg:PWindowBase0); virtual;
  // When a dialog box has been filled with its
  // Control windows, and is ready to be shown.
  // the dialog calls this method with a pointer on itself. (it's his parent method)
  // Its a good place to fill list boxes if wanted, or
  // modify any control after its creation of their window (handle).
  // Is destinated to be overriden, does nothing by default.

  procedure CtrlVarUpdate(poCtrl:PWindowBase0; pVar:pointer); virtual;
  // When a control changed its value and it is linked to a variable and
  // it belongs to a dialog belonging to a window, then
  // the control calls this method with a pointer on itself and
  // a pointer on the associated variable. (it's his grand parent method)
  // Either you know the type, or you can get it with
  // pControl(poCtrl)^.rVarLinkInfo.wVarType
  // Is destinated to be overriden, does nothing by default.

  procedure CtrlLooseFocus(poCtrl:PWindowBase0; var wAcceptQuit:word32); virtual;
  // If a control is about to loose its focus, then the grand parent window
  // which is usually the main window, will receive this message with
  // poCtrl = a pointer on the control loosing the focus.
  // If the value of  wAcceptQuit  is set to  0
  // then it is refused that the control loosed its focus
  // and it will keep it.
  // For example,  wAcceptQuit:=0;
  // will reject the possibility that the control looses the focus.
  // Is destinated to be overriden, does nothing by default.

  procedure ButtonClicked(poCtrl:PWindowBase0; wCtrlID:Word32); virtual;
  // When a button is clicked, and it is in a dialog which belongs to a Window,
  // then the button calls this method, with a pointer on itself
  // and the ID it has been given. (it's his grand parent method)
  // Is destinated to be overriden, does nothing by default.

  procedure AskAcceptDlgQuit(poDlg:PWindowBase0; wCtrlIDP:word32; var wRep:word32); virtual;
  // If a dialog box is about to quit with return, i.e.
  // accepting its changes, then this dialog parent's method is called
  // poDlg is a pointer on the dialog box.
  // wCtrlIDP = id used to quit the dialog
  // If the value of wRep is set to  0
  // then it is refused to quit the dialog box.
  // For example, modifying  wRep:=0;
  // will reject the possibility to quit the dialog box.
  // Is destinated to be overriden, does nothing by default.

  procedure DialogAllRestored(poDlg:PWindowBase0); virtual;
  // When a dialog box is ended with a Cancel, all variables associated
  // to controls are set back to their original values.
  // Thus the dialog box calls this method with a pointer on itself.  (it's his parent method)
  // Is destinated to be overriden, does nothing by default.

  procedure WindowDestroyAllChild; virtual;
  // Normally Windows destroy automatically all child windows.
  // But this is not the case for popup windows.
  // And this can not be the case if the window is only a
  // child object window, but not a child window concerning Windows.

  procedure AddChildObjectWindow(poChildW:PWindowBase0); virtual;
  // Add a child object window to the child list.
  // Usually not overridden

  procedure RemoveChildObjectWindow(poChildW:PWindowBase0); virtual;
  // Remove a child object window to the child list.
  // Usually not overridden

  procedure GetWindowPosSize(var nLeft, nTop, nWidth, nHeight:integer); virtual;
  // returns the size and position of the window

  function GetObjectMenu:pointer; virtual;
  // Will be overridden by the TWindow and TMDIWindow
  // to return a pointer on the menu.
  // for the moment, return NIL

 { procedure SetWindowPosSize(nLeft, nTop, nWidth, nHeight:integer); virtual;  missing a fRepaint:boolean parameter ?}
  // sets the position and size of the window
  end; // TWindowBase0 = object
  TWindowBase =  TWindowBase0;
  PWindowBase = ^TWindowBase;

procedure gglGetPointerObject(hWinPrm:HWND; var wType:word; var poWin:PWindowBase0);
//==================================================================================
// Given the handle on a window, returns the pointer on the corresponding object window
// if it was created with this library.
// If fails, return a NIL pointer.
// in  wType  returns the type of the window, which can be one of the
// constant  GGLCTE_MAIN_WINDOW  or one other declared in  wGlobals2.pp
// Thus from a pointer on the object, you get the handle on the window
// with the  .hWin  property
// Having the handle on the window, you get the pointer on the
// corresponding object with this procedure

procedure gglGetPointerObjectWinCtrl(hWinPrm:HWND; var wType:word; var poWin:PWindowBase0);
//=========================================================================================
// This is a little more genera than  gglGetPointerObject.
// Given the handle on any window, a normal or a control windos,
// returns the pointer on the corresponding object window
// if it was created with this library.
// If fails, return a NIL pointer.
// See comments of  gglGetPointerObject

const
  // Pointer on the main application window
  // It points to the same value as  gglpoMainWindow  defined in TWindo0.pp
  // but in order to not have units including themselves circularly,
  // This pointer copy can be used.
  // Normally, there is no problems including circularly the units
  // in the IMPLEMENTATION part, but I had severals compilation runtime errors
  // when I did it. Specially when the wStatusLine0.pp
  // included the wWindow.pp unit.
  gglpoMainWindowCopy : PWindowBase0 = NIL;

IMPLEMENTATION
//""""""""""""

uses wErrors0, wDebug0;

var nNotUsed : integer; // only to avoid an error message while compliling in DevPascal 1.9

procedure gglGetPointerObject(hWinPrm:HWND; var wType:word; var poWin:PWindowBase0);
//==================================================================================
// Given the handle on a window, returns the pointer on the corresponding object window
// if it was created with this library.
// If fails, return a NIL pointer.
// in  wType  returns the type of the window, which can be one of the
// constant  GGLCTE_MAIN_WINDOW  or one other declared in wGlobals0.pp
// Thus from a pointer on the object, you get the handle on the window
// with the  .hWin  property
// Having the handle on the window, you get the pointer on the
// corresponding object with this procedure
var
  wL, wH      : word;

Begin
poWin :=NIL;
wType :=0;  // default value if faild.

// Test if the signature is correct.
// This is a security, probably useless
wL:=GetWindowWord(hWinPrm,  0); //  LOWORD
wH:=GetWindowWord(hWinPrm,  2); //  HIWORD
if (MakeLong(wL, wH) <> GGLCTE_SIGNATURE_WND_EXTRA) then exit;

wL:=GetWindowWord(hWinPrm,  4); //  LOWORD
wH:=GetWindowWord(hWinPrm,  6); //  HIWORD
if (MakeLong(wL, wH) <> GGLCTE_SIGNATURE_WND_EXTRA) then exit;

// Type of the window.
wType:=GetWindowWord(hWinPrm,  8);

// Pointer on the corresponding object window.
wL:=GetWindowWord(hWinPrm, 12); //  LOWORD
wH:=GetWindowWord(hWinPrm, 14); //  HIWORD
poWin:=Pointer(MakeLong(wL, wH));
end;  // gglGetPointerObject

procedure gglGetPointerObjectWinCtrl(hWinPrm:HWND; var wType:word; var poWin:PWindowBase0);
//=========================================================================================
// This is a little more genera than  gglGetPointerObject.
// Given the handle on any window, a normal or a control windos,
// returns the pointer on the corresponding object window
// if it was created with this library.
// If fails, return a NIL pointer.
// See comments of  gglGetPointerObject
var
  poCtrl      : PWindowBase0;

Begin
gglGetPointerObject(hWinPrm, wType, poWin);

if (poWin <> NIL) then exit;  // point on a normal window

// The problem is that the handle doesn't identify always the control.
// only controls that has been defined in the library.
poCtrl:=Pointer(GetWindowLong(hWinPrm, GWL_USERDATA));

// If the pointer is valid and represent the same handle, then
// it is a pointer on the object corresponding to the control.
if (not IsBadReadPtr(poCtrl, sizeOf(TWindowBase0))) then begin
   if (poCtrl^.hWin = hWinPrm) then begin
      // the control has been identified to be the one having the right handle
      poWin:=poCtrl;
      wType:=poWin^.wWindowType;
      end;
   end;

// else, poWin = NIL, with wType = 0 are returned.
end;  // gglGetPointerObjectWinCtrl

constructor TWindowBase0.Init(poParentW:PWindowBase0);
//====================================================
begin
// Memorise who is the parent object window.
poParent:=poParentW;

wIsDone  :=1;
hWin     :=0;
hParent  :=0;

wWindowType   :=0; // default values, redefined in descendent.

strWindowName :=''; // no name by default.

pUserData     :=NIL; // to be used by the application creator.

// No chiildren by default.
nMaxArrayOfChild :=0;
nNumberOfChild   :=0;
papoChild        :=NIL;
end;  // TWindowBase0.Init

destructor  TWindowBase0.Done;
//============================
// Disposes all child object windows, if not already done and
// if they have been created with the  new(..) procedure.
// In any case, call the destructor if not already done.
var
  nChild      : integer; // count the children
  nDiff       : int32; // Store the address of Child - Parent
  hWinMemL    : HWND;

begin
wIsDone :=0; // do not dispose again.

// Disposes all of its child object windows.
// Loop on all children
   for nChild:=nNumberOfChild downto 1 do begin
      // Verifications can be done :
      // IsBadWritePtr(poChild, sizeOf(TWindowBase))
      // must be false to have a valid pointer.
      // poChild^.poParent = @Self must be right to have a valid pointer.
      if (not IsBadWritePtr(papoChild^[nChild], sizeOf(TWindowBase))) then begin
         // Test if it was created with new or not. c.f. tEx021.pp for test
         // If the address of the pointer of the children is bigger than
         // the one of the parent, but their difference is
         // smaller than the parent size, then the child is a member of
         // the parents, and thus was not created with the 'new(...) procedure.
         nDiff:=int32(papoChild^[nChild]) - int32(@Self);

         if (nDiff > 0) and (nDiff < SizeOf(Self)) then begin
            // Child is a member of the parent, do not dispose, but
            // call the destructor  Done.
//            DBW0(1, 'Is a member, type=' + ITS(papoChild^[nChild]^.wWindowType));
            if (papoChild^[nChild]^.wIsDone = 1) then papoChild^[nChild]^.Done;
            end
         else begin
            // Child has probably been created with new(...).
            // If not, it is a programming error.
            // Childs should either be memeber of their parents, or
            // better, have been created with the 'new(...)' procedure.
//            DBW0(1, 'Done, type=' + ITS(papoChild^[nChild]^.wIsDone) + ITS(papoChild^[nChild]^.wWindowType));
            if (papoChild^[nChild]^.wIsDone = 1) and
               (papoChild^[nChild]^.wWindowType > 0) then dispose(papoChild^[nChild], Done);
            end; // if (nDiff > 0) and ...
      // else do nothing. Child was already disposed
         end; // if (not IsBadWritePtr(...
      end; // for nChild:=nNumberOfChild downto 1 do begin

if (nMaxArrayOfChild > 0) then
   FreeMem(papoChild, nMaxArrayOfChild*SizeOf(PWindowBase));

if (hWin <> 0) then begin
   // If not destroyed, then destroy the window
   hWinMemL:=hWin;
   DestroyWindow(hWinMemL);
   end;

// No more child lists.
nMaxArrayOfChild:=0;
nNumberOfChild:=0;
papoChild:=NIL;

wWindowType :=0;
poParent    :=NIL;
hParent     :=0;
hWin        :=0;
end;  // TWindowBase0.Done

procedure TWindowBase0.WindowDestroy;
//===================================
// This destroy the associated window, but keeps the object.
begin
// ignore the success or failure of destroying.
if (hWin <> 0) then DestroyWindow(hWin);
hWin        :=0;
end;  // TWindowBase0.WindowDestroy

procedure TWindowBase0.WMFromParent(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM);
//=====================================================================================
// Messages sent from a parent to all its children.
begin
hWindow:=hWindow; wMsg:=wMsg; wPar:=wPar; lPar:=lPar; // to avoid Hints
// overridden by some descendents
end;  // TWindowBase0.WMFromParent

procedure TWindowBase0.WMToAllChildren(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM);
//========================================================================================
// In some cases, the Windows Messages are sended to their
// Children. For example, WM_Size messages
var
  nChild      : integer; // count the children

begin
// Send the message to all childrens.
// Loop on all children
for nChild:=nNumberOfChild downto 1 do begin
   // Verifications can be done :
   // IsBadWritePtr(poChild, sizeOf(TWindowBase))
   // must be false to have a valid pointer.
   // poChild^.poParent = @Self must be right to have a valid pointer.
   if (not IsBadWritePtr(papoChild^[nChild], sizeOf(TWindowBase))) then begin
      if (papoChild^[nChild]^.wIsDone = 1) then // Security
         papoChild^[nChild]^.WMFromParent(hWindow, wMsg, wPar, lPar);
      end
   end; // for nChild:=nNumberOfChild downto 1 do begin
end; // TWindowBase0.WMToAllChildren

procedure TWindowBase0.UpdateFromVar;
//===================================
// If you want that each control Update its content with the value of its
// linked variable, just call this method.
// If the control receive it, it will update itself.
// If the dialog receive it, it will update all its child control.
// If the window owning the dialg receive it, all its dialog will
// ask their child control to be updated.
var
  nChild      : integer; // count the children
begin
// Search the child object window in the list of child object.
// Loop on all children
for nChild:=nNumberOfChild downto 1 do begin
   if (not IsBadWritePtr(papoChild^[nChild], sizeOf(TWindowBase))) then begin
      // Notify all its children that they must update themselves with
      // the content of their linked variable.
      papoChild^[nChild]^.UpdateFromVar;
      end;
   end; // for nChild:=nNumberOfChild downto 1 do begin
end;  // TWindowBase0.UpdateFromVar

procedure TWindowBase0.InitDialogBegin(poDlg:PWindowBase0);
begin poDlg:=poDlg; // to avoid hints
end; // to be overriden

procedure TWindowBase0.InitDialogEnd(poDlg:PWindowBase0);
begin poDlg:=poDlg; // to avoid hints
end; // to be overriden

procedure TWindowBase0.CtrlVarUpdate(poCtrl:PWindowBase0; pVar:pointer);
begin poCtrl:=poCtrl; pVar:=pVar; // to avoid hints
end; // to be overriden

procedure TWindowBase0.CtrlLooseFocus(poCtrl:PWindowBase0; var wAcceptQuit:word32);
begin poCtrl:=poCtrl; wAcceptQuit:=wAcceptQuit; // to avoid hints
end; // to be overriden

procedure TWindowBase0.ButtonClicked(poCtrl:PWindowBase0; wCtrlID:Word32);
begin poCtrl:=poCtrl; wCtrlID:=wCtrlID; // to avoid hints
end; // to be overriden

procedure TWindowBase0.AskAcceptDlgQuit(poDlg:PWindowBase0; wCtrlIDP:word32; var wRep:word32);
begin poDlg:=poDlg;  wCtrlIDP:=wCtrlIDP;  wRep:=wRep; // to avoid hints
end; // to be overriden

procedure TWindowBase0.DialogAllRestored(poDlg:PWindowBase0);
begin poDlg:=poDlg; // to avoid hints
end; // to be overriden

procedure TWindowBase0.WindowDestroyAllChild;
//===========================================
// Normally Windows destroy automatically all child windows.
// But this is not the case for popup windows.
// And this can not be the case if the window is only a
// child object window, but not a child window concerning Windows.
var
  nChild      : integer; // count the children

begin
// Destroys all of its child windows, if not already done.
// Loop on all children
for nChild:=nNumberOfChild downto 1 do begin
   // Verifications can be done :
   // IsBadWritePtr(poChild, sizeOf(TWindowBase))
   // must be false to have a valid pointer.
   // poChild^.poParent = @Self must be right to have a valid pointer.
   if (not IsBadWritePtr(papoChild^[nChild], sizeOf(TWindowBase))) and
      (papoChild^[nChild]^.hWin <> 0) and
      (papoChild^[nChild]^.wWindowType <> GGLCTE_StatusLine) then begin
      // destroy  its child window, except for Status line,
      // because they are handeled by Windows.
      DestroyWindow(papoChild^[nChild]^.hWin);
      papoChild^[nChild]^.hWin:=0; // to be sure that it is marked 'destroyed'
   // else do nothing. Child was already disposed
      end; // if
   end; // for nChild:=nNumberOfChild downto 1 do begin
end;  // TWindowBase0.WindowDestroyAllChild

procedure TWindowBase0.AddChildObjectWindow(poChildW:PWindowBase0);
//================================================================
// Add a child object window to the child list.
var
  nn           : integer;
  papoChildLoc : PArrayChild;

begin
if (poChildW = NIL) then exit; // do nothing

if (poChildW = @Self) then begin
   ErrorWrite( ERR_TWindowBase0_AddChildObjectWindow_1,
               'in TWindowBase0.AddChildObjectWindow');
   ErrorComment( ERR_TWindowBase0_AddChildObjectWindow_1,
                'This is a programming error. One try to add a children');
   ErrorComment( ERR_TWindowBase0_AddChildObjectWindow_1,
                'to a parent, which is the child itself.');
   ErrorComment( ERR_TWindowBase0_AddChildObjectWindow_1,
                'An object window is not allowed to be a parent of itself.');
   ErrorComment( ERR_TWindowBase0_WindowDestroyAllChild_1,
                  'Thus it is not added to the child list.');
   ErrorComment( ERR_TWindowBase0_AddChildObjectWindow_1,
                 'Type =' + ITS(poChildW^.wWindowType));
   exit;
   end;

// if it is the first one, allocate memory for an array of children
if (papoChild = NIL) then begin
   nNumberOfChild :=0;    // total number of child object windows.
   nMaxArrayOfChild:=100; // Allocate memory for 100 children
   GetMem(papoChild, nMaxArrayOfChild*SizeOf(PWindowBase));  // no memory check !!!
   end;

if (nNumberOfChild >= nMaxArrayOfChild) then begin // should never be ">"
   // The limit of  nMaxArrayOfChild  has been reached,
   // Allocate more memory
   GetMem(papoChildLoc, (nMaxArrayOfChild+100)*SizeOf(PWindowBase));

   // Copy the old array in the new one
   for nn:=1 to nNumberOfChild do papoChildLoc^[nn]:=papoChild^[nn];

   // Dispose the old array
   FreeMem(papoChild, nMaxArrayOfChild*SizeOf(PWindowBase));

   // Remember the new list, and its maximum index
   papoChild:=papoChildLoc;
   nMaxArrayOfChild:=nMaxArrayOfChild+100;
   end;

// the number of children has increased.
inc(nNumberOfChild);

// Add the child in the list.
papoChild^[nNumberOfChild]:=poChildW;
end;  // TWindowBase0.AddChildWindowWindow

procedure TWindowBase0.RemoveChildObjectWindow(poChildW:PWindowBase0);
//====================================================================
// Remove a child object window to the child list.
// Usually not overridden
var
  nn,
  nChild      : integer; // count the children
begin
if (poChildW = NIL) then exit; // do nothing
// if (poChildW = @Self) then exit; // cannot be in the list

// Loop on all children
for nChild:=nNumberOfChild downto 1 do begin
   if (poChildW = papoChild^[nChild]) then begin
      // Found. We remove it.
      dec(nNumberOfChild);
      for nn:=nChild to nNumberOfChild do papoChild^[nn]:=papoChild^[nn+1];
      // exit;  it should not be twice here, but let test it anyway.
      end; // if (poChildW = papoChild^[nChild]) then begin
   end; // for nChild:=nNumberOfChild downto 1 do begin
end;  // TWindowBase0.RemoveChildObjectWindow

procedure TWindowBase0.GetWindowPosSize(var nLeft, nTop, nWidth, nHeight:integer);
//================================================================================
// returns the size and position of the window
var
  rRec        : Rect;

begin
if (hWin = 0) then begin
   // if the window has no handle, it doesn't exit
   nLeft:=0;  nTop:=0;  nWidth:=0;  nHeight:=0;  exit;
   end;

GetWindowRect(hWin, @rRec);

nLeft  :=rRec.Left;
nTop   :=rRec.Top;
nWidth :=rRec.Right  - rRec.Left;
nHeight:=rRec.Bottom - rRec.Top;
end;  // TWindowBase0.GetWindowPosSize

function TWindowBase0.GetObjectMenu:pointer;
//==========================================
// Will be overridden by the TWindow and TMDIWindow
// to return a pointer on the menu.
// for the moment, return NIL
begin
GetObjectMenu:=NIL;
end;  // TWindowBase0.GetObjectMenu

End.  // unit wWindowBase0;

