Chapter 3 (continued)

Basic Event Handling Routines

The three logical functions necessary for building a graphical user interface include (1) pointing, (2) position sensing, and (3) action sensing. We are assuming the availability of a WIMP system shell in which case the pointing device is the mouse/cursor. The mouse sensing interrupt on systems such as the Macintosh and PC Windows operates at the highest priority level. That is, the mouse is "alive" and the cursor responds to mouse motion even when other processes are in operation. In fact, on such systems the quickest test for system failure is an unresponsive mouse. So the first function, pointing, is automatically available on all WIMP systems.

The second and third functions, position sensing and action sensing, are provided through functions at the language or systems level. Additional Pascal routines are available for event handling, timing purposes, and sound generation. A summary of the basic interactive functions is presented in Table 3.3.

A quick scan of Table 3.3 suggests that these functions from the Event Manager are more than adequate for handling the interactive dialog for GUIs and graphics applications. The next step, therefore, is to implement several of these functions in the simple demonstration programs, ButtonEvents and ReadPosition.


Button Messages Algorithm

In ButtonEvents the use of two interactive routines, Button and TickCounts, is illustrated. These procedures are used to detect the three messages:

  • Button down message,
  • Button up message,
  • Double-click message.


  • Table 3.3
    Event Handling Routines
    Object
    Pascal Syntax
    Example Call
    Description
    Button
    Function Button:Boolean if Button then º  

    elseº 

    The Button function returns True if the mouse button is down; False otherwise 
    Button
    Function StillDown: Boolean  if StillDown thenº  

    elseº 

    Returns True if the button is still down from the original press; False if it has been released or released and repressed (i.e., mouse event on event queue) 
    Button
    Function WaitMouseUp:Boolean  if WaitMouseUp thenº  

    elseº 

    Same as StillDown, but removes previous mouse event from queue before returning False 
    Mouse cursor
    Procedure GetMouse(var mouseLoc:point)  GetMouse(p); Returns the present mouse cursor position in local coordinates as a Point type 
    Keyboard
    Procedure GetKeys(var theKeys:KeyMap)  GetKeys(key); Reads the current state of the keyboard and returns a keyMap, a Packed Array[1..127] of Boolean 
    Clock
    Function TickCount:LongInt if TickCount<60 thenº  Returns the elapsed time in ticks (1/60th sec) since last TickCount call 
    Event
    Function GetNextEvent  

    (eventMask:Integer;var theEvent:EventRecord): Boolean 

    if GetNextEvent(2,Rec)  

    thenº  

    elseº 

    A logical function which returns True if an event of the requested type exists on the event queue; False otherwise. If True, it also returns a descriptive record of the event. (type, location, time, conditions, etc) 

     
    Here is a simple algorithm which accomplishes these tasks.
     

    Pascal Program ButtonEvents 


    program ButtonEvents;
    {Program to demonstrate detection of button events }
    {and use of button for program control. }

    var
    tic1, tic2: longint;

    begin

    {Open Drawing Window and label screen.}
    ShowDrawing;
    MoveTo(20, 20);
    TextSize(18);
    WriteDraw('Button Event Test');
    MoveTo(35, 40);
    TextSize(12);


    WriteDraw('Double-Click to QUIT');
    MoveTo(40, 70);
    WriteDraw('Now the Button is: ');

    {Use XOR pattern to erase and rewrite output}
    TextMode(srcXor);
    MoveTo(80, 100);
    TextSize(24);
    TextFace([bold]);

    repeat {Until we double-click}
    while button do {Button down message detector}
    begin
    WriteDraw('down');
    MoveTo(80, 100);
    repeat {Tight loop until button up}
    until not Button;
    WriteDraw('down'); {Erase "down" text}
    MoveTo(80, 100);
    tic1 := TickCount; {Sample system clock: }
    end; {1/60 sec ticks}

    while not button do { Button up message detector}
    begin
    WriteDraw('up');
    MoveTo(80, 100);
    repeat {Tight loop until button down}
    until button;
    WriteDraw('up'); {Erase "up" text}
    MoveTo(80, 100);
    tic2 := TickCount; {Sample system clock}
    end;

    until abs(tic2 - tic1) < 30; {Double click message detector}

    end.


    The main features and new concepts introduced in ButtonEvents include:

  • A formatting section in which the Drawing window is opened, font sizes selected (in points), and the figure labeled.

  • The pen mode set to exclusive or (Xor) for purposes of carrying out the erase text function. Text erase is accomplished simply by writing the text a second time in the same location. The Xor function changes the previous black pixels of the text to white, effectively erasing what was written the first time.

  • A repeat block designed to be exited by double-clicking the mouse button. The difference between tic2 and tic1 is the number of (1/60)ths of a second (ticks) between the release of the mouse button and the next press of the button. This number is an effective measure of how rapidly the mouse button is double-clicked. By setting the limit to 30 we determine that the double-click message will be recognized if the time between clicks is shorter than about 1/2 second.

  • Two while loops corresponding to the two logical possibilities -- button-down or button-up. When the button-down message is received, it is acknowledged by writing "down" to the Drawing window and waiting for the button state to change with the not Button test. The Button-up loop is identical to the Button-down loop with the interchange of the not Button ´ Button tests.

  • The result of this algorithm is a button interpreting program which displays the current state of the mouse button, precisely tracking button presses and releases. In addition, it provides double-click recognition and uses it for a graceful exit of the program. Figure 3.15 shows the program output when the button is pressed.



    Figure 3.15
    Output of program, ButtonEvents. It responds to ButtonUp, ButtonDown, and DoubleClick messages with methods which acknowledge the button state or exit the program. 




    Cursor Position Algorithm

    Frequently, it is as important to know where an event has happened as it is to know that it has happened. This logical function of pointing is carried out on most graphics workstations with the mouse cursor, although earlier systems used light pens, joy sticks, data tablets, and even human fingers for this purpose. The essential function of all such devices is to provide an interactively controlled cursor, along with software routines for reading its (x,y) position on the screen.

    The next algorithm, Read_Position, demonstrates the use of the mouse cursor with the routine, GetMouse, for performing the cursor reading operation and using the results of this operation for program control as shown in Figure 3.16.



    Figure 3.16
    Output of Read_Position program. As the user moves the cursor about the screen, the X and Y "odometer" panels continuously monitor its location. The present cursor position, (x,y), is reported upon pressing the mouse button. Clicking in the Quit box causes program termination.  




    Pascal Program Read_Position 


    program Read_Position;

    {Program to demonstrate the GetMouse procedure }

    {and use it to implement a position measuring } algorithm and for program control via the Quit box }

    var

    x, y: integer;
    xrect, yrect, QRect: rect;
    Pt: Point;
    begin

    {Open Drawing Window and label screen.}

    ShowDrawing;
    MoveTo(20, 20);
    TextSize(18);
    WriteDraw('Position Reading Test');
    MoveTo(35, 40);
    TextSize(12);
    WriteDraw('Click in Quit-Box to QUIT');
    MoveTo(40, 70);
    WriteDraw('Now the Cursor is at: ');
    {Now draw the "speedometer boxes" for x & y position}
    {and the Quit box to terminate program operation }
    MoveTo(40, 90);
    WriteDraw(' X Y Quit');
    SetRect(xRect, 40, 95, 90, 115);
    SetRect(yRect, 100, 95, 150, 115);
    SetRect(QRect, 160, 95, 210, 115);
    FrameRect(QRect);
    PenSize(2,2);
    repeat
    GetMouse(Pt); {This reads the cursor position}
    x := Pt.h; {as a 2 component vector of }
    y := Pt.v; {type POINT = (v,h) }
    MoveTo(52, 110);
    WriteDraw(x : 3); {x value in pixels}
    MoveTo(112, 110);
    WriteDraw(y : 3); {y value in pixels}
    if button then {If button pressed, then}
    begin {Plot and label point}
    MoveTo(x, y);
    DrawLine(x,y,x,y);
    WriteDraw('(', x : 3, ', ', y : 3, ')');
    end;
    EraseRect(xRect); {Must erase rectangles}
    EraseRect(yRect); {to avoid overwriting all}
    FrameRect(xRect); {values of measurements}
    FrameRect(yRect); {then redraw rectangles}
    until (PtInRect(Pt, QRect) and Button); {Click}

    end. {in Quit Box}


    Figure 3.16 illustrates some of the output possible with the Read-Position program. Several novel features introduced in this program include:

  • Dynamic, continuous read-out "odometer" type displays of the x and y coordinates of the cursor throughout the lifetime of the program. These are very useful in exploring the pixel dimensions of the Drawing window and even regions outside of this window. Note that in this application the required erase-before-writing function is performed by the EraseRect procedure.

  • Option for measuring any point within the Drawing window, plotting its point permanently, and recording the coordinates of the plotted point next to it on the screen in an (x,y) format. The message to measure and display the present cursor position is sent by clicking the mouse button.

  • Program control via a menu box labeled Quit. To gracefully exit the program, the user simply moves the cursor into the Quit box and clicks the mouse button. The exit message is detected by the elegant combination of PtInRect (Point in rectangle) and Button functions.

  •  

    ButtonEvents and ReadPosition should suggest to the reader the enormous potential of even simple routines like Button and GetMouse for interactive GUI design. Once the user can point to any object on the screen and signal an action based on the object to which she/he is pointing, a whole new world of possibilities opens up. In fact, it can be shown, as we suggest in the next section, that all six of the CORE input device classes can be implemented almost trivially by combinations of these two functions.
     
     

    CORE Input Device Classification

    As we indicated in the previous chapter's discussion of standards, the ACM CORE graphics system proposed the following six classes of logical input devices.

    1. Button device - indicates a choice, returning an integer, or selecting a menu item. Logical button devices are typically implemented using mouse buttons or function keys. A PC mouse provides two or three unique buttons, and the Macintosh mouse has a single button which can simulate additional button commands through double and triple clicking. Both systems can simulate additional button commands through picking virtual buttons drawn on the screen.

    2. Pick device - selects user-defined objects on the screen. Typically, a logical pick device is implemented by a combination of continuously reading the cursor position plus event selection by pressing the button when the cursor is near or on the desired object. The Quit box in Read_Position illustrates the use of a pick device.

    3. Keyboard device - returns a character string. Typically this logical device is implemented through a real keyboard, generally including function keys and keypad. However, this logical function can also be readily performed by using the mouse/cursor to select keys from a virtual keyboard. An example familiar to every Macintosh user is the Key Caps desk accessary shown in Figure 3.17. Typing on this keyboard with the mouse generates the string of characters shown in the window at the top of the keyboard.

    4. Valuator device - returns numerical values. This logical device is readily implemented, for example, by drawing a horizontal scale along which the user slides the cursor. Other valuator devices may be implemented by dragging meter needles and twisting control knobs graphically.

    5. Locator device - returns the (x,y) coordinates of a point on the graphics screen. This logical functions is implemented precisely with the GetMouse function demonstrated in Read_Position. What else is there to say?

    6. Stroke device - returns a sequence of (x,y) coordinates. This logical function was implemented by the x and y "speedometer" boxes on the Drawing window in ReadPosition.

    A number of graphics text writers have observed that any of these logical interactive devices may be "mapped onto"Ûor implemented by)Ûany real input device. For instance, a keyboard with cursor arrow keys may be used to perform these logical functions for languages and platforms not supporting mice (such as pre-Windows Turbo Pascal). This is roughly equivalent toÛand about as convenient asÛusing the mouse for generating a character string, a feat neatly accomplished in Figure 3.17.


    Figure 3.17
    Generating a character string using a mouse to type on a virtual keyboard. Using the mouse to implement a keyboard logical device is somewhat like using keyboard cursor arrows to implement a locator device. 




    Interactive Construction Techniques

    Now that we have built up a repertoire of interactive techniques, the tools are at hand for writing useful interactive programs. A large class of interactive graphics programs involves the use of interactive devices such as the mouse to construct graphical objects on the screen. This class includes all painting, drawing, drafting, CAD, and finite element programs. A useful set of interactive techniques have evolved for making the task of constructing objects simpler, faster, and more accurate. Most of these interactive construction techniques involve use of the logical devices and interactive techniques demonstrated above.

    Four of the most general interactive construction techniques include:

  • Gridding - Most construction applications include a grid option. When the grid option is turned on, lines are constrained to start and stop at the grid points, and the bounding box of more complex graphical objects snap from one grid point to the next. This assures physical continuity of the object and provides size and location accuracy limited only by the precision of the machine. The grid option usually provides a sub-option for displaying or hiding the grid points.

  • Constraints - Construction application programs usually provide constraint options for constructing graphics primitives such as lines, squares, and circles. A typical constraint applied to a line (e.g., by holding down the Option key while drawing it), restricts the angle it makes with the horizontal to modulo 45 o. Constraining a rectangle produces a square, and constraining an oval produces a circle. Such constraints greatly simplify the construction process.

  • Snaps - The snap option resembles an object-based grid constraint. That is, when the snap option (also known as "gravity field") is turned on, the end point of a line being drawn in the vicinity of an already existing line "snaps" to the end of the existing line to assure continuity. Typically, objects can be selected as the "snap-to" object so that a line drawn near a snap-to circle will be drawn in so as to be tangent to it. Snaps are particularly important in applications such as finite element analysis to assist in eliminating gaps, cracks, holes, and other embarrassing drafting artifacts.

  • Rubber banding - Lines are the basic graphical primitive of all construction programs, and any technique for simplifying and making line drawing more intuitive is highly desirable. One of the most useful techniques for constructing lines is the rubber band algorithm. Rubber banding involves sending a message via input device to select the anchor point (first point) of the line, moving the cursor to the terminal point of the line and sending another message to signal the end point. During the motion to the final point position, the rubber band algorithm draws, erases, and redraws the line repeatedly between the anchor point and the present cursor position to provide highly effective feedback to the designer on what the final result will look like.


  • Rubber Banding Algorithm

    A rubber banding algorithm is presented in the program RubberBand, and its output is shown in Figure 3.18.

    The minimalist philosophy used in building RubberBand was: write a labeled, rubber banding construction program in the minimum number of lines of code. All of the procedures used in RubberBand have been described previously.

    The first section, down to repeat, opens the Drawing window, labels it, lists the program options, and sets the pen mode to Xor to facilitate the line erase process. The repeat loop performs the repetitive line drawing operations and is exited only by double clicking on the left-hand side of the Drawing window. The repeat loop does nothing until the button is pressed. Then, the anchor point is read as (x1,y1) and the program enters a loop which continues as long as the button remains down. This is the key rubber banding loop which reads the current cursor position, (x2,y2), draws a line from (x1,y1) to (x2,y2), and immediately erases it by the second DrawLine command in Xor mode. Without the erase operation, the screen soon becomes cluttered with a "sun burst" of lines radiating from the anchor point. When the button is released, program control drops down to the final DrawLine command which draws a permanent line between the desired points. The exit procedure is an inelegant shortcut to terminate the program without introducing any more variables or defining new menu boxes.

    In spite of its simplicity, RubberBand offers nearly the same functionality as the Line mode of many commercial painting and drawing programs. Its primary weakness is that the Xor pen mode causes the intersection of two lines to revert to a white pixel.
     
     

    Pascal Program RubberBand 


    program RubberBand;

    {Program to demonstrate Rubber Band technique}
    {for constructing graphical objects }
    var
    x1, y1, x2, y2: integer;
    p:point;
    begin

    ShowDrawing; {Open Drawing Window}
    MoveTo(20, 20); {Label graph and options}
    TextSize(18);
    WriteDraw('Rubber Band Program');
    TextSize(10);
    MoveTo(30, 40);
    WriteDraw('* Button down to draw line');
    MoveTo(30, 50);
    WriteDraw('* Double-click left of window to QUIT');
    PenMode(patXor); {Set Pen Mode to Xor}
    {to erase and redraw line}
    repeat {Keep working until exit}
    if Button then {executes once/line}
    begin
    GetMouse(p); {Read first point on line}
    x1:=p.h; {horizontal element of point}
    y1:=p.v; [vertical element of point}
    while Button do {Loop until button released}
    begin
    GetMouse(p); {Read second point}
    x2:=p.h; {horizontal element}
    y2:=p.v; [vertical element}
    DrawLine(x1, y1, x2, y2); {Draw line}
    DrawLine(x1, y1, x2, y2); {Erase line}
    end; {Now redraw permanent line }

    DrawLine(x1, y1, x2, y2); end;

    until (x1 < 0) and Button {Exit by clicking left}

    end. {of Drawing Window}


     


    Figure 3.18
    Output of program, RubberBand. The program provides a simple but intuitive drawing tool. After about fifteen minutes of practice, the user can create sketches such as this in about five minutes. 



     

    Conclusions



    An important element in developing graphical applications programs is a supportive programming environment. Such environments provide an integrated editor- linker- compiler- debugger with helpful windows for observing variables in single step or trace modes. Object-oriented programming (OOP) is a particularly effective paradigm for designing, building and manipulating graphical objects. WIMP graphical user interfaces illustrate all the essential features and advantages of OOP. Interaction with objects on a graphics screen require the capabilities for pointing, position sensing, and action sensing. Procedures for performing these three functions may be combined to implement all six logical input device functions. These, in turn, form the basis for interactive construction techniques such as constraints and rubber banding on which painting, drawing, and CAD applications are built.