{ 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_c.pp  example file.

  In this example, I will remove what has been done in the
  two previous one, and show a simple example having drawings
  which are remembered and redrawn in the WM_PAINT message..

  We can see a little problem, which will be removed in the next example
  When drawing lines, the old ones are not removed.

  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_c;

// 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;
  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 point

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;

//  function PointSelected(nX0,nY0:integer):PShape; virtual;
  {======================================================}
  { search if the point nX0,nY0 belongs to the shape.
    YES => return a pointer on the Shape,
    NO  => return NIL }

  procedure Draw(hDCPaint:HDC); virtual;
  //====================================

//  function DelPoint(var Msg:TMessage):boolean; virtual;
  { Return TRUE if the Shape has to be deleted }

{  procedure WMLButtonDown(var Msg:TMessage); virtual;
  procedure WMLButtonUp(var Msg:TMessage); virtual;
  procedure WMRButtonUP(var Msg:TMessage); virtual;
  procedure WMMouseMove(var Msg:TMessage); 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;

  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;

  constructor Init;
  procedure Draw(hDCPaint:HDC); virtual;
  end; // TMyLine = 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.

  // This part is quite complicate, and will be simpler with the use
  // of the library.
  hPenWin, // A pen to choose the drawing color, size of the line and style.
  hPenOld  // to remeber the old pen used by window.
           : HPEN;

  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;
end;  // TMyPoint.Init

procedure TMyPoint.Draw(hDCPaint:HDC);
//====================================
// Draw a little square around the point
begin
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);
end;  // TMyPoint.Draw


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

constructor TMyLine.Init;
//========================
begin
ptP1.Init;
ptP2.Init;
end;  // TMyLine.Init

procedure TMyLine.Draw(hDCPaint:HDC);
//====================================
// Draw the line
begin
MoveToEx(hDCPaint, ptP1.nX, ptP1.nY, NIL);
lINETo  (hDCPaint, ptP2.nX, ptP2.nY);
end;  // TMyLine.Draw

//***************************************************
// 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;
hPenWin      :=0; // for the moment there is no pen.
hPenOld      :=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(hMenuWin, MF_POPUP, hSubMenu, '&Shapes');

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);

// creation of a Pen, to change its color, width, etc.
hPenWin:=CreatePen(PS_SOLID, 1, colPen);
// 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.
hPenOld:=SelectObject(hDcWin, hPenWin);

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

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

// 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(hPenWin);

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;

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);

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

   // creation of a Pen, to change its color, width, etc.
   hPenWin:=CreatePen(PS_SOLID, 1, colPen);
   // 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.
   hPenOld:=SelectObject(hDcWin, hPenWin);

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

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

   // 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(hPenWin);

   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);

   // 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);

   // 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

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

   // Paint the point.
   hDCWin:=GetDC(hWin);

   // creation of a Pen, to change its color, width, etc.
   hPenWin:=CreatePen(PS_SOLID, 1, colPen);
   // 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.
   hPenOld:=SelectObject(hDcWin, hPenWin);

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

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

   // 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(hPenWin);

   ReleaseDC(hWin, hDCWin);

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

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

end;  // TMyWindow.WMLButtonUp

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

begin
if (wShapeNext = CTE_Line) and (not fLineFirstPoint) then begin
   // Draw a line from the first point to the mouse's position

   // get a device context to draw on the client window
   hDcWin:=GetDC(hWin);

   // creation of a Pen, to change its color, width, etc.
   hPenWin:=CreatePen(PS_SOLID, 1, colPen);
   // 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.
   hPenOld:=SelectObject(hDcWin, hPenWin);

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

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

   // Draw The line
   poLine^.Draw(hDCWin);

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

   // 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(hPenWin);

   ReleaseDC(hWin, 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;

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_Line : ChangeMenuShape(wNrMenu);

   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_c');

// 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 C (Ex019_c.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.

