{ Example of creating dialog boxes.
  This example follows example tEx022.pp
  It show how to create dialog boxes, using the library,
  without resources.

  It shows the use of a modal dialog box, i.e. the program doesn't accept
  input while the dialog box is opened, but still can receive messages

  It shows the use of a modeless dialog box, i.e. the program continues
  normaly while the dialog box is opened.

  I find it still quite complicate, and will simplify it in the library.

  Later I will use this method to create dynamicaly a dialog box from
  a simple text file descriptor. I hope to have a first version of it
  at the beginning of february 2002.

  --------------------------------------------}
program tEx023;

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

Uses
  Windows
 ,wApplication0
 ,wWindowBase0
 ,wWindow0
 ,wDialog0
 ,wButtonPush0
 ,wStatic0
 ,wEditBox0
 ,wComboBox0
 ,wGlobals2
 ,wDebug0;

type
  PMyDialog = ^TMyDialog;
  TMyDialog = object(TDialog)
  //"""""""""""""""""""""""""
//  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

  // defined here, to be used easily in the WMCommand procedure.
  oEditBox1  : TEditBox; // to insert an edit box.
                         // Here it will be a simple monoline edit box.
  oComboBox1 : TComboBox; // to insert a Drop Down combo box.

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

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

  function WMNotify(wPar:WPARAM; lPar:LPARAM):BOOL; virtual;
  // Handles the notification messages send from controls
  // It is usually overriden, if used.

  function WMCommand(wPar:WPARAM; lPar:LPARAM):BOOL; virtual;
  // Handles norification messages
  // It is usually overriden

  procedure WMInitDialogFirst; virtual;
// -->  This Must be overridden to put controls into the dialog box.
  // The first call to the dialog box is done here.
  // At this place, we add the controls in the dialog box.
  // Later the library will give a simpler way to do it.
  end; // TMyDialog = object(TDialog)

type
  PMyWindow = ^TMyWindow;
  TMyWindow = object(TWindow0)
  //""""""""""""""""""""""""""

  constructor Init(strWinTitlePrm:ShortString);

  procedure MenuCreate; virtual;
  // Add a menu to the window

  procedure WinCreate(strWinClass:ShortString); virtual;
  // When creating the window, controls will be added.

  procedure WMCreateDlgModal; virtual;
  // Creates a Modal dialog box.
  // Modal means that the rest of the application will not be accessible
  // while the dialog box is opened.

  procedure WMCreateDlgModeless; virtual;
  // Creates a Modeless dialog box.
  // Modeless means that the rest of the application will be accessible
  // while the dialog box is opened.

  function WMCommand(wPar:WPARAM; lPar:LPARAM):LRESULT; virtual;
  // Handles the menu messages

  function WindowProc(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT; virtual;
  end; // TMyWindow = object(TWindow)

var
  oApp        : TApplication0;
  poMyMainWin : PMyWindow;
  glpoModelessDlg : PMyDialog;
  // pointer on the modeless dialog box.
  // It is NIL if closed, <> NIL if open.

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

//***************************************************
// TMyDialog = object(TDialog)
//***************************************************

constructor TMyDialog.Init(poParentP:PWindowBase);
//================================================
// The constructor must exist, even if it only calls the parent.
begin
inherited Init(poParentP);

// Defines a new Title of the dialog box.
strDlgTitle :='My Dialog Box';

// Position and size of the dialog box, in pixel, relative to the screen.
nXPosPix  := 350;
nYPosPix  := 250;
nXSizePix := 300;
nYSizePix := 200;

// Style of the dialog can be changed here. (Seldom used.)
// Default values
// dwStyle:=DS_MODALFRAME or WS_POPUP or WS_CAPTION or WS_SYSMENU; // default value
// dwExStyle :=0;             // default value

oEditBox1.Init(@Self);
oComboBox1.Init(@Self);
end;  // TMyDialog.Init

destructor TMyDialog.Done;
//========================
// Call Done; at the end of a new destructor.
begin
oEditBox1.Done;
oComboBox1.Done;

// if the modeless dialog has been destroyed, the pointer is no more valid.
if (glpoModelessDlg = @Self) then glpoModelessDlg:=NIL;

inherited Done;
end;  // TMyDialog.Done

function TMyDialog.WMNotify(wPar:WPARAM; lPar:LPARAM):BOOL;
//=========================================================
// Handles the notification messages send from controls
// It is usually overriden, if used.
var  prNM : ^NMHDR;
begin
wPar:=wPar; // to remove a Hint
// WMNotify:=inherited WMNotify(wPar, lPar); useless, does nothing
// WMNotify:=True;  // default return value, if message treated.
WMNotify:=False; // default return value, if message not treated.

// id Ctrl = wPar = ID of the control who sendet the notification.
prNM:=pointer(lPar);
DBW0(1, 'WMNotify= ' + DBNotifToStr(prNM^.Code));
// prNM^.code     = Notification code sendet by the control.
// prNM^.hwndFrom = handle of the control who sendet the notification.
// prNM^.idFrom   = wPar = ID of the control who sendet the notification.

// The code can be :
// NM_CLICK       The user has clicked the left mouse button within the control.
// NM_DBLCLK      The user has double-clicked the left mouse button within the control.
// NM_KILLFOCUS   The control has lost the input focus.
// NM_OUTOFMEMORY The control could not complete an operation because there was not enough memory available.
// NM_RCLICK      The user has clicked the right mouse button within the control.
// NM_RDBLCLK     The user has double-clicked the right mouse button within the control.
// NM_RETURN      The control has the input focus, and the user has pressed the ENTER key.
// NM_SETFOCUS    The control has received the input focus.    UINT code;
// Or a notification such as :
// ???? to come
end;  //  TMyDialog.WMNotify

function TMyDialog.WMCommand(wPar:WPARAM; lPar:LPARAM):BOOL;
//==========================================================
// Handles norification messages. It is always overriden.
// Later dialog boxes will be simplified, and nearly nothing will
// have to be done.
// LoWord(wPar) = control ID
// lPar = control handle
// Here are some notification messages (in HighWord(wPar))
// BN_CLICKED (=0), BN_DISABLE, BN_HILITE, BN_PAINT, BN_UNHILITE, DM_GETDEFID
// BN_DBLCLK, BN_DOUBLECLICKED, BN_KILLFOCUS, BN_SETFOCUS

var
  wCtrlID,
  wNoticode   : word;
  strS        : ShortString;
  nSelection  : int32;

begin
lPar:=lPar; // to avoid a Hint
// WMCommand:=inherited WMCommand(wPar, lPar);  // does nothing.

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

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

if (wNotiCode = BN_CLICKED) and (wCtrlID = 21) then begin
   // The "My Button" has been pressed

   // Get the text of the edit box
   strS:='Edit Box = ' + oEditBox1.GetCurrentText;

   // We could write the string in the debug window here, but
   // As an example we sent it the main window, in a User message
   // Messages above or equal to  WM_User + 1000  can be used
   // by a final application developper.
   SendMessage(hParent, WM_User + 1000, 0, LPARAM(@strS));

   // Get the text of the combo box
   strS:='Combo Box =' + oComboBox1.GetCurrentText;

   // Again, we could write the string in the debug window here.
   SendMessage(hParent, WM_User + 1000, 0, LPARAM(@strS));

   nSelection:=oComboBox1.GetCurrentSelection;
   DBW0(2048, 'Selection= ' + ITS(nSelection));

   exit;
   end;

if (wNotiCode = BN_CLICKED) and (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
   // in the dialog box description defined in tEx022.rc
   // 10 is the "Validate" button ID of the modeless dialog box.

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

if (wNotiCode = BN_CLICKED) and (wCtrlID = IDCANCEL) then begin
   // IDCANCEL = 2 is the standard value for the ok button.
   // but this ID must have been given to the CANCEL button
   // in the dialog box description defined in tEx022.rc

   // It tells to end the dialog Box
   // return  IDCANCEL (=2)  to tell that the CANCEL
   // button has been pressed.
   DialogEnd(IDCANCEL);
   Exit;
   end;

WMCommand:=False;
end;  //  TMyDialog.WMCommand

procedure TMyDialog.WMInitDialogFirst;
//====================================
// The first call to the dialog box is done here.
// At this place, we add the controls in the dialog box.
// Later the library will give a simpler way to do it.
// It is here, that we determine really the controls, style etc.
// of the dialog box
var
  // you must have pointers on object control here.
  // you could have used object defined in the TMyDialog structure.
  poButtonQuit,
  poButton1   : PButtonPush;
  poStatic1   : PStatic;

// defined as child object of the dialog box.
//  oEditBox1  : TEditBox; // to insert an edit box.
//                         // Here it will be a simple monoline edit box.
//  oComboBox1 : TComboBox; // to insert a Drop Down combo box.


begin //  TMyDialog.WMInitDialogFirst
// It is still quite complicate, but will become very simple when a
// text description file will be used.
// the dialog box creation will be done in one line of program only.

// You can check the type of the dialog here if wanted.
if (wWindowType = GGLCTE_A_DIALOG_Modeless) then begin
   // it is a modeless dialog which is created.
   // Just to see, changes the position and title
   strDlgTitle :='This is a modeless dialog Box';

   // if you change the size, controls will not move.
   nXPosPix  := 50;
   nYPosPix  := 200;
   end;
{else wWindowType = GGLCTE_A_DIALOG_Modeless }

// for the Demo02 release, the oject control will not be disposed. !
// But in the Demo03, it will be automatically disposed.
new(poButton1, Init(@Self));

// defines position, size, label, ID of the button
// See tEx017.pp, it is done the same way here.
with poButton1^ do begin
   vXPosMM    :=   3; // X position in milimeters in the parent window
   vYPosMM    :=   2; // Y position in milimeters in the parent window

   // It is a good idea to give the sizes in char "Ch" units.
   // Sizes are usually given only in "Ch" (=characters) units.
   // vXSizeCh  = 15 means about 15 character long.
   // vYSizeCh =  2 means the height of two characters.
   vXSizeCh   := 15; // in character width units
   vYSizeCh   :=  2; // in character height units

   // Minimum control sizes in pixels
   nXSizeMinPix := 5; // default value
   nYSizeMinPix := 5; // default value

   strCtrlLabel  :='My Button'; // Text on the button
   strCtrlHint   :='Click the button'; // default value = no hints by default.
   wCtrlID:=21;  // identifier of the control
                 // used in TMyDialog.WMCommand

   // creates the button
   CtrlCreate;
   end; // with poButton1^ do begin

// for the Demo02 release, the oject control will not be disposed. !
// But in the Demo03, it will be automatically disposed.
new(poButtonQuit, Init(@Self));  // it will be disposed by the dialog box (=the parent)

// defines position, size, label, ID of the button
with poButtonQuit^ do begin
   // the default values are not shown again here.
   vXSizeCh   :=  10;  // 10 characters width
   vYSizeCh   :=   2; //   2 characters height, normal for buttons

   vXPosMM    := -1; // X position in milimeters in the parent window
   vYPosMM    := -1; // Y position in milimeters in the parent window

   vXPosCtrlRef :=1; // Position relative to the Right  of the control. 0=default
   vYPosCtrlRef :=1; // Position relative to the Bottom of the control. 0=default

   // Defines which point of the parent window is take as the origine
   vXPosParentRef :=1; // Position relative to the Right  of the parent window. 0=default
   vYPosParentRef :=1; // Position relative to the Bottom of the parent window. 0=default

   strCtrlLabel  :='Quit'; // Text on the button
   strCtrlHint   :='Click to quit'; // default value = no hints by default.
   wCtrlID:=IDOK; // identifier of the control. IDOK => the OK button.
                  // This value will be used in TMyDialog.WMCOMMAND to
                  // determine which button has been clicked.

   // creates the button
   CtrlCreate;
   end; // with poButtonQuit^ do begin

// for the Demo02 release, the oject control will not be disposed. !
// But in the Demo03, it will be automatically disposed.
new(poStatic1, Init(@Self));  // it will be disposed by the dialog box (=the parent)

// defines position, size, label, ID of the static text control
with poStatic1^ do begin
   // the default values are not shown again here.
   vXSizeCh   :=  15; // 15 characters width
   vYSizeCh   := 1.5; // 1.5 characters height

   vXPosMM    :=  -2; // X position in milimeters in the parent window
   vYPosMM    :=   2; // Y position in milimeters in the parent window

   vXPosCtrlRef :=1; // Position relative to the Right  of the control. 0=default
   vYPosCtrlRef :=0; // Position relative to the Top of the control. 0=default

   // Defines which point of the parent window is take as the origine
   vXPosParentRef :=1; // Position relative to the Right  of the parent window. 0=default
   vYPosParentRef :=0; // Position relative to the Top of the parent window. 0=default

   strCtrlLabel  :='Static text'; // Text on the button
   strCtrlHint   :='Nothing interresting'; // default value = no hints by default.
   wCtrlID:=11; // identifier of the control, any number >= 10.

   // defined in wWindowBase0.pp
   // Every window can have a name, not usefull for controls, thus
   // the next line is useless.
   strWindowName :='First stattic control';

   // Center the text in the place reserved for it
   // See CreateWindow in Win32.hlp sdk help for mor options
//   dwStyle:=dwStyle or SS_SS_RIGHT;  // would place it to the right
   dwStyle:=dwStyle or SS_CENTER;

//   fUseDefaultWMPaint :=FALSE; default, to use the next properties.
   colTextFont        :=RGB(255,0,0);       // default = RGB(  0,  0,  0);
//   colTextBack        :=RGB(255,255,255); // default = RGB(255,255,255);
//   colTextTransp      :=TRANSPARENT;      // default = TRANSPARENT, other = OPAQUE

   // creates the static text
   CtrlCreate;
   end; // with poStatic1^ do begin

// defines position, size, label, ID of the edit box
with oEditBox1 do begin
   // the default values are not shown again here.
   // sizes are usually given only in "Ch" (=characters) units.
   vXSizeCh   := 15;
   vYSizeCh   :=  1.5;

   vXPosMM    := poButton1^.vXPosMM;
   vYPosMM    :=  20; // Y position in milimeters in the parent window

   strCtrlLabel  :='Edit me'; // Text on the button
   strCtrlHint   :='Enter a little text'; // default value = no hints by default.
   wCtrlID:=101; // identifier of the control, any number
                 // Could be used in the WM_COMMAND

   // creates the edit box
   CtrlCreate;
   end; // with oEditBox1 do begin

// defines position, size, label, ID of the combo Box
with oComboBox1 do begin
   // the default values are not shown again here.
   // sizes are usually given only in "Ch" (=characters) units.
   vXSizeCh   := 15;
   vYSizeCh   :=  8;  // can view  vYSizeCh - 2  lines

   vXPosMM    := oEditBox1.vXPosMM;
   vYPosMM    := oEditBox1.vYPosMM + 3;
   vYPosCh    := oEditBox1.vYPosCh + oEditBox1.vYSizeCh; // to be bellow the edit box

   strCtrlLabel  :='to edit too'; // useless to define here
   strCtrlHint   :='Text can be entered too'; // default value = no hints by default.
   wCtrlID:=102; // identifier of the control, any number
                 // Could be used in the WM_COMMAND

   // creates the combo box
   CtrlCreate;

   // You cannot add a string before the combo box has been created.
   AddString('first line');
   AddString('second line');
   AddString('third line');
   AddString('fourth line');

   // which line should be shown first.
//   SetCurrentSelection(0); // if not, the  strCtrlLabel  appear.
   end; // with oComboBox1 do begin

end;  // TMyDialog.WMInitDialogFirst

//***************************************************
// TMyWindow = object(TWindow)
//***************************************************

constructor TMyWindow.Init(strWinTitlePrm:ShortString);
//=====================================================
begin
inherited Init(strWinTitlePrm);
end;  // TMyWindow.Init

procedure TMyWindow.MenuCreate;
//=============================
var
  hSubMenu    : HMenu;
begin
hMenuWin := CreateMenu;

hSubMenu := CreateMenu;
AppendMenu(hSubmenu,MF_STRING,100,'&Open debug window');
AppendMenu(hSubmenu,MF_STRING,101,'&1 A Modal dialog box...');
AppendMenu(hSubmenu,MF_STRING,102,'&2 A Modeless dialog box...');
AppendMenu(hSubmenu,MF_SEPARATOR,0,Nil);
AppendMenu(hSubMenu,MF_String, 200, 'E&xit');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&File');

hSubMenu := CreateMenu;
AppendMenu(hSubmenu,MF_STRING, 401, '&Info...');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&Help');
end;  // TMyWindow.MenuCreate

procedure TMyWindow.WinCreate(strWinClass:ShortString);
//=====================================================
// Creates a new window.
Begin
inherited WinCreate(strWinClass);
End;  // TMyWindow.WinCreate

procedure TMyWindow.WMCreateDlgModal;
//===================================
// Creates a Modal dialog box.
var
  nRet : integer;
  poDlg : PMyDialog;

begin
new(poDlg, Init(@Self));

// Creates the dialog, and execute it.
// If you send NIL as a parameter, the dialog will exist,
// and the programme will still be actif. But it is not the
// right way to create a Modeless dialog box.
nRet:=poDlg^.DialogExec;
DBW0(1, 'Dlg Ret value =' + ITS(nRet));

// It is disposed in the library automatically. --> nothing do dispose
// But if you want, you can test it, and
// dispose it yourself if the test infirme the the fact that it was already done.
// Here are two security tests to see if the dialog object has
// already been released.
//if (not IsBadWritePtr(poDlg, SizeOf(TDialog))) and
//   (poDlg^.poParent = @Self) then
//   dispose(poDlg, Done); // the dialog has been destroyed.
end;  // TMyWindow.WMCreateDlgModal

procedure TMyWindow.WMCreateDlgModeless;
//======================================
// Creates a Modeless dialog box.
begin
if (glpoModelessDlg <> NIL) then begin
   MessageBeep(0);
   exit; // don't want to open it twice
   end;

new(glpoModelessDlg, Init(@Self));

// Creates the dialog, and execute it.
// If you send NIL as a parameter, the dialog will exist,
// and the programme will still be actif. But it is not the
// right way to create a Modeless dialog box.
glpoModelessDlg^.DialogCreate;

if (glpoModelessDlg^.hWin = 0) then begin
   // failed creating the dialog
   dispose(glpoModelessDlg, Done);
   glpoModelessDlg:=NIL;
   end;

// The object dialog will be disposed in the wDialog0.pp unit,
// when the dialog window (=dialog box) is closed.
end;  // TMyWindow.WMCreateDlgModeless

function TMyWindow.WMCommand(wPar:WPARAM; lPar:LPARAM):LRESULT;
//=============================================================
// Handles the menu messages
Var  wNrMenu     : word;
begin
WMCommand:=0;  // default return value

if (lPar <> 0) then begin
   WMCommand:=inherited WMCommand(wPar, lPar);
   exit;
   end;

// for the menus,  lPar = 0
//=========================
wNrMenu   := LoWord(wPar);
Case wNrMenu Of
   100 : DBW0(0, '');  // just open the debugging window
   101 : WMCreateDlgModal;
   102 : WMCreateDlgModeless;

   200 : PostMessage( hWin,WM_Close,0,0);
   401 : MessageBox( hWin,'Example of creation of Modal and Modeless dialog boxes.',
                          'Dialog boxes.',
                     MB_OK Or MB_IconInformation);
   else WMCommand:=inherited WMCommand(wPar, lPar);
   end;  // Case NrMenu Of
end;  // TMyWindow.WMCommand

function TMyWindow.WindowProc(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT;
//=======================================================================================
// All window messages are send here.
Type PShortString = ^ShortString;
var
  pstrS : PShortString;

Begin
WindowProc := 0; // Default return value when processing a message

{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(32, DBMsgToStr(wMsg) + ITS(wPar) + ITS(lPar)); }

Case wMsg Of
   // End application developpers can use self defined messages
   // having number above or equal to : WM_User + 1000
   WM_User + 1000: begin
      // message from the modale dialog box
      pstrS:=PShortString(lPar);
      DBW0(64, pstrS^);
      end;

   else begin // Case wMsg Of
      // Default processing of the messages.
      WindowProc:=inherited WindowProc(hWindow, wMsg, wPar, lPar);
      end; // Case wMsg Of
   End; // Case wMsg Of

End;  // TMyWindow.WindowProc

//***************************************************
// Main : The program begins here.
//***************************************************

Begin // MAIN ******
// Name under which the main window is registered. Spaces are not allowed
oApp.Init('Example023');

// the dialog box is not open.
glpoModelessDlg:=NIL;

// Change the color of the background of the main window.
// to have the desktop color
oApp.WindowClass.hbrBackground :=COLOR_BACKGROUND+1;

// Creation of the main window
new(poMyMainWin, Init('tEx023. Creating dialog boxes without resources, with the help of the library'));

// The App.WindowClass can be modified here, if wanted. (Not usual)
oApp.Run;
oApp.Done;  // The main window object is disposed here.
End.

