{ Example of program using the GAP library.
  This example shows how to draw text and pictures.
  Windows, as nearly any other Graphical User Interface, do not
  "remember" what was drawn or painted. Each time a window
  was hidden by another one, and comes in from again, the whole
  window must be repaint.

  What is called the Non Client area, i.e. the title on the top, the
  maximize, close etc. buttons, the frame around the window, the background
  of the window (white in general) are painted by windw.
  But the content of what is called the client area, typically where you
  enter a text, or do some drawings, is over your responsibility, or the
  responsibility of Controls.

  Text is never "written" but always "painted" (or "drawn", equivalent.)
  Thus when I say "paint", it includes text.

  If you want to paint directly on the client part of the window,
  there are two moment when it is done.
  First, if you draw directly something, you must draw it without delay.
  For this, you need to get a Device Context, usually with GetDC(hWin),
  then choose among :
  OBJ_BRUSH        Brush
  OBJ_PEN          Pen
  OBJ_FONT         Font
  OBJ_BITMAP       Bitmap
  OBJ_REGION       Region
  OBJ_EXTPEN       Extended pen
  OBJ_PAL          Palette
  and maybe other things like Metafiles.
  Then you can draw, then you have to close your device context.

  Secondly, if another window came over yours, and goes away,
  the system will send a WM_PAINT message wich tells your program it
  should paint again all your drawings.

  This means that the program must "remember" what has been painted directly
  in the client area, to be able to repaint it when receiving the
  WM_Paint message.
  It the program fails to "remember", the drawing will not reapear
  after another window went over the program's window.

  We will see the four things.
  1) Painting always a same text in the client window.
     (When receiving the WM_Paint message)
  2) Painting some figures done by a user and remembered.
     (When receiving the WM_Paint message)
  3) Drawing some figures, which will be memorised.
  4) Drawing free curves, which will not be memorised, and
     not redrawn when the WM_Paint message arrives.

  To see the evolution, various files will be written
  This one is the  tEx019_d.pp  example file.

  This last example, will correct one problem of the last example,
  and add two more shapes.
  The problem is that when creating a line, many lines are drawn
  during creation. They are normal when the complete window is
  redrawn, but during creation, too many lines are drawn.
  To solve it, we will draw using the  SetROP2(HDC, R2_NOTXORPEN);
  funtion, which draws by inversing the color.

  If the background color were black,  SetROP2(HDC, R2_XORPEN);
  should have been used insted.
  For other background colors, it becomes more complicate.

  Also the colors where not remembered.

  In the previous example, we had quite nice shapes, when creating new
  lines. But they disapeared when redrawing the window.
  Here we will add one such new shape, with multi-lines.

  We use also the menu, to have simple selection of some colors
  and put checkmarks on the corresponding menus.

  Later this will be simplified with the library.
  --------------------------------------------}
program tEx019_d;

// 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
 ,wGlobals2
 ,wApplication0
 ,wWindow0
 ,wErrors0
 ,wDebug0;

const
  HUNDERT     = 100;
  NINEHUNDERT  =900;

  CTE_None         = 500;  // it correspond also to the menus
  CTE_Point        = 501;  // next shape wanted is a Point
  CTE_Line         = 502;  // next shape wanted is a Line
  CTE_MultiLine    = 503;  // next shape wanted is a MultiLine
  CTE_Rect         = 504;  // next shape wanted is a rectangle

type
  PMyShape = ^TMyShape;
  TMyShape = object
  //"""""""""""""""
  // An abstract object, parent of all shapes (=fugures = drawings) to
  // be drawn.
  // Each shape must at least know how to draw itself.

  constructor Init;
  destructor Done; virtual;

  procedure Draw(hDCPaint:HDC); virtual;
  //====================================
  end; // TMyShape = object

type
  PMyPoint = ^TMyPoint;
  TMyPoint = object(TMyShape)
  //"""""""""""""""""""""""""
  // Describe a simple point.

  // coordinate of the point. (In pixels, for simplicity of the example)
  nX,
  nY   : int32;

  colPoint : word32;
  // color of the line

  constructor Init;
  procedure Draw(hDCPaint:HDC); virtual;
  end; // TMyPoint = object(TMyShape)

type
  PMyLine = ^TMyLine;
  TMyLine = object(TMyShape)
  //""""""""""""""""""""""""
  // A very simple shape. It is a line
  ptP1,
  ptP2  : TMyPoint;

  colLine : word32;
  // color of the line

  constructor Init;
  procedure Draw(hDCPaint:HDC); virtual;
  end; // TMyLine = object(TMyShape)

type
  PMyMultiLine = ^TMyMultiLine;
  TMyMultiLine = object(TMyShape)
  //"""""""""""""""""""""""""""""
  // Many lines, starting all from the same initial point.
  ptOrg  : TMyPoint;
  // the initial point.

  apPt : array[1..NINEHUNDERT] of TMyPoint;
  // all the second end of the lines.

  nNbLine : int32;
  // number of lines.

  colLine : word32;
  // color of the line

  constructor Init;
  procedure Draw(hDCPaint:HDC); virtual;
  end; // TMyMultiLine = object(TMyMultiShape)

type
  PMyRect = ^TMyRect;
  TMyRect = object(TMyShape)
  //""""""""""""""""""""""""
  // A filled rectangle.
  rRec   : RECT;

  colRect : word32;
  // color of the rectangle

  constructor Init;
  procedure Draw(hDCPaint:HDC); virtual;

  procedure DrawOut(hDCPaint:HDC);
  // Draw the outline of the Rectangle
  end; // TMyRect = object(TMyShape)

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

  fLButtonDown : boolean;
  // is the left button down ?

  hDcWin : HDC;
  // The device context of the client window, to draw on it.

  colPen : word32;
  // Color of the line when drawing

  wColorCheckMark : word32;
  // to remember wich menu has a check mark, corresponding to the
  // color.

  apoShape : array[1..HUNDERT] of PMyShape;
  // Small array, allowing to memorise HUNDERT shapes.

  nNbShapes : int32;
  // number of shapes memorised.

  wShapeNext : word32;
  // kind of shape wanted.

  fLineFirstPoint : boolean;
  // tells when creating a line, if it is the first point.

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

  procedure ChangeMenuColor(wMenuID:word32);
  // Change the painting color,
  // and put a check mark in the menu to tell which is the selected color.

  procedure ChangeMenuShape(wMenuID:word32);
  // Change the Shape
  // and put a check mark in the menu to tell which shape is selected

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

  procedure WMPaint; virtual;
  // the system want the program to repaint the client area.

  procedure WMLButtonDown(wPar, wXPosM, wYPosM:word);
  // a WM_LButtonDown message is treated here, with the position of
  // the mouse given as parameters.

  procedure WMLButtonUp(wPar, wXPosM, wYPosM:word);
  // a WM_LButtonDown message is treated here, with the position of
  // the mouse given as parameters.

  procedure WMMouseMove(wPar, wXPosM, wYPosM:word);
  // treat the mouse movments.

  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 nNotUsed : integer; // only to avoid an error message while compliling in DevPascal 1.9

//***************************************************
// TMyShape = object
//***************************************************

constructor TMyShape.Init;
//========================
begin
end;  // TMyShape.Init

destructor TMyShape.Done;
//=======================
begin
end;  // TMyShape.Done

procedure TMyShape.Draw(hDCPaint:HDC);
//====================================
begin
// The descenden will draw itself.
hDCPaint:=hDCPaint; // to remove a Hint
end;  // TMyShape.Draw

//***************************************************
// TMyPoint = object(TMyShape)
//***************************************************

constructor TMyPoint.Init;
//========================
begin
nX:=0;
nY:=0;
colPoint :=RGB(0,0,0);
end;  // TMyPoint.Init

procedure TMyPoint.Draw(hDCPaint:HDC);
//====================================
// Draw a little square around the point
var
  hPenWinL, // A pen to choose the drawing color, size of the line and style.
  hPenOldL  // to remeber the old pen used by window.
           : HPEN;
begin
// creation of a Pen, to change its color, width, etc.
hPenWinL:=CreatePen(PS_SOLID, 1, colPoint);
// Other first parameter could be :
// PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFRAME

// Use this pen in the device context and
// remember the handle on the old pen.
hPenOldL:=SelectObject(hDCPaint, hPenWinL);

MoveToEx(hDCPaint, nX-3, nY-3, NIL);
lINETo  (hDCPaint, nX+3, nY-3);
lINETo  (hDCPaint, nX+3, nY+3);
lINETo  (hDCPaint, nX-3, nY+3);
lINETo  (hDCPaint, nX-3, nY-3);

// must give back the old pen, before destroying the used pen.
SelectObject(hDCPaint, hPenOldL);

// Detroy the pen, to free resources.
// If you forget to do this, Windows resources will go down,
// and after a while, the complete system will crash.
DeleteObject(hPenWinL);
end;  // TMyPoint.Draw

//***************************************************
// TMyLine = object(TMyShape)
//***************************************************

constructor TMyLine.Init;
//========================
begin
ptP1.Init;
ptP2.Init;
colLine:=RGB(0,0,0);
end;  // TMyLine.Init

procedure TMyLine.Draw(hDCPaint:HDC);
//====================================
// Draw the line
// It is quite complicate to only select the color of the line !!!
var
  hPenWinL, // A pen to choose the drawing color, size of the line and style.
  hPenOldL  // to remeber the old pen used by window.
           : HPEN;

begin
// creation of a Pen, to change its color, width, etc.
hPenWinL:=CreatePen(PS_SOLID, 1, colLine);
// Other first parameter could be :
// PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFRAME

// Use this pen in the device context and
// remember the handle on the old pen.
hPenOldL:=SelectObject(hDCPaint, hPenWinL);

MoveToEx(hDCPaint, ptP1.nX, ptP1.nY, NIL);
LineTo  (hDCPaint, ptP2.nX, ptP2.nY);

// must give back the old pen, before destroying the used pen.
SelectObject(hDCPaint, hPenOldL);

// Detroy the pen, to free resources.
// If you forget to do this, Windows resources will go down,
// and after a while, the complete system will crash.
DeleteObject(hPenWinL);
end;  // TMyLine.Draw

//***************************************************
// TMyMultiLine = object(TMyShape)
//***************************************************

constructor TMyMultiLine.Init;
//========================
begin
ptOrg.Init;
nNbLine:=0;
colLine:=RGB(0,0,0);
end;  // TMyMultiLine.Init

procedure TMyMultiLine.Draw(hDCPaint:HDC);
//====================================
// Draw the line
// It is quite complicate to only select the color of the line !!!
var
  hPenWinL, // A pen to choose the drawing color, size of the line and style.
  hPenOldL  // to remeber the old pen used by window.
           : HPEN;
  nn       : integer;

begin
// creation of a Pen, to change its color, width, etc.
hPenWinL:=CreatePen(PS_SOLID, 1, colLine);
// Other first parameter could be :
// PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFRAME

// Use this pen in the device context and
// remember the handle on the old pen.
hPenOldL:=SelectObject(hDCPaint, hPenWinL);

// Draw all the lines.
for nn:=1 to nNbLine do begin
   MoveToEx(hDCPaint, ptOrg.nX, ptOrg.nY, NIL);
   LineTo  (hDCPaint, apPt[nn].nX, apPt[nn].nY);
   end;

// must give back the old pen, before destroying the used pen.
SelectObject(hDCPaint, hPenOldL);

// Detroy the pen, to free resources.
// If you forget to do this, Windows resources will go down,
// and after a while, the complete system will crash.
DeleteObject(hPenWinL);
end;  // TMyMultiLine.Draw

//***************************************************
// TMyRect = object(TMyShape)
//***************************************************

constructor TMyRect.Init;
//========================
begin
rRec.Left  :=0;
rRec.Top   :=0;
rRec.Right :=0;
rRec.Bottom:=0;
colRect:=RGB(0,0,0);
end;  // TMyRect.Init

procedure TMyRect.Draw(hDCPaint:HDC);
//========================================
// Draw the rectangle
// It is quite complicate to only to fill a rectangle with a single color
var
  rLogBr      : LOGBRUSH;
  hBrOld,     // remember the actual brush color
  hBr         : HBRUSH;

begin
// to fill a region, you must use a Brush.
rLogBr.lbStyle:=BS_SOLID; // this is the easyest, BS_HATCHED would be another possibility.
rLogBr.lbColor:=colRect;
rLogBr.lbHatch:=HS_VERTICAL; // not used, since BS_Solid

// create a new brush
hBr:=CreateBrushIndirect(rLogBr);

// Use this brush in the device context and
// remember the handle on the old pen.
hBrOld:=SelectObject(hDCPaint, hBr);

// fills the rectangle

FillRect(hDCPaint, rRec, hBr);

// must give back the old pen, before destroying the used pen.
SelectObject(hDCPaint, hBrOld);

// Detroy the brush, to free resources.
// If you forget to do this, Windows resources will go down,
// and after a while, the complete system will crash.
DeleteObject(hBr);
end;  // TMyRect.Draw

procedure TMyRect.DrawOut(hDCPaint:HDC);
//======================================
// Draw the outline of the Rectangle
// It is quite complicate to only select the color of the line !!!
var
  hPenWinL, // A pen to choose the drawing color, size of the line and style.
  hPenOldL  // to remeber the old pen used by window.
           : HPEN;

begin
// creation of a Pen, to change its color, width, etc.
hPenWinL:=CreatePen(PS_SOLID, 1, colRect);
// Other first parameter could be :
// PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFRAME

// Use this pen in the device context and
// remember the handle on the old pen.
hPenOldL:=SelectObject(hDCPaint, hPenWinL);

MoveToEx(hDCPaint, rRec.Left,    rRec.Top, NIL);
LineTo  (hDCPaint, rRec.Right,   rRec.Top);
LineTo  (hDCPaint, rRec.Right,   rRec.Bottom);
LineTo  (hDCPaint, rRec.Left,    rRec.Bottom);
LineTo  (hDCPaint, rRec.Left,    rRec.Top);

// must give back the old pen, before destroying the used pen.
SelectObject(hDCPaint, hPenOldL);

// Detroy the pen, to free resources.
// If you forget to do this, Windows resources will go down,
// and after a while, the complete system will crash.
DeleteObject(hPenWinL);
end;  // TMyRect.DrawOut

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

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

// As always, it is best to initialise the members (=properties)
fLButtonDown :=FALSE;
hDcWin       :=0;
colPen       :=RGB(0,0,0); // black by default.

wColorCheckMark   :=301; // menu corresponding to black.

nNbShapes  :=0;
wShapeNext :=CTE_None;
fLineFirstPoint:=TRUE;
end;  // TMyWindow.Init

destructor TMyWindow.Done;
//========================
begin
inherited Done;
end;  // TMyWindow.Done

procedure TMyWindow.ChangeMenuColor(wMenuID:word32);
//==================================================
// Change the painting color,
// and put a check mark in the menu to tell which is the selected color.
begin
// remove the checkmark from the menu
CheckMenuItem(hMenuWin, wColorCheckMark, MF_BYCOMMAND or MF_UNCHECKED);

// Set the new the checkmark
wColorCheckMark:=wMenuID;
CheckMenuItem(hMenuWin, wColorCheckMark, MF_BYCOMMAND or MF_CHECKED);

// change the pen color
case wMenuID of
   301 : colPen:=RGB(  0,  0,  0);
   302 : colPen:=RGB(255,  0,  0);
   303 : colPen:=RGB(  0,255,  0);
   304 : colPen:=RGB(  0,  0,255);
   305 : colPen:=RGB(128,128,  0);
   306 : colPen:=RGB(128,  0,128);
   307 : colPen:=RGB(  0,128,128);
   308 : colPen:=RGB(255,255,255);
   end;
end;  // TMyWindow.ChangeMenuColor

procedure TMyWindow.ChangeMenuShape(wMenuID:word32);
//==================================================
// Change the Shape
// and put a check mark in the menu to tell which shape is selected
begin
// remove the checkmark from the menu
CheckMenuItem(hMenuWin, wShapeNext, MF_BYCOMMAND or MF_UNCHECKED);

// Set the new the checkmark
wShapeNext:=wMenuID;
CheckMenuItem(hMenuWin, wShapeNext, MF_BYCOMMAND or MF_CHECKED);
end;  // TMyWindow.ChangeMenuShape

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

hSubMenu := CreateMenu;
AppendMenu(hSubmenu,MF_STRING,100,'&Open debug window');
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, CTE_None, '&0 None');
AppendMenu(hSubmenu,MF_STRING, CTE_Point, '&1 A Point');
AppendMenu(hSubmenu,MF_STRING, CTE_Line, '&2 A Line');
AppendMenu(hSubmenu,MF_STRING, CTE_MultiLine, '&3 A Multi Lines');
AppendMenu(hSubmenu,MF_STRING, CTE_Rect, '&4 A Rectangle');
AppendMenu(hSubmenu,MF_SEPARATOR,0,Nil);
AppendMenu(hSubmenu,MF_STRING, 601, '&Delete last shape');
AppendMenu(hSubmenu,MF_STRING, 602, 'Delete &previous last shape');
AppendMenu(hSubmenu,MF_STRING, 603, 'Delete 2nd p&revious last shape');
AppendMenu(hSubmenu,MF_STRING, 604, 'Delete 3rd pr&evious last shape');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&Shapes');
// we need a dialog box to select really the shape to delete.

hSubMenu := CreateMenu;
AppendMenu(hSubmenu,MF_STRING, 301, '&Black');
AppendMenu(hSubmenu,MF_STRING, 302, '&Red');
AppendMenu(hSubmenu,MF_STRING, 303, '&Green');
AppendMenu(hSubmenu,MF_STRING, 304, '&Blue');
AppendMenu(hSubmenu,MF_STRING, 305, 'Br&own');
AppendMenu(hSubmenu,MF_STRING, 306, '&Magenta');
AppendMenu(hSubmenu,MF_STRING, 307, '&Cyan');
AppendMenu(hSubmenu,MF_STRING, 308, '&White');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&Colors');

hSubMenu := CreateMenu;
AppendMenu(hSubmenu,MF_STRING, 401, 'Info &1...');
AppendMenu(hMenuWin, MF_POPUP, hSubMenu, '&Help');

// Set a checkmark corresponding to the black color
CheckMenuItem(hMenuWin, wColorCheckMark, MF_BYCOMMAND or MF_CHECKED);

// Set a checkmark corresponding to no shapes
CheckMenuItem(hMenuWin, wShapeNext, MF_BYCOMMAND or MF_CHECKED);
end;  // TMyWindow.MenuCreate

procedure TMyWindow.WMPaint;
//==========================
Var
  hDCPaint    : hdc;
  rPS         : paintstruct;
  nn          : integer; // to draw all the shapes

begin
// Get a device context.
// Here we have a facility, during the WMPaint, we must always
// begin with  "BeginPaint", which gives a Device Context to paint, and
// end with "EndPaint", to tell that the painting is done.
hDCPaint:=BeginPaint(hWin, @rPS);

// Paint all the shapes.
for nn:=1 to nNbShapes do apoShape[nn]^.Draw(hDCPaint);

EndPaint(hWin, rPS);
end;  // TMyWindow.WMPaint

procedure TMyWindow.WMLButtonDown(wPar, wXPosM, wYPosM:word);
//===========================================================
// a WM_LButtonDown message is treated here, with the position of
// the mouse given as parameters.
begin
wPar:=wPar; wXPosM:=wXPosM; wYPosM:=wYPosM; // to avoid a Hint
fLButtonDown:=TRUE;
// button is down
// it will be used in the  WMLButtonUp  methode.
end;  // TMyWindow.WMLButtonDown

procedure TMyWindow.WMLButtonUp(wPar, wXPosM, wYPosM:word);
//==========================================================
// a WM_LButtonDown message is treated here, with the position of
// the mouse given as parameters.
// It is at button up that the point is marked.
var
  poPoint     : PMyPoint;
  poLine      : PMyLine;
  poMLine     : PMyMultiLine;
  poRect      : PMyRect;

begin
wPar:=wPar; // to avoid a Hint
if (not fLButtonDown) then exit; // button was not down in this window before
fLButtonDown:=FALSE;

if (nNbShapes >= HUNDERT) then exit; // no more than HUNDERT shapes are allowed.

if (wShapeNext = CTE_Point) then begin
   // We have one more shape
   inc(nNbShapes);

   // create a new point
   new(poPoint, Init);

   // put this new point in the array of shapes.
   apoShape[nNbShapes]:=poPoint;

   // position of the new point.
   poPoint^.nX:=int32(wXPosM);
   poPoint^.nY:=int32(wYPosM);
   poPoint^.colPoint:=colPen;

   // Paint the point. It is quit long...
   hDCWin:=GetDC(hWin);

   // Ask the point to draw itself
   poPoint^.Draw(hDCWin);

   ReleaseDC(hWin, hDCWin);
   end; // if (wShapeNext = CTE_Point) then begin

if (wShapeNext = CTE_Line) and (fLineFirstPoint) then begin
   // We have one more shape
   inc(nNbShapes);

   // create a new line
   new(poLine, Init);

   // put this new point in the array of shapes.
   apoShape[nNbShapes]:=poLine;

   // position of one end of the line
   poLine^.ptP1.nX:=int32(wXPosM);
   poLine^.ptP1.nY:=int32(wYPosM);

   // For the moment, the second point is the same
   poLine^.ptP2.nX:=int32(wXPosM);
   poLine^.ptP2.nY:=int32(wYPosM);
   poLine^.colLine:=colPen;

   // the mouse will belong to this application, where ever it is
   // no other applications will receive messages from the mouse.
   // it will be free, when the second point will have been choosen
   SetCapture(hWin);

   // Get a device context to paint the point while the mouse moves.
   hDCWin:=GetDC(hWin);

   // this makes all the difference when drawing lines.
   // It will inverse the color of the pixels which are used
   // to draw the line
   SetROP2(hDcWin, R2_NOTXORPEN);

   // draw the line, even if it is only one pixel.
   poLine^.Draw(hDCWin);

   // It is not any more the first point
   fLineFirstPoint:=FALSE;
   end // if (wShapeNext = CTE_Line) then begin
else if (wShapeNext = CTE_Line) and (not fLineFirstPoint) then begin
   // the second point of the line has been determined

   // mouse messages are sended to the window under it.
   // but a button down is also received by another window,
   // if the cursor is over it.
   ReleaseCapture;

   // define the second point of the line
   poLine:=PMyLine(apoShape[nNbShapes]); // we know that this object is a line

   // Cancel the last drawing of the line
   apoShape[nNbShapes]^.Draw(hDCWin);

   poLine^.ptP2.nX:=int32(wXPosM);
   poLine^.ptP2.nY:=int32(wYPosM);

   // Ask the line to draw itself
   apoShape[nNbShapes]^.Draw(hDCWin);

   ReleaseDC(hWin, hDCWin);

   // Next time it will again be a new line
   fLineFirstPoint:=TRUE;

   end; // if (wShapeNext = CTE_Line) and (not fLineFirstPoint) then begin

if (wShapeNext = CTE_MultiLine) and (fLineFirstPoint) then begin
   // We have one more shape
   inc(nNbShapes);

   // create a new multi-line
   new(poMLine, Init);

   // put this new point in the array of shapes.
   apoShape[nNbShapes]:=poMLine;

   // position of one end of the line
   poMLine^.ptOrg.nX:=int32(wXPosM);
   poMLine^.ptOrg.nY:=int32(wYPosM);

   poMLine^.nNbLine :=1; // there is at least one line

   // For the moment, the second point is the same
   poMLine^.apPt[1].nX:=int32(wXPosM);
   poMLine^.apPt[1].nY:=int32(wYPosM);
   poMLine^.colLine:=colPen;

   // the mouse will belong to this application, where ever it is
   // no other applications will receive messages from the mouse.
   // it will be free, when the second point will have been choosen
   SetCapture(hWin);

   // Get a device context to paint the point while the mouse moves.
   hDCWin:=GetDC(hWin);

   // this makes all the difference when drawing lines.
   // It will inverse the color of the pixels which are used
   // to draw the line
//   SetROP2(hDcWin, R2_NOTXORPEN);  Do not this for a multi-line

   // It is not any more the first point
   fLineFirstPoint:=FALSE;
   end // if (wShapeNext = CTE_MultiLine) then begin
else if (wShapeNext = CTE_MultiLine) and (not fLineFirstPoint) then begin
   // Finished to add lines in the multi-line

   // mouse messages are sended to the window under it.
   // but a button down is also received by another window,
   // if the cursor is over it.
   ReleaseCapture;

   ReleaseDC(hWin, hDCWin);

   // Next time it will again be a new multi-line
   fLineFirstPoint:=TRUE;
   end; // if (wShapeNext = CTE_MultiLine) and (not fLineFirstPoint) then begin

if (wShapeNext = CTE_Rect) and (fLineFirstPoint) then begin
   // We have one more shape
   inc(nNbShapes);

   // create a new line
   new(poRect, Init);

   // put this new point in the array of shapes.
   apoShape[nNbShapes]:=poRect;

   // position of one end of the line
   poRect^.rRec.Left  :=int32(wXPosM);
   poRect^.rRec.Top   :=int32(wYPosM);

   // For the moment, the second point is the same
   poRect^.rRec.Right :=int32(wXPosM);
   poRect^.rRec.Bottom:=int32(wYPosM);
   poRect^.colRect:=colPen;

   // the mouse will belong to this application, where ever it is
   // no other applications will receive messages from the mouse.
   // it will be free, when the second point will have been choosen
   SetCapture(hWin);

   // Get a device context to paint the point while the mouse moves.
   hDCWin:=GetDC(hWin);

   // this makes all the difference when drawing lines.
   // It will inverse the color of the pixels which are used
   // to draw the line
   SetROP2(hDcWin, R2_NOTXORPEN);

   // draw the rectangle, even if it is only one pixel.
   poRect^.DrawOut(hDCWin);

   // It is not any more the first point
   fLineFirstPoint:=FALSE;
   end // if (wShapeNext = CTE_Rect) then begin
else if (wShapeNext = CTE_Rect) and (not fLineFirstPoint) then begin
   // the second point of the line has been determined

   // mouse messages are sended to the window under it.
   // but a button down is also received by another window,
   // if the cursor is over it.
   ReleaseCapture;

   // define the second point of the line
   poRect:=PMyRect(apoShape[nNbShapes]); // we know that this object is a rectangle

   // Cancel the last drawing of the rectangle
   poRect^.DrawOut(hDCWin);

   poRect^.rRec.Right  :=int32(wXPosM);
   poRect^.rRec.Bottom :=int32(wYPosM);

   // Ask the rectangle to draw itself
   poRect^.Draw(hDCWin);

   ReleaseDC(hWin, hDCWin);

   // Next time it will again be a new line
   fLineFirstPoint:=TRUE;

   end; // if (wShapeNext = CTE_Rect) and (not fLineFirstPoint) then begin
end;  // TMyWindow.WMLButtonUp

procedure TMyWindow.WMMouseMove(wPar, wXPosM, wYPosM:word);
//==========================================================
// treat the mouse movments.
var
  poLine      : PMyLine;
  poMLine     : PMyMultiLine;
  poRect      : PMyRect;

begin
wPar:=wPar; // ro remove a Hint
if (wShapeNext = CTE_Line) and (not fLineFirstPoint) then begin
   // Draw a line from the first point to the mouse's position
   // The device context has already been opened.

   // new temporary position for the second point
   poLine:=PMyLine(apoShape[nNbShapes]); // we know that this object is a line

   // redraw over the old line, to cancel its drawing.
   poLine^.Draw(hDCWin);

   poLine^.ptP2.nX:=int32(wXPosM);
   poLine^.ptP2.nY:=int32(wYPosM);

   // Draw The line
   poLine^.Draw(hDCWin);
   end; // if (wShapeNext = CTE_Line) then begin

if (wShapeNext = CTE_MultiLine) and (not fLineFirstPoint) then begin
   // define other lines of the multi-line
   poMLine:=PMyMultiLine(apoShape[nNbShapes]); // we know that this object is a multi line

   if (poMLine^.nNbLine < NINEHUNDERT) then begin
      inc(poMLine^.nNbLine); // there is one more line in the multi-line

      // New line, define by one end of the line
      poMLine^.apPt[poMLine^.nNbLine].nX:=int32(wXPosM);
      poMLine^.apPt[poMLine^.nNbLine].nY:=int32(wYPosM);

      // Ask the line to draw itself
      poMLine^.Draw(hDCWin);
      end;
   end; // if (wShapeNext = CTE_MultiLine) and (not fLineFirstPoint) then begin

if (wShapeNext = CTE_Rect) and (not fLineFirstPoint) then begin
   // Draw a rectangle from the first point to the mouse's position
   // The device context has already been opened.

   // new temporary position for the second point
   poRect:=PMyRect(apoShape[nNbShapes]); // we know that this object is a rectangle

   // redraw over the old line, to cancel its drawing.
   poRect^.DrawOut(hDCWin);

   poRect^.rRec.Right  :=int32(wXPosM);
   poRect^.rRec.Bottom :=int32(wYPosM);

   // Draw The line
   poRect^.DrawOut(hDCWin);
   end; // if (wShapeNext = CTE_Line) then begin
end;  // TMyWindow.WMMouseMove

function TMyWindow.WMCommand(wPar:WPARAM; lPar:LPARAM):LRESULT;
//=============================================================
// Handles the menu messages
Var
//  wNoticode,  // notification code, from a child window.
  wNrMenu     : word;
  rRec        : RECT;
  nn          : integer;

begin
WMCommand:=0; // default return value
// wNotiCode := HiWord(wPar);

{DBW0(64, DBNotifToStr(wNotiCode) + ITS(LoWord(wPar))); }

if (lPar <> 0) then begin
   // call the parent WMCommand method.
   WMCommand:=inherited WMCommand(wPar, lPar);
   exit;
   end;

// for the menus,  lPar = 0
//=========================
wNrMenu   := LoWord(wPar);
Case wNrMenu Of
   // these calls to DBW0 can be done from anywhere
   100 : DBW0(0, '');  // just open the debugging window

   200 : PostMessage( hWin,WM_Close,0,0);

   // Some colors to paint.
   301..308 : ChangeMenuColor(wNrMenu);

   401 : MessageBox( hWin,
       'Nothing to say yet.',
       'No help for the moment',
                     MB_OK Or MB_IconInformation);

  CTE_None..CTE_Rect : ChangeMenuShape(wNrMenu);

  601..610 : begin
     // delete the last-(wNrMenu-601) shape
     if (nNbShapes <= (wNrMenu-601) ) then exit; // nothing to delete

     // moves the shapes in the array of shapes
     for nn:=nNbShapes - (wNrMenu-601) to nNbShapes-1 do begin
        apoShape[nn]:=apoShape[nn+1];
        end;

     // one less shape
     dec(nNbShapes );

     // ask to redraw the complete window
     GetClientRect(hWin, rRec); // get the size of the client area, to redraw it
     invalidateRect(hWin, rRec, TRUE); // TRUE => redraw the backgroud (white) also
     end;

   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.
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
   WM_Paint   : WMPaint; // must redraw the client area part.
   WM_Command : WindowProc:=WMCommand(wPar, lPar);

   WM_LButtonDown : WMLButtonDown(wPar, LoWord(lPar), HiWord(lPar));
   WM_LButtonUp   : WMLButtonUp(wPar, LoWord(lPar), HiWord(lPar));
   WM_MouseMove   : WMMouseMove(wPar, LoWord(lPar), HiWord(lPar));

   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

var
  oApp        : TApplication0;
  poMyMainWin : PMyWindow;

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

// 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('Title of the Example019 D (Ex019_d.pp) Window'));

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

