{ Example of a Multiple Document Interface program
  which each child window being a little text editor.
  Uses a menu, accelerators, a toolbar.
  It's only to show how simple it became to have the beginning
  of an editor if the GAP library is used.
  --------------------------------------------}
program tEx054;

// 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
 ,wToolBar0
 ,wRichTextFrame0
 ,wDialogCreatorLOW2
 ,wDebug0;

type
  PMyMDIWindow = ^TMyMDIWindow;
  TMyMDIWindow = object(TMDIWindow)
  //"""""""""""""""""""""""""""""""
  poToolBar : PToolBar;
  // ToolBar of the application

  constructor Init(strWinTitlePrm:ShortString);

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

  procedure CreateMDIChild; virtual;

  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; // 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   : TRichTextFrame;   // RichEdit control contained in the window

  constructor Init(strWinTitlePrm:ShortString);

  // 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
  // menu id in the WM_Command when a MDI child window is selected in the menu
  cteFiveHund = 500; // Will not be used

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 'nChildSubMenuPos', but send WM_COMMAND messages
// with wPar = nChildSubMenuID + position in the menu
nChildSubMenuID :=cteFiveHund;

// This line is all what is needed to have a status line.
new(poStatusLine, Init(@Self));
end;  // TMyMDIWindow.Init

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

// At the begining, there are no window to edit.
poMenu^.EnableMenuItemByMsgNum(10001, MF_GRAYED);
poMenu^.EnableMenuItemByMsgNum(10002, MF_GRAYED);
poMenu^.EnableMenuItemByMsgNum(10003, MF_GRAYED);
poMenu^.EnableMenuItemID(65203, MF_GRAYED);
poMenu^.EnableMenuItemID(65204, MF_GRAYED);
poMenu^.EnableMenuItemID(65400, MF_GRAYED);
DrawMenuBar(hWin); // must redraw the menu bar

// Add a toolbar
poToolBar:=PToolBar(ToolBarCreateMSG(11199, @self));

// The client window size is automatically adjusted when then main MDI
// window size change
MoveWindow(hWin, nLeft, nTop, nWidth, nHeight+1, TRUE);
End;  // TMyMDIWindow.WinCreate

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

begin
new(poMDIChild, init('New document'));

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

// If a child window has been created, then we can edit it
// Thus enable the menus concerning child windows.
poMenu^.EnableMenuItemByMsgNum(10001, MF_ENABLED);
poMenu^.EnableMenuItemByMsgNum(10002, MF_ENABLED);
poMenu^.EnableMenuItemByMsgNum(10003, MF_ENABLED);
poMenu^.EnableMenuItemID(65203, MF_ENABLED);
poMenu^.EnableMenuItemID(65204, MF_ENABLED);
poMenu^.EnableMenuItemID(65400, MF_ENABLED);
DrawMenuBar(poMyMainWin^.hWin); // must redraw the menu bar
end;  // TMyMDIWindow.CreateMDIChild

function TMyMDIWindow.WMCommand(wPar:WPARAM; lPar:LPARAM):LRESULT;
//================================================================
// Handles the menu messages
Var
  wNrMenu     : word;
  hWinLast    : HWND; // To give back the focus to the last active 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);
hWinLast:=0;
if (gglpoMDIActiveWindow <> NIL) then begin
   PMyMDIChildWindow(gglpoMDIActiveWindow)^.oRichEdit.WMCommand(wNrMenu);

   // Remember who was the last active window
   hWinLast:=gglpoMDIActiveWindow^.hWin;

   if (wNrMenu = 65203) or (wNrMenu = 65204) then
      with PMyMDIChildWindow(gglpoMDIActiveWindow)^ do begin
      // Change its title
      strWinTitle:=oRichEdit.strFilePath;
      strWinTitle[length(strWinTitle)+1]:=#0;
      SetWindowText( PMyMDIChildWindow(gglpoMDIActiveWindow)^.hWin,
                     @strWinTitle[1]);
      end;
   end;

Case wNrMenu Of
   // from the file menu
   100 : CreateMDIChild;
   102 : begin
         // Load a new text file
         CreateMDIChild;
         if not PMyMDIChildWindow(gglpoMDIActiveWindow)^.oRichEdit.LoadRICH then begin
            DestroyWindow(gglpoMDIActiveWindow^.hWin);
            // dispose(gglpoMDIActiveWindow, Done); // would do the same

            // Activate the last active window
            if (hWinLast <> 0) then begin
               SendMessage(hWinClient, WM_MDIACTIVATE, hWinLast, 0);
               end;
            end
         else with PMyMDIChildWindow(gglpoMDIActiveWindow)^ do begin
            // Define its title
            strWinTitle:=oRichEdit.strFilePath;
            strWinTitle[length(strWinTitle)+1]:=#0;
            SetWindowText( PMyMDIChildWindow(gglpoMDIActiveWindow)^.hWin,
                           @strWinTitle[1]);
            end;
         end;
   else WMCommand:=inherited WMCommand(wPar, lPar);
   end;  // Case NrMenu Of
end;  // TMyMDIWindow.WMCommand

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

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

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

// Initialise the RichTextFrame object
oRichEdit.Init(@Self);
end;  // TMyMDIChildWindow.Init

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

// Tell the edit box that the main window has a status bar.
oRichEdit.poStatusLine:=poMyMainWin^.poStatusLine;

// 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.
Begin
WindowProc := 0; // Default return value when processing a message

case wMsg of
   // processing of messages
   WM_DESTROY : begin
      // A complicate way to know if it was the last MDI child window
      // But it works, desactivate corresponding menus.
      if (Cardinal(EnableMenuItem( poMyMainWin^.poMenu^.hMenuObj, cteFiveHund,
                          MF_BYCOMMAND or MF_ENABLED)) = -1) then begin
         // it was the last child window, desactivate the menu Edit.
         poMyMainWin^.poMenu^.EnableMenuItemByMsgNum(10001, MF_GRAYED);
         poMyMainWin^.poMenu^.EnableMenuItemByMsgNum(10002, MF_GRAYED);
         poMyMainWin^.poMenu^.EnableMenuItemByMsgNum(10003, MF_GRAYED);
         poMyMainWin^.poMenu^.EnableMenuItemID(65203, MF_GRAYED);
         poMyMainWin^.poMenu^.EnableMenuItemID(65204, MF_GRAYED);
         poMyMainWin^.poMenu^.EnableMenuItemID(65400, MF_GRAYED);
         DrawMenuBar(poMyMainWin^.hWin); // must redraw the menu bar
         end;
      end;

   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_SETFOCUS : begin
      // The status line must change
      with  PMyMDIWindow(poGrParent)^ do begin
         oRichEdit.SetModifyState(oRichEdit.fModified);
         end;

      // give the focus to the rich text.
      SetFocus(oRichEdit.hWin);
      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('Example054');

// Creation of the main window
new(poMyMainWin, Init('tEx054. A little MDI text editor. Very simple to create.'));

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

