unit wDialog0;

{ Feel free to use this unit as you want.
  First developped by Bernard Gisin
  This unit uses object programming to simplify the developpment
  of programs.

  To create dialog boxes, without using resources.
  It will simplify creating dialog boxes, with a simple description
  in a text file.
----------------------------------}

// 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 wControl0, wWindowBase0, wGlobals2, wGlobals0, windows;

type
  PDialog0 = ^TDialog0;
  TDialog0 = object(TWindowBase)
  //""""""""""""""""""""""""""""
//  hWin    : HWND;  // Handle on the dialog, got from TWindowBase
//  hParent : HWND;  // Handle on the parent, got from TWindowBase
//  poParent: PWindowBase0;  // Pointer on the parent, got from TWindowBase

  strDlgTitle : string[200];
  // title of the dialog, when it is created
  // Memorise the strWinTitlePrm parameter of the init constructor.

  nXPosPix,  // X position in pixels in the parent window, = 0 if not used
  nYPosPix,  // Y position in pixels in the parent window, = 0 if not used
  nXSizePix,  // X size in pixels, = 0 if not used
  nYSizePix  // Y size in pixels, = 0 if not used
      : int32;
  // Position and size of the dialog when created

  dwStyle : word32;
  // Style of the dialog.
  // Seldom changed

  dwExStyle : word32;
  // Extra Style of the dialog.
  // Seldom changed

  // The dialog Box keeps a list of all object child windows,
  // i.e. of all object controls. For this, an array of pointer is
  // used. It is defined in TWindowBase.
  // Thus automatic children notification will be done,
  // automatic disposing of the children will be done, at
  // the dispose of the Dialog box.
  // The Windows  EnumChildWindows  function could have been used,
  // but it would complicate the treatments.

  wMsgDescriptor : Word32;
  // If the dialog was generated from a description in a  .msg  file
  // starting at a specifique number, then
  // the number is stored in this variable, for reference.

  nDlgReturnValue : Int32;
  // Value returned. It is tested at the very end, when the dialog is destroyed

  awIDExit : array[1..6] of Word32;
  // Six button id which can be used to quit the dialog,
  // accepting the modifications.
  // by default they all equal IDOK
  // IDCANCEL (=2) is the only id which quits the dialog,
  // without accepting the modifications.

  constructor Init(poParentW:PWindowBase);
  // Call Init; at the beggining of a new Init constructor.

  destructor  Done; virtual;
  // Call Done; at the end of a new destructor.

  function DialogExec:int32; virtual;
  // Execution of the dialog
  // Can be overriden, but not overriden usually

  procedure DialogCreate; virtual;
  // Creates a new Modeless Dialog.
  // Returns the handle on the dialog in  hWin.

  function GetControlFromID(wIDP:word32):PControl; virtual;
  // Given an ID, return a pointer on the control having this ID.
  // Return NIL if none has this ID
  // Try only with child object controls from the dialog box.

  function GetControlFromVar(var AnyVar):PControl; virtual;
  // Given a variable, return a pointer on the control linked to it.
  // Return NIL if none is linked to it.
  // Try only with child object controls from the dialog box.

  procedure NotifyEndDialogToCtrl(nReturnValue:int32); virtual;
  // Notify all its child control that the dialog is about to be Ended.
  // with the return value : nReturnValue
  // If it is a IDCANCEL, variables associated to the controls will
  // be reset to their original values.

  procedure DialogEnd(nReturnValue:int32); virtual;
  // When a dialog box should be closed, call this method.
  // Usually not overriden.

  function AcceptToQuit(wCtrlIDP:Word32):boolean; virtual;
  // Quitting the dialog has been requested, with id wCtrlIDP,
  // Ask the prent window if it accept to quit.

  function WMClose:BOOL; virtual;
  // By default, if the close upper right button has been pressed,
  // the dialog box closes and return a IDCANCEL (=2) value.
  // Seldom overriden.

  function WMNotify(wPar:WPARAM; lPar:LPARAM):BOOL; virtual;
  // Handles the notification messages send from controls.
  // It is usually overriden, if used.
  // By default does nothing. Thus it is useless to call the
  // inherited WMNotify(wPar, lPar)  from the descendent.
  // Should return TRUE if the message was processed, FALSE otherwise.
  // But the return value is ignored by Windows.

  function WMCommand(wPar:WPARAM; lPar:LPARAM):BOOL; virtual;
  // Does know something...
// --> Is nearly always overriden, to handles notification messages.
  // By default does nothing. Thus it is useless to call the
  // inherited WMCommand(wPar, lPar)  from the descendent.
  // Should return TRUE if the message was processed, FALSE otherwise.
  // But the return value is usually ignored by Windows.

  function WMCTLCOLOR(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT; virtual;
  // Special case where controls colors can be defines by their parent.
  // The controls can define the colors directly through this call.
  // lPar = handle on the control who wants to be redrawn
  // wPar = Device context used to draw the control.

  function DialogProc(hDialog:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT; virtual;
  // Handles the dialog messages
  // It is seldom overriden, since most (all) messages are
  // treated in  WMCommand.
  // Should return LRESULT(TRUE) if the message was processed,
  // LRESULT(FALSE) otherwise.
  // Noramly, the return value should be TRUE or FALSE.
  // But if the dialog color is changed, the return value is a handle on
  // a brush, this is why a LRESULT is returned, and not a BOOL.
  // (In C language, BOOL and LRESULT are the same type, not in pascal)

  procedure WMNotifyFirst(wPar:WPARAM; lPar:LPARAM); virtual;
  // Handles the notification messages send from the controls
  // It is seldom overriden.
  // It is send before the  DialogProc  receive the WM_NOTIFY

  procedure WMCommandFirst(wPar:WPARAM; lPar:LPARAM); virtual;
  // Handles norification messages
  // It is seldom overriden.
  // It is send before the  DialogwProc  receives the WM_COMMAND

  procedure WMInitDialogFirst; virtual;
  // Can be overriden, but it's not usual.
  // Create the windows of all its object control.
  // The first call to the dialog box is done here.
  // At this place, we add the control windows in the dialog box.
  // It is here, that we determine really the controls, style etc.
  // of the dialog box

  function DialogProcFirst(hDialog:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT; virtual;
  // Handles messages for the library, before calling  DialogProc
  // It is usually not overriden

// To come later :
// hControl :=GetDlgItem(hWin, nID_Control); to get the handle of the control
// nLen :=GetDlgItem(hWin, nID_Control, @strS[1], length(strS)-2);
//   strS[0]:=char(nLen);
//   to get the text of the control, typically an edit box or combo box
// nValue :=GetDlgItemInt(hWin, nID_Control, @fSuccess, fSigned);
// to get the integer value of a control.

  end; // TDialog0 = object(TWindowBase)
  TDialog =  TDialog0;
  PDialog = ^TDialog;

const
  // Pointer on the active dialog if there is one.
  gglpoActiveDialog : PDialog0 = NIL;

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

uses // wStringDataLink2, reported as not being used, but it is used !?!
     wParamApplic2, wErrors0 ,wDebug0;

var glnNotUsed : integer; // To avoid an error in the DevPascal 1.9 environment

Function TheDialogProc(hDialog:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT; stdcall; export;
//=================================================================================================
// This is the message handle function, which calls the
// 'DialogProc' function
var
  // corresponding object dialog to the  hDialog  handle
  poDlg       : PDialog0;

Begin
// Gets the adress of the object corresponding to this control.
poDlg:=Pointer(GetWindowLong(hDialog, GWL_USERDATA));

{if (wMsg <> WM_MOUSEMOVE) and (wMsg <> WM_SetCursor) and
   (wMsg <>  WM_NCHITTEST) then
   DBW0(1024, DBMsgToStr(wMsg) + ITS(wPar) + ITS(lPar) + PTS(poDlg)); }

if (poDlg = NIL) or IsBadWritePtr(poDlg, sizeOf(TDialog)) then begin
   // The first call to a dialog enters here.
   // poDLG = NIL & wMsg = WM_INITDIALOG

   if (gglpoActiveDialog = NIL) or
      IsBadWritePtr(gglpoActiveDialog, sizeOf(TDialog)) then begin
      // if it is stll invalid, quit the dialog box
      gglpoActiveDialog:=NIL; // secutity, this pointer is invalid

      // Arriving here is a difficult error to handle !!!

//      EndDialog(hDialog, -2); // -2 => Error trying opening the dialog
      // -1 is returned from Windows, if the dialog could not be created

      TheDialogProc:=ord(FALSE);
      exit;
      end
   else begin
      // The pointer on the object dialog has still not been associated
      // Memorises the pointer on the object dialog, in the
      // dialog created by windows, in order to reuse it.
      SetWindowLong(hDialog, GWL_USERDATA, longint(gglpoActiveDialog));

      poDlg:=gglpoActiveDialog;
      end;
   end;

// The object dialog method is called.
TheDialogProc := poDlg^.DialogProcFirst(hDialog, wMsg, wPar, lPar);
End;  // TheDialogProc

//***************************************************
// TDialog0 = object(TWindowBase)
//***************************************************

constructor TDialog0.Init(poParentW:PWindowBase);
//===============================================
begin
inherited Init(poParentW);

// By default it is Modal.
// If Modeless, then changed in DialogCreate.
wWindowType :=GGLCTE_A_DIALOG_Modal; // def. in wGlobals2.pp

// add the dialog to the parent's child list.
if (poParent <> NIL) then poParent^.AddChildObjectWindow(@Self);

strDlgTitle:='';

// Default position and size of the dialog when created
nXPosPix  :=5;
nYPosPix  :=5;
nXSizePix :=500;
nYSizePix :=300;

// Default style of the dialog box
dwStyle   :=DS_MODALFRAME or WS_POPUP or WS_CAPTION or WS_SYSMENU;
dwExStyle :=0;

wMsgDescriptor:=0;
nDlgReturnValue:=0;

awIDExit[1] :=IDOK;
awIDExit[2] :=IDOK;
awIDExit[3] :=IDOK;
awIDExit[4] :=IDOK;
awIDExit[5] :=IDOK;
awIDExit[6] :=IDOK;
end;  // TDialog0.Init

destructor TDialog0.Done;
//=======================
begin
// remove it from the list of child Windows of its parent.
if (poParent <> NIL) then poParent^.RemoveChildObjectWindow(@Self);
poParent:=NIL;

if (gglpoActiveDialog = @Self) then gglpoActiveDialog:=NIL;

inherited Done;
end;  // TDialog0.Done

function TDialog0.DialogExec:int32;
//=================================
// Creates a new Modal Dialog.
var
  rTemplate   : record
                rDlg   : DLGTEMPLATE;
                wMenu  : word; // = 0 => no menu       2 bytes variable
                wClass : word; // = 0 => default class 2 bytes variable
                wTitle : word; // = 0 => no title
                end;
  nRet        : int32;
  wD1,wD2,wD3,wD4,wD5, // data associated with the dialog file name
  wErrorL     : Word32;

Begin
// It is supposed that the poParent is a valid pointer, if non NIL.
if (poParent <> NIL) then
     hParent :=poParent^.hWin // both are always the same
else hParent:=0;              // A dialog box without a parent !

// If the dialog has a name, then its size and position can be associated
// with it in the String - Data Link file.
if (strWindowName <> '') and (gglpoPrm <> NIL) then begin
   gglpoPrm^.oSDL.GetStringDataW5(strWindowName, wD1,wD2,wD3,wD4,wD5,wErrorL);
   if (wErrorL = 0) then begin
      // Position set by the last position of the dialog
      // But size remains as computed (or set) otherwise.
      nXPosPix  :=wD1;
      nYPosPix  :=wD2;
//      nXSizePix :=wD3;
//      nYSizePix :=wD4;
      end;
   end; // if (strWindowName <> '') then begin

// tell who is the active Dialog.
gglpoActiveDialog := @Self;
with rTemplate.rDlg do begin
   dwExtendedStyle :=0;
   style  := DS_MODALFRAME or WS_POPUP or WS_CAPTION or WS_SYSMENU
             or WS_TABSTOP;
   cdit   :=0;  // number of controls in the dialog. (Here, no controls)
   x      :=98; // dialog box coordinate in dialog box unit.
   y      :=58; //  What does it mean ?
   cx     :=142;
   cy     :=92;;
   end; // with rTemplate.rDlg do begin

with rTemplate do begin
   wMenu  :=0; // no menu
   wClass :=0; // default dialog box class
   wTitle :=0
   end;

// Create an empty dialog box, without resources,
// just from the description above.
// It will be filled later with controls.
nRet:=DialogBoxIndirect( System.MainInstance, @rTemplate, hParent,
                         DLGPROC(@TheDialogProc));

DialogExec:=nRet;
End;  // TDialog0.DialogExec

procedure TDialog0.DialogCreate;
//==============================
// Creates a new Modeless Dialog.
// Returns the handle on the dialog in  hWin.
var
  rTemplate   : record
                rDlg   : DLGTEMPLATE;
                wMenu  : word; // = 0 => no menu       2 bytes variable
                wClass : word; // = 0 => default class 2 bytes variable
                wTitle : word; // = 0 => no title
                end;
  wD1,wD2,wD3,wD4,wD5, // data associated with the dialog file name
  wErrorL     : Word32;

Begin
// It is supposed that the poParent is a valid pointer, if non NIL.
if (poParent <> NIL) then
     hParent :=poParent^.hWin // both are always the same
else hParent:=0;              // A dialog box without a parent !

wWindowType :=GGLCTE_A_DIALOG_Modeless; // def. in wGlobals2.pp

// tell who is the active Dialog.
gglpoActiveDialog := @Self;

// If the dialog has a name, then its size and position can be associated
// with it in the String - Data Link file.
if (strWindowName <> '') and (gglpoPrm <> NIL) then begin
   gglpoPrm^.oSDL.GetStringDataW5(strWindowName, wD1,wD2,wD3,wD4,wD5,wErrorL);
   if (wErrorL = 0) then begin
      // Position set by the last position of the dialog
      // But size remains as computed (or set) otherwise.
      nXPosPix  :=wD1;
      nYPosPix  :=wD2;
//      nXSizePix :=wD3;
//      nYSizePix :=wD4;
      end;
   end; // if (strWindowName <> '') then begin

with rTemplate.rDlg do begin
   dwExtendedStyle :=0;
   style  := DS_MODALFRAME or WS_POPUP or WS_CAPTION or WS_SYSMENU
             or WS_TABSTOP;
   cdit   :=0;  // number of controls in the dialog. None here
   x      :=18; // dialog box coordinate in dialog box unit.
   y      :=48; //  What does it mean ?
   cx     :=142;
   cy     :=92;;
   end; // with rTemplate.rDlg do begin

with rTemplate do begin
   wMenu  :=0;
   wClass :=0;
   wTitle :=0;
   end;

// Create an empty dialog box, without resources,
// just from the description above.
// It will be filled later with controls.
hWin:=CreateDialogIndirect( System.MainInstance, @rTemplate, hParent,
                            DLGPROC(@TheDialogProc));

// Memorise in the window's data, that Self is the associated object.
SetWindowLong(hWin, GWL_USERDATA, longint(@Self));

// Show the window dialog box
if (hWin > 0) then ShowWindow(hWin, SW_SHOWNORMAL)
else  gglpoActiveDialog:=NIL;  // didn't sucess creating the dialog.
End;  // TDialog0.DialogCreate

function TDialog0.GetControlFromID(wIDP:word32):PControl;
//=======================================================
// Given an ID, return a pointer on the control having this ID.
// Return NIL if none has this ID
// Try only with child object controls from the dialog box.
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.
   if (not IsBadWritePtr(papoChild^[nChild], sizeOf(TWindowBase))) and
      (papoChild^[nChild]^.wWindowType >= 200) and
      (papoChild^[nChild]^.wWindowType <= 500) and
      (PControl(papoChild^[nChild])^.wCtrlID = wIDP) then begin
      GetControlFromID:=PControl(papoChild^[nChild]);
      exit;
   // else // Not a valid pointer, or not a control
      end; // if (not IsBadWritePtr(...
   end; // for nChild:=nNumberOfChild downto 1 do begin

// No control has this ID
GetControlFromID:=NIL;
end;  // TDialog0.GetControlFromID

function TDialog0.GetControlFromVar(var AnyVar):PControl;
//=======================================================
// Given a variable, return a pointer on the control linked to it.
// Return NIL if none is linked to it.
// Try only with child object controls from the dialog box.
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.
   if (not IsBadWritePtr(papoChild^[nChild], sizeOf(TWindowBase))) and
      (papoChild^[nChild]^.wWindowType >= 200) and
      (papoChild^[nChild]^.wWindowType <= 500) and
      (PControl(papoChild^[nChild])^.pVarLink = @AnyVar) then begin
      GetControlFromVar:=PControl(papoChild^[nChild]);
      exit;
   // else // Not a valid pointer, or not a control
      end; // if (not IsBadWritePtr(...
   end; // for nChild:=nNumberOfChild downto 1 do begin

// No control is linked to this variable.
GetControlFromVar:=NIL;
end;  // TDialog0.GetControlFromVar

procedure TDialog0.NotifyEndDialogToCtrl(nReturnValue:int32);
//===========================================================
// Notify all its child control that the dialog is about to be Ended.
// with the return value : nReturnValue
// If it is a IDCANCEL, variables associated to the controls will
// be reset to their original values.
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))) and
      (papoChild^[nChild]^.wWindowType >= 200) and
      (papoChild^[nChild]^.wWindowType <= 500) then begin
      // Notify all its children that the dialog is about to end.
      PControl(papoChild^[nChild])^.NotifyEndDialogFromDialog(@Self, nReturnValue);
      end; // if
   end; // for nChild:=nNumberOfChild downto 1 do begin
end;  // TDialog0.NotifyEndDialogToCtrl

procedure TDialog0.DialogEnd(nReturnValue:int32);
//===============================================
// When a dialog box should be closed, call this method.
// Usually not overriden.
Begin
// Send to all children the return value of an ending dialog box.
// If it is a IDCANCEL, variables associated to the controls will
// be reset to their original values.
NotifyEndDialogToCtrl(nReturnValue);

// To be tested when it is destroyed
nDlgReturnValue:=nReturnValue;

if (wWindowType = GGLCTE_A_DIALOG_Modeless) then begin
   // it is a Modeless dialog, it must be destroyed.
   DestroyWindow(hWin);
   // the object dialog will be disposed in the WM_NCDestroy message
   end
else begin
   // It is a modal dialog
   // this call automaticaly the WM_Destroy & WM_NCDestroy
   EndDialog(hWin, nReturnValue);
   end;
End;  // TDialog0.DialogEnd

function TDialog0.AcceptToQuit(wCtrlIDP:Word32):boolean;
//======================================================
// Quitting the dialog has been requested, with id wCtrlIDP,
// Ask the parent window if it accept to quit.
var
  wRep : Word32;
begin
wRep:=1;
if (poParent <> NIL) then poParent^.AskAcceptDlgQuit(@Self, wCtrlIDP, wRep);
AcceptToQuit:=(wRep <> 0);
end;  // TDialog0.AcceptToQuit

function TDialog0.WMClose:BOOL;
//=============================
// By default, if the close upper right button has been pressed,
// the dialog box closes and return a IDCANCEL (=2) value.
// Seldom overriden.
begin
DialogEnd(IDCANCEL);

WMClose:=FALSE; // the message was processed.
end;  // TDialog0.WMClose

function TDialog0.WMNotify(wPar:WPARAM; lPar:LPARAM):BOOL;
//========================================================
// Handles the notification messages send from the control
// It is usually overriden, if used.
// Resend this notification to the control
var
  prNH        : ^NMHDR; // info associated with the notification message
  poCtrl      : PControl;

begin
wPar:=wPar; // to avoid a Hint

// WMNotify:=True;  // default return value, if message treated.
WMNotify:=False; // default return value, if message not treated.

// This is not very important, since notification messages
// will be treated by the child earlier

// Notification message information. From whom and what.
prNH:=Pointer(lPar);

// The problem is that the handle doesn't identify always the control.
// only controls that has been defined in the library.
if (IsBadReadPtr(prNH, sizeOf(NMHDR))) then exit;
poCtrl:=Pointer(GetWindowLong(prNH^.hwndFrom, 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(TControl0))) then begin
//   DBW0(2048, DBNotifToStr(prNH^.Code) + ITS(poCtrl^.hWin) + ' = ' + ITS(prNH^.hwndFrom));
   if (poCtrl^.hWin = prNH^.hwndFrom) then begin
      // the control has been identified to be the one having the right handle
      // Calls the notification message to the control's notification message handler
      // poCtrl^.Code = notification message, as NM_XXX and EN_XXX and ...
      poCtrl^.CtrlNotifMessageHandle(prNH^.Code, lPar);
      end;
   end;

end;  //TDialog0.WMNotify

function TDialog0.WMCommand(wPar:WPARAM; lPar:LPARAM):BOOL;
//=========================================================
// Handles some notification messages
// Usually overriden
var
  poCtrl      : PControl;
  wCtrlID,
  wNoticode   : word;

begin
// WMCommand:=True;  // default return value, if message treated.
WMCommand:=False; // default return value, if message not treated.

// This is not very important, since notification messages
// will be treated by the child earlier
if (lPar <> 0) then begin
   // HiWord(wPar) is a notification message send from the child control
   // to its parent. Send it back to the object control

   // this message is a notification code from the control.
   // lPar = handle of the control, get the pointer on the object.
   poCtrl:=Pointer(GetWindowLong(lPar, 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(TControl0))) then begin
//      DBW0(4096, DBNotifToStr(HiWord(wPar)) + ITS(poCtrl^.hWin) + ' = ' + ITS(lPar));
      if (poCtrl^.hWin = lPar) then begin
         // The control has been identified to be the one having the right handle
         // Calls the notification message to the control's notification
         // message handler
         // poCtrl^.Code = notification message, as NM_XXX and EN_XXX and ...
         poCtrl^.CtrlNotifMessageHandle(HiWord(wPar), lPar);
         end;
      end;
   end; // if (lPar <> 0) then begin

WMCommand:=True;  // default return value, if message treated.

wNoticode :=HiWord(wPar);
wCtrlID   :=LoWord(wPar);

if (wNotiCode = BN_CLICKED) and
   ((wCtrlID = awIDExit[1]) or (wCtrlID = awIDExit[2]) or (wCtrlID = awIDExit[3]) or
    (wCtrlID = awIDExit[4]) or (wCtrlID = awIDExit[5]) or (wCtrlID = awIDExit[6]) or
    (wCtrlID = IDOK)) then begin
   // IDOK = 1 is the standard value for the ok button.
   // but this ID must have been given to the OK button

   // Tells to terminate the dialog, and return an
   // IDOK (=1) to the creator of the dialog.
   if AcceptToQuit(wCtrlID) then DialogEnd(wCtrlID);
   Exit;
   end; // if (wNotiCode = BN_CLICKED) and ...

if (wNotiCode = BN_CLICKED) and (wCtrlID = IDCANCEL) then begin
   // IDCANCEL = 2 is the standard value for the Cancel button.
   // but this ID must have been given to the Cancel button

   // Tells to terminate the dialog, and return an
   // IDCANCEL (=2) to the creator of the dialog.
   // if AcceptToQuit(wCtrlID) then  accept to quit always with CANCEL
   DialogEnd(IDCANCEL);
   Exit;
   end; // if (wNotiCode = BN_CLICKED) and (wCtrlID = IDCANCEL) then begin
end;  // TDialog0.WMCommand

function TDialog0.WMCTLCOLOR(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT;
//======================================================================================
// Special case where controls colors can be defines by their parent.
// The controls can define the colors directly through this call.
// lPar = handle on the control who wants to be redrawn
// wPar = Device context used to draw the control.
var
  poCtrl      : PControl;
  wRep        : LRESULT; // = word32;
begin
if (lPar <> 0) then begin
   // this message is sended to the control, so that he can decide
   // the colors he will use to draw itself.

   // lPar = handle of the control, get the pointer on the object.
   poCtrl:=Pointer(GetWindowLong(lPar, 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(TControl0))) then begin
//    DBW0(4096, DBMsgToStr(wMsg) + ITS(poCtrl^.hWin) + ' = ' + ITS(lPar));
//    DBW0(4096, 'Type=' + ITS(poCtrl^.wWindowType) + ' ' + poCtrl^.strClassName);
      if (poCtrl^.hWin = lPar) then begin
         // The control has been identified to be the one having the right handle
         // Calls the notification message to the control's notification
         // message handler
         // poCtrl^.Code = notification message, as NM_XXX and EN_XXX and ...
         // poCtrl^hWin could also be the dialog box !

         // Security test.
         if (poCtrl^.wWindowType >= 200) then
              wRep:=poCtrl^.CtrlWMCTLCOLOR(hWindow, wMsg, wPar, lPar)
         else wRep:=0; // not a control

         // it could be simplified, since LRESULT(FALSE) = 0.
         // But for clarity, I prefer to let it this way.
         if (wRep = 0) then WMCTLCOLOR:=LRESULT(FALSE)  // Ask Windows to do the default message processing
         else               WMCTLCOLOR:=wRep;
         exit;
         end;
      end;
   end;

WMCTLCOLOR := LRESULT(FALSE);  // Ask Windows to do the default message processing
end;  // TDialog0.WMCTLCOLOR

function TDialog0.DialogProc(hDialog:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT;
//======================================================================================
// var hDevC : HDC;  fRes : LRESULT;
begin
hDialog:=hDialog; // to avoid a Hint

// Default treatment of a message. Does nearly nothing
DialogProc:=ord(TRUE); // default return value, if processed

Case wMsg Of
   WM_Notify  : DialogProc:=LRESULT(WMNotify(wPar, lPar));
   WM_Command : DialogProc:=LRESULT(WMCommand(wPar, lPar));
   WM_Close   : DialogProc:=LRESULT(WMClose);
   WM_InitDialog : exit; // return TRUE to let the default init to be done.

   WM_CTLCOLORMSGBOX,
   WM_CTLCOLOREDIT,
   WM_CTLCOLORLISTBOX,
   WM_CTLCOLORBTN,
//   WM_CTLCOLORDLG,
   WM_CTLCOLORSCROLLBAR,
   WM_CTLCOLORSTATIC : begin
      // Special case where controls colors can be defines by their parent.
      // The controls can define the colors directly through this call.
      // lPar = handle on the control who wants to be redrawn
      // wPar = Device context used to draw the control.
      DialogProc:=WMCTLCOLOR(hDialog, wMsg, wPar, lPar);
      end;
{
   WM_CTLCOLORDLG : begin
      // Change the dialog box color, as an example
      hDevC:=HDC(wPar);

      // You can change the text color and the back colors
      SetTextColor(hDevC, RGB(255,255,  0)); // colTextFont);
      SetBkColor(hDevC,  RGB(  0,  0,  0));  // colTextBack);
      SetBkMode(hDevC, OPAQUE);  // colTextTransp);  // OPAQUE or TRANSPARENT

      fRes:=GetStockObject(NULL_BRUSH);  // a transparent dialog box  :-)
//      fRes:=GetStockObject(BLACK_BRUSH);
//      fRes:=GetStockObject(DKGRAY_BRUSH);
//      fRes:=GetStockObject(GRAY_BRUSH);
//      fRes:=GetStockObject(LTGRAY_BRUSH);
//      fRes:=GetStockObject(WHITE_BRUSH);
//      fRes:=CreateSolidBrush(RGB(red,green,blue)); could be used too.
      DialogProc:=fRes;
      end; // WM_CTLCOLORDLG : begin
}
   else begin // of case
      DialogProc := LRESULT(FALSE); // Ask Windows to do the default message processing
      end;
   End; // Case wMsg Of
end;  // TDialog0.DialogProc

procedure TDialog0.WMNotifyFirst(wPar:WPARAM; lPar:LPARAM);
//=========================================================
// Handles the notification messages send from the control
// It is usually overriden, if used.
// It is send before the  DialogProc  receives it.
var
  prNH        : ^NMHDR; // info associated with the notification message
  poCtrl      : PControl;

begin
wPar:=wPar; // to avoid the Hint message

// Notification message information. From whom and what.
prNH:=Pointer(lPar);

// The problem is that the handle doesn't identify always the control.
// only controls that has been defined in the library.
poCtrl:=Pointer(GetWindowLong(prNH^.hwndFrom, 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(TControl0))) then begin
//   DBW0(2048, DBNotifToStr(prNH^.Code) + ITS(poCtrl^.hWin) + ' = ' + ITS(prNH^.hwndFrom));
   if (poCtrl^.hWin = prNH^.hwndFrom) then begin
      // the control has been identified to be the one having the right handle
      // Calls the notification message to the control's notification message handler
      // poCtrl^.Code = notification message, as NM_XXX and EN_XXX and ...
      poCtrl^.CtrlNotifMessageHandleFirst(prNH^.Code, lPar);
      end;
   end;

// else do nothing
end;  //TDialog0.WMNotifyFirst

procedure TDialog0.WMCommandFirst(wPar:WPARAM; lPar:LPARAM);
//==========================================================
// Handles some notification messages
// It is send before the  DialogProc  receives it.
// Seldom overriden
var
  poCtrl      : PControl;
  wNoticode   : word;

begin
//DBW0(128, 'Notif=' + DBNotifToStr(HiWord(wPar)) + ITS(LoWord(wPar)) + ITS(lPar));

if (lPar <> 0) then begin
   // It is a notification message send from the child control to its parent.
   // send it back to the object control
   wNotiCode := HiWord(wPar);

   // this message is a notification code from the control.
   // lPar = handle of the control, get the pointer on the object.
   poCtrl:=Pointer(GetWindowLong(lPar, 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(TControl0))) then begin
//      DBW0(4096, DBNotifToStr(wNotiCode) + ITS(poCtrl^.hWin) + ' = ' + ITS(lPar));
      if (poCtrl^.hWin = lPar) then begin
         // The control has been identified to be the one having the right handle
         // Calls the notification message to the control's notification
         // message handler
         // poCtrl^.Code = notification message, as NM_XXX and EN_XXX and ...
         poCtrl^.CtrlNotifMessageHandleFirst(wNotiCode, lPar);
         end;
      end;
   end;

// else do nothing
end;  // TDialog0.WMCommandFirst

procedure TDialog0.WMInitDialogFirst;
//===================================
// Can be, but it's not usual.
// Create the windows of all its object control.
var
  nn          : integer;

begin
// Just before creating the windows of its children
// Tells its parent that the dialog is about to be filled with its
// Control windows.
if (poParent <> NIL) then poParent^.InitDialogBegin(@Self);

if (papoChild = NIL) then exit; // nothing to do.

// loop over all child object controls, and create their windows.
for nn:=1 to nNumberOfChild do begin
   if (papoChild^[nn] <> NIL) and
      (papoChild^[nn]^.hWin = 0) and
      (papoChild^[nn]^.wWindowType >= 200) and
      (papoChild^[nn]^.wWindowType <= 500) then begin
      // the child window has still not been created.
      // And it is a control, thus create it
      PControl(papoChild^[nn])^.CtrlCreate;
      end; // if
   end; // for

// Tells its parent that the dialog window has been filled with its
// Control windows, and is ready to be shown.
if (poParent <> NIL) then poParent^.InitDialogEnd(@Self);
end;  // TDialog0.WMInitDialogFirst

function TDialog0.DialogProcFirst(hDialog:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT;
//==========================================================================================
// Handles messages for the library, before calling  DialogProc
Var
  wErrorL     : Word32;
  rRec        : rect;
  poDial      : PDialog;

begin
{ if (wMsg <> WM_MouseMove)   and (wMsg <> WM_NCMouseMove) and
   (wMsg <> WM_MENUSELECT )   and (wMsg <> WM_ENTERIDLE ) and
   (wMsg <> WM_SetCursor)   and (wMsg <> WM_NCHittest) and
//   (wMsg <> WM_SetFocus) and (wMsg <> WM_KillFocus) and
//   (wMsg <> WM_KEYDOWN) and (wMsg <> WM_KEYUP) and
   (wMsg <> WM_Move)        and (wMsg <> WM_NCActivate) then
   DBW0(64, DBMsgToStr(wMsg) + ITS(wPar) + ITS(lPar)); }

Case wMsg Of
   WM_Notify  : WMNotifyFirst(wPar, lPar);
   WM_Command : WMCommandFirst(wPar, lPar);

   WM_InitDialog : begin
      hWin:=hDialog;  // here we memorise the handle on the dialog.

      // Position and size of the dialog. Do not repaint, it is still not visible
      // Must be done before the WMInitDialogFirst, since
      // controls are put into the dialog and uses the size of the dialog box.
      MoveWindow(hWin, nXPosPix, nYPosPix, nXSizePix, nYSizePix, FALSE);

      // It is here, that we determine really the content of the dialog box
      // i.e. the controls, in it.
      WMInitDialogFirst;

      // Changes the style of the dialog box.
      SetWindowLong(hWin, GWL_STYLE, dwStyle);
      SetWindowLong(hWin, GWL_EXSTYLE, dwExStyle);

      // Defines a new Title of the dialog box.
      strDlgTitle[length(strDlgTitle)+1]:=#0;
      SetWindowText(hWin, @strDlgTitle[1]);

      // move again, in case the position has changed.
      MoveWindow(hWin, nXPosPix, nYPosPix, nXSizePix, nYSizePix, FALSE);
      end; // WM_InitDialog : begin

   WM_Move,
   WM_Size : begin
      if (wMsg = WM_Size) then
         WMToAllChildren(hDialog, wMsg, wPar, lPar);  // send the message to all childrens

      if not (IsZoomed(hDialog) or IsIconic(hDialog)) then begin
         // Remember the position and size of the Dialog
         GetWindowRect(hDialog, @rRec);
         nXPosPix :=rRec.Left;
         nYPosPix  :=rRec.Top;
         nXSizePix :=rRec.Right  - rRec.Left;
         nYSizePix :=rRec.Bottom - rRec.Top;
         end;
      end;

   WM_ACTIVATE : if (LoWord(wPar) = WA_ACTIVE) or
                    (LoWord(wPar) = WA_CLICKACTIVE) then begin
                    // Sets the pointer on the active dialog.
                    gglpoActiveDialog:=@Self;
                    end;

   end; // Case wMsg Of

DialogProcFirst:=DialogProc(hDialog, wMsg, wPar, lPar);

if (wMsg = WM_NCDestroy) then begin
   // this is the last message a window receive before beeing removed from memory
   hWin:=0;  // do not destroy again.
//   WindowDestroyAllChild; // destroy all childs, if not already done. Useless I think, Windows did it already.

   // If the dialog has a name, then its size and position can be associated
   // with it in the String - Data Link file. Save these values
   if (strWindowName <> '') and (gglpoPrm <> NIL) then begin
      gglpoPrm^.oSDL.SetStringDataW5(strWindowName, nXPosPix,nYPosPix,nXSizePix,nYSizePix,0,wErrorL);
      end; // if (strWindowName <> '') then begin

   if (nDlgReturnValue = IDCANCEL) and (poParent <> NIL) then begin
      // Tells the owner of the dialog that values changed, since they are
      // reset to their original values
      poParent^.DialogAllRestored(@Self);
      end;

   if (wIsDone = 1) then begin
      // dispose also the object, and all its child object also.
      poDial :=@Self;
      dispose(poDial, Done);
      end;
   // else it has already been disposed.
   end;

end;  // TDialog0.DialogProcFirst

end.  // unit wDialog0;

