This is only supporting file for this training; if you have missed this training, you will have to study this from the literature.

The Menu Components

Many programs with more functions use the Main Menu (pulldown menu) function selection. There can be one menu of this type in each form. To create this, choose the appropriate icon ( , the first on the Standard ribbon) and place it anywhere on the form (this symbol of the component presence is not visible after program execution). To activate this, you should find property "mainmenu" of the form (using the Object Inspector) and selet this menu; to set up this function, double-clisk to this symbol on the form (another window appears, so you can fill in the items captions (using Object Inspector) and their functions - the second by use the double-clisk to the menu item (=line).

(there can be more menues in the form, but only one active; using the MainMenu property of the form, you can change the menu, if you have more menues prepared).

You can move in the menu structure by the cursor keys (arrows). If you need to create an item (in the pull-down), which can be expanded, use the Ctrl and the right arrow (cursor key) combination. To add the separation line, add a single dash ("-", minus) to the approptiate caption.

To make menu accessible by Alt+key combination, use the & (ampersant) character before the desired letter in the caption. For create the shortcut key combination, set the ShortCut property in the Object Inspector (recommended to be choosen from a list). The key for fast select and the shortcut for function are two different things.

Only filled items (with some caption) will be displayed after program execution

You can add a small picture (icon) to the menu item by selecting the Bitmap property. This will be displayed after program execution.

Local menu

The second icon on the Standard ribbon is a local (Popup) menu. This menu should be associated with an active component and is displayed by the right mouse button. Each component (on the form) can has own popup menu; there are the PopupMenu property for the most of the components, selectable by the Object Inspector (you should prepare a local menu before select here). The one associated with the form will be accessible only if the cursor will be outside the other components. You can use the same popup menu for more components, or change (by setting the property value) the menu when program is running.

Task: Modify program from the previous lesson so that the image use whole area of the form (set the Allign property of the image to alClient). Add a main menu and prepare functions for load, save, new, color of the pen (use ColorDialog) and the pen width (use a second form with the SpinEdit, or the InputBox function).

(some text missing)

********...

Case

Sometimes, we need to create more branches of the program, depending on a simple condition, usealy on some integer (or more general - ordinal) variable or expression. For this, we can use the case directive:

case <ordinal expression> of
  <value (or values)> : <command>;
  <value (or values)> : <command>;
 ...
  else <command>;
  end;

<ordinal expression> should result in a finite number of values. The program will continue with the first line, where condition is met (use the begin - end brackets for more commands), then ignore the rest of the lines till the end keyword. If no one condition is met and the line with the else keyword is present (not obligatory), then the command after the else will be executed.

Note: instead of value, you can use a list of values, separated by comma (,), or interval (in Pascal, the first value, then double dot (..), and the last value of interval), or combination of both:

case a of
  1,4,7 : s:='left';
  2,0 : s:='down';
  3,6,9..15 : s:='right';
end;

...

Task: the program should somehow switch the mode from free sketch drawing (already solved) to polygon drawind, or the empty or filled rectangle drawing. To save the actual program function, create a global variable mode.

Now, not only OnMouseMove event should be solved - line drawing need to use the OnMouseDown event, for detecting the first mouse click - typically the start or a continue of a polygon.

For changing the program function, we can use a menu, and just set the mode variable. We can assume, that when function is selected, mouse button is not pressed (the left one).

Before definitive position of a line is selected, user should somehow see the actual value. Best and now rather common solution is to draw a line regularly, while keeping the line background, to return the original picture before the next position is drawen. This method is complicated; the next text describe older method, which has been used, when computers had been slow. This older method is to use "xor" function for any informatics drawing. This function will be used, if we set the pmXor drawing mode of the canvas pen. In the next text is explained, how the xor works for keeping yhe old value; before this, remember the xor function for one bit:

picture pen result
a b x
0 0 0
0 1 1
1 0 1
1 1 0

This will be done with each of bits. Before we will wait for the next position, we will draw a line, using pmXor mode. We will get the next result:

original 0 1 1 0 1 0 1 0 0 1
pen 0 0 0 0 1 1 1 1 1 1
result 0 1 1 0 0 1 0 1 1 0

Before changing the line position, we will draw the same line as previous - now, the previous result is in the first line:

last line of previous table 0 1 1 0 0 1 0 1 1 0
pen 0 0 0 0 1 1 1 1 1 1
new result 0 1 1 0 1 0 1 0 0 1

After drawing twice the same object (using pmXor), the picture disappear and the original pisture is reconstructed. Note, that the previous tables describe just 8 bits; in real, we have 24 bits for each of point, many points for a line.

For real use, typical pen color for pmXor is -in binary- all ones, so $FFFFFF. This represents the clWhite color. In some special reason (drawing to monochromatic grayscale picture), different color can be used, for example clYellow ($FFFF00).

In the real situation, the first line should be drawen while function is initiated, and its position has to be saved to a global variable (for a line, it is better to use four integer numbers for coordinates; a special type of variable, which can contain all of them (type TRect) is available, but working with this is more complicated).

When mouse is moved and this "mode" is active, we will redraw (result with erase) the line first, then move the coordinates, then draw again.

When another click is detected (next final point of a line), then the temporal line from previous step should be erased by drawing using pmXor, then the correct color, mode pmCopy and the actual coordinates should be set, then the final line should be drawen. The new coordinates (x1=x2=x, y1=y2=y) and pmXor should be set, as when beginning of this funcion, and the first line should be drawn (note, that we can skip this, because of zero length).

If the user stops the function (value of the "mode" variable should be changed), the last position of the preview line should be erased.

The program example:

var
  Form1: TForm1;
  mode, x0,y0, x1,y1 : integer;
  color : longint;

implementation

The next procedura has been generated by doubleclick in the Object Inspector (Image1 component, the Events, OnMouseMove):

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  case mode of
    0 : {nothing - no function has been selected};
    1 : if ssLeft in Shift then Image1.Canvas.LineTo(X,Y)
                           else Image1.Canvas.MoveTo(X,Y);
        {1 = free drawing}
    2 : begin  {we are "inside" drawing a line; set color and xor:}
          Image1.Canvas.Pen.Mode := pmXor;
          color := Image1.Canvas.Pen.Color; {remember the old value of color}
          Image1.Canvas.Pen.Color := clWhite; {$FFFFFF as a standard solution}
          Image1.Canvas.MoveTo(x0,y0);  {... as a result, the previous line is erased}
          Image1.Canvas.Lineto(x1,y1);
          x1:=x;                          {settings the new position}
          y1:=y;
          Image1.Canvas.MoveTo(x0,y0);  {draw the line in the new position}
          Image1.Canvas.Lineto(x1,y1);
          Image1.Canvas.Pen.Mode := pmCopy;  {pen color and mode restore}
          Image1.Canvas.Pen.Color := color;
        end;
  end {of case};
end;

The next procedure will draw the line on the definitive position, with respect of the first and the last point (the first point is in very different situation, the special "mode" value is needed, to invoke this):

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  case mode of
    2 : begin
          Image1.Canvas.Pen.Mode := pmXor;    {erase the preview line}
          color := Image1.Canvas.Pen.Color;
          Image1.Canvas.Pen.Color := clWhite;
          Image1.Canvas.MoveTo(x0,y0);
          Image1.Canvas.Lineto(x1,y1);
          Image1.Canvas.Pen.Mode := pmCopy;    {setting the definitive color}
          Image1.Canvas.Pen.Color := color;
          Image1.Canvas.MoveTo(x0,y0);         {drawing the definitive line}
          Image1.Canvas.Lineto(X,Y);
          {in the next test, the setting any point twice is used to stop drawing => mode:=0}
          {Note: 1) the expanation in the previous text is different - can you use this? (homewor?)
                 2) the drawing of one additional line of the zero length is not a problem }
          if (X=x0) and (Y=y0) then mode := 0;
          x0:=X;                               {the new starting point is the end of the previou segment of polygon}
          y0:=Y;
          x1:=X;
          y1:=Y;
        end;
    3 : begin  {beginning og the polygon drawing}
          x0:=X;     {the first point settings (zero length line can be drawn, if you like...) }
          y0:=Y;
          x1:=X;
          y1:=Y;
          mode := 2; {for the next point, use the "2" value of "mode"}
        end;   {the rest values of mode do nothing for now...}
  end {of case};
end;

Task: add a function for an ellipse or rectangle drawing.

--- Another task: use the FontDialog and add use of the "TextOut(X,Y,form2.edit1.text);" procedure.