{  Example of program using
   wApplication0.pp, wMDIWindow0.pp, wMDIChildWindow0.pp,
   wStatus0.pp and wRichText0.pp

   We saw MDI application and RichText component.
   Here, we mix both, to have a mini editor, with multiple windows,
   each being a little text editor with unlimited text length.

   uses the library to simplify greatly the treatment of MDI windows.

   It uses the  Riched32.dll  file, which should be in your
   windows\system directory.
  --------------------------------------------}
program tEx012;

// 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
 ,wMDIWindow0
 ,wMDIChildWindow0
 ,wStatusLine0
 ,wRichText0
 ,wDebug0;

type
  PMyMDIWindow = ^TMyMDIWindow;
  TMyMDIWindow = object(TMDIWindow)
  //"""""""""""""""""""""""""""""""
  // Add a status line to the main window
  oStatusL    : TStatusLine; // Status line of the main window

  constructor Init(strWinTitlePrm:ShortString);
  destructor Done; virtual;

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

  procedure WinCreate(strWinClass:ShortString); virtual;

  procedure CreateMDIChild; virtual;

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

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

type
  PMyMDIChildWindow = ^TMyMDIChildWindow;
  TMyMDIChildWindow = object(TMDIChildWindow)
  //"""""""""""""""""""""""""""""""""""""""""
  // Usually it is better to have a pointer on objects than objects,
  // since pointer can also designate descendent of the objects.
  // More, pointer can be nil, indicating that is has not been initialised.
  oRichEdit   : TRichText;   // RichEdit control contained in the window

  // Text specific to each child window
  strText : string[100];

  // Memorise the value of the glnCpt for each MDI child window
  nCpt : integer;

  constructor Init(strWinTitlePrm:ShortString);
  destructor Done; virtual;

  // creation of an MDI child window
  // Can be overriden
  // poGrParentPrm is a pointer on the grand parent, which is normally
  // the main application MDI window
  procedure WinMDICreate(poGrParentPrm:PMDIWindow0); virtual;

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

const
  // Child window count
  glnCpt : integer = 0;
  // menu id in the WM_Command when a MDI child window is selected in the menu
  cteFiveHund = 500;

var
  oApp        : TApplication0;
  poMyMainWin : PMyMDIWindow;

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

//***************************************************
// TMyMDIWindow = object(TMDIWindow)
//***************************************************

constructor TMyMDIWindow.Init(strWinTitlePrm:ShortString);
//========================================================
begin
inherited init(strWinTitlePrm);

// in which sub menu will the child window menu appair
// 0 => appair in the first menu (File menu usually)
// 1 => appair in the second menu (Edit menu)
// usually set a number to have it in the 'Window' menu
// this is usually set by the user
// In the case of this demo, the Window menu is the second, thus :=1;
nChildSubMenuPos :=3;

// When a child window is created, it will appair in the subMenu.
// designated by 'nChildSubMenuPo'
// When it is selected, it will send a WM_Command
// with wPar = nChildSubMenuID     for the first child,
// with wPar = nChildSubMenuID + 1 for the second child
// with wPar = nChildSubMenuID + 2 for the Third child
// etc.
// with wPar = nChildSubMenuID + 8 for the ninth child
// with wPar = nChildSubMenuID + 9 when a dialog with a list box is opened.
// this is usually set by the user
// set a number not used in the menu.
nChildSubMenuID :=cteFiveHund;

// Initialise the object oStatusL
oStatusL.Init(@Self);
end;  // TMyMDIWindow.Init

destructor TMyMDIWindow.Done;
//===========================
begin
oStatusL.Done;

inherited done;
end;  // TMyMDIWindow.Done

procedure TMyMDIWindow.MenuCreate;
//================================
var
  hSubMenu    : HMenu;

begin
hMenuWin := CreateMenu;

hSubMenu := CreateMenu;
AppendMenu(hSubmenu,MF_STRING,100,'&New child window');
AppendMenu(hSubmenu,MF_STRING,101,'N&ew...');
AppendMenu(hSubmenu,MF_STRING,102,'&Open...');
AppendMenu(hSubmenu,MF_STRING,103,'&Save');
AppendMenu(hSubmenu,MF_STRING,104,'Save &as...');
AppendMenu(hSubmenu,MF_SEPARATOR,0,Nil);
AppendMenu(hSubmenu,MF_STRING,120,'&Close child window');
AppendMenu(hSubmenu,MF_SEPARATOR,0,Nil);
AppendMenu(hSubMenu,MF_String,198,'Open &Debug window');
AppendMenu(hSubmenu,MF_SEPARATOR,0,Nil);
AppendMenu(hSubMenu,MF_String,199,'E&xit');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&File');

hSubMenu := CreateMenu;
// The shortcuts are defined in the system, nothing has to be done.
AppendMenu(hSubMenu,MF_String,201,'&Undo'#8'Ctrl+Z');
AppendMenu(hSubmenu,MF_SEPARATOR,0,Nil);
AppendMenu(hSubMenu,MF_String,202,'&Cut'#8'Ctrl+X');
AppendMenu(hSubMenu,MF_String,203,'&Copy'#8'Ctrl+C');
AppendMenu(hSubMenu,MF_STRING,204,'&Paste'#8'Ctrl+V');
AppendMenu(hSubmenu,MF_SEPARATOR,0,Nil);
AppendMenu(hSubMenu,MF_STRING,205,'Select &All'#8'Ctrl+A');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&Edit');

hSubMenu := CreateMenu;
AppendMenu(hSubMenu,MF_String,301,'&Font...');
AppendMenu(hSubMenu,MF_String,302,'Toggle &Read Only');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&Options');

hSubMenu := CreateMenu;
AppendMenu(hSubmenu,MF_STRING,1200,'&Tile');
AppendMenu(hSubmenu,MF_STRING,1201,'&Cascade');
AppendMenu(hSubmenu,MF_STRING,1202,'&Arange icons');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&Window');
end;  // TMyMDIWindow.MenuCreate

procedure TMyMDIWindow.WinCreate(strWinClass:ShortString);
//========================================================
// Create the window, and insert a status line in it.
var
  wStatH      : word; // height of the status line
  rRec        : RECT;

begin
// here strWinClass=''.
inherited WinCreate(strWinClass);

oStatusL.statuscreate;

// Must change the size of the client window, since there is a Status line.
GetClientRect(oStatusL.hWin, @rRec);
wStatH := word(rRec.Bottom - rRec.Top);
GetClientRect(hWin, @rRec);
MoveWindow (hWinClient,0,0, rRec.Right, rRec.Bottom - wStatH, True);
end;  // TMyMDIWindow.WinCreate

procedure TMyMDIWindow.CreateMDIChild;
//====================================
// From the menu, a new child window can be opened
var
  poMDIChild  : PMyMDIChildWindow;
  strTitle    : string[30];

begin
inc(glnCpt);
strTitle :='Hello ' + IntToStr(glnCpt,3);

new(poMDIChild, init(strTitle));

poMDIChild^.strText :=strTitle;

// Creates an MDI child window, and gives it a pointer on its grand parent.
poMDIChild^.WinMDICreate(@Self);
end;  // TMyMDIWindow.CreateMDIChild

function TMyMDIWindow.WMCommand(wPar:WPARAM; lPar:LPARAM):LRESULT;
//================================================================
// Handles the menu messages
Var
  wNrMenu     : word;
  hWinChild   : HWND; // handle of the active MDI child window

begin
WMCommand := 0; // Default return value when processing a message

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

// for the menu, lPar = 0
wNrMenu   := LoWord(wPar);
if (gglpoMDIActiveWindow <> NIL) then
   with PMyMDIChildWindow(gglpoMDIActiveWindow)^ do begin
   Case wNrMenu Of
      // these treatments will become standard in the library in a next version.
      // menu number over 30000 are reserved for the library.
      101 : oRichEdit.NewText;
      102 : oRichEdit.LoadRICH;
      103 : oRichEdit.SaveRICH;
      104 : oRichEdit.SaveRICHas;

      201 : oRichEdit.UndoText;
      202 : oRichEdit.CutText;
      203 : oRichEdit.CopyText;
      204 : oRichEdit.PasteText;
      205 : oRichEdit.SelectAllText;
      301 : oRichEdit.SelectFont;
      end; // case
   end;

Case wNrMenu Of
   // from the file menu
   100 : CreateMDIChild;
   198 : DBW0(0, ''); // opens the debugging window.
   199 : PostMessage( hWin,WM_Close,0,0);

   // Closes the MDI child window.
   120 : begin
      hWinChild:=LOWORD (SendMessage (hWinClient, WM_MDIGETACTIVE, 0, 0)) ;

      if (SendMessage ( hWinChild, WM_QUERYENDSESSION, 0, 0) <> 0) then
         SendMessage (hWinClient, WM_MDIDESTROY, hWinChild, 0);
      end;

   // Messages for arranging windows
   1200 : SendMessage (hWinClient, WM_MDITILE, 0, 0);
   1201 : SendMessage (hWinClient, WM_MDICASCADE, 0, 0);
   1202 : SendMessage (hWinClient, WM_MDIICONARRANGE, 0, 0);

   cteFiveHund..cteFiveHund+9 : begin
      // it is difficult to know to which MDI child window
      // this menu message is destinated.
      // But using the default window handling, does what one expect.
      // DBW0(16, 'menu :' + ITS(wPar) + ITS(lPar));

      // Menu messages corresponding to the selection of a child window
      // should call the inherited method to have the default handling
      WMCommand:=inherited WMCommand(wPar, lPar);
      end;

   else WMCommand:=inherited WMCommand(wPar, lPar);
   end;  // Case NrMenu Of
end;  // TMyMDIWindow.WMCommand

function TMyMDIWindow.WindowProc(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT;
//==========================================================================================
// All window messages are send here.
var
  wStatH      : word; // height of the status line
  rRec        : RECT;
  alEdges     : array[1..2] of LongInt;

Begin
WindowProc := 0; // Default return value when processing a message
// DBW0(1024, DBMsgToStr(wMsg));

Case wMsg Of
   WM_Destroy : PostQuitMessage (0); // quit the application
   WM_Command : WindowProc:=WMCommand(wPar, lPar);
   WM_Size : begin
      GetClientRect(oStatusL.hWin, @rRec);
      wStatH := word(rRec.Bottom - rRec.Top);
      GetClientRect(hWindow, @rRec);
      MoveWindow (oStatusL.hWin,0, rRec.Bottom - wStatH, rRec.Right, rRec.Bottom, FALSE);

      // Define the position of the separation in the Status line
      alEdges[1] := rRec.Right  - rRec.Left - 80;
      alEdges[2] := rRec.Right  - rRec.Left;
      SendMessage(oStatusL.hWin, SB_SETPARTS, 2, LongInt(@alEdges));

      // Must change the size of the client window too
      MoveWindow (hWinClient,0,0, rRec.Right, rRec.Bottom - wStatH, True);
      end; // WM_Size

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

//***************************************************
// TMyMDIChildWindow = object(TMDIChildWindow0)
//***************************************************

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

strText:='';

nCpt:=glnCpt;

// change the default size
nWidth  :=350;
nHeight :=200;

// Initialise the object oEditB
oRichEdit.Init(@Self);

oRichEdit.vXSizeRel:=1;
oRichEdit.vYSizeRel:=1;
end;  // TMyMDIChildWindow.Init

destructor TMyMDIChildWindow.Done;
//================================
begin
// destroy the object oRichEdit
oRichEdit.Done;

inherited done;
end;  // TMyMDIChildWindow.Done

procedure TMyMDIChildWindow.WinMDICreate(poGrParentPrm:PMDIWindow0);
//==================================================================
// creation of an MDI child window
begin
inherited WinMDICreate(poGrParentPrm);

// Add a status bar to the edit box
oRichEdit.poStatusLine:=@poMyMainWin^.oStatusL;

// Size = the whole client areat
oRichEdit.vXSizeRel:=1;  // if you don't specify, it is set automatically
oRichEdit.vYSizeRel:=1;  // but for clarity, it is better to specify.

// The status line is in the main window.
oRichEdit.CtrlCreate;
SetFocus(oRichEdit.hWin);

// Set the status line state.
with PMyMDIWindow(poGrParent)^ do begin
   oRichEdit.SetModifyState(oRichEdit.fModified);
   end;
end;  // TMyMDIChildWindow.WinMDICreate

function TMyMDIChildWindow.WindowProc(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT;
//===============================================================================================
// All window messages are send here.
var
  rRec        : RECT;

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

case wMsg of
   // processing of messages
   WM_Close : begin
      // Ask only if it is closed explicitely, not if the main window is closed
      oRichEdit.AskSave;
      WindowProc:=inherited WindowProc(hWindow, wMsg, wPar, lPar);
      end;
   WM_Size : begin
      GetClientRect(hWindow, @rRec);
      MoveWindow (oRichEdit.hWin,0,0, rRec.Right, rRec.Bottom, False);
      WindowProc := inherited WindowProc(hWindow, wMsg, wPar, lPar);
      end; // WM_Size
   WM_SETFOCUS : begin
      // The status line must change
      with  PMyMDIWindow(poGrParent)^ do begin
         oRichEdit.SetModifyState(oRichEdit.fModified);

         // give the focus to the rich text.
         SetFocus(oRichEdit.hWin);
         end;
      end;
   else WindowProc := inherited WindowProc(hWindow, wMsg, wPar, lPar);
        // Default message handling for MDI child windows
   end; // case
End;  // TMyMDIChildWindow.WindowProc

//*********************************************************
// Main programm
//*********************************************************

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

// Creation of the main window
new(poMyMainWin, Init('Title of the MDI Example012 (Ex012.pp) Window'));

oApp.Run;
oApp.Done;  // The main window object is disposed here.
End. // program tEx012;

