* `moveTo`: is triggered by the `Move` button via the `move` method (below). It displays prompts to select the window to be moved and the destination desk, unmaps the window, and assigns the number of the specified desk to this window in the registry.

* `openBrowser` and `openWorkspace` buttons open the Browser and the Workspace.

* `rename` requests a new name for the currently selected desk button and assigns it to the button as its label.

* `move` asks the user to select a window and click the destination desk button. The internal assignment of the desk is performed by `updateDesk`.

* Private support methods.

* `updateDesk`: is activated by a desk button. It performs the value of the `mode` variable and supplies the desk number as the argument. The result is either a change of desk (method `changeTo:`) or reassignment of a window to a new desk (method `moveTo:`), depending on the context.

## Implementation

We will now list and explain some of the methods.

### Opening

**openWithManyApps: classArray andNames: nameArray onDesk: anInteger**

"Open on desk anInteger, with classes as specified in classArray, and button names according to nameArray. Applications that are not on desk anInteger do not open yet."

`| desk deskBuilder |`

`desk := self new.`

"Create the desk manager but stop short of opening so that the manager window can be positioned, etc."

`deskBuilder := desk allButOpenInterface: #windowSpec.`

"Rename desk buttons via the builder and button widget Ids."

1 to 4 do: [:index | | buttonView | buttonView := (deskBuilder componentAt: ('desk', index printString) asSymbol) widget. buttonView label: (Label with: (nameArray at: index))].

"Position Desk Manager window in upper left."

`deskBuilder source finallyOpenIn: (10 @ 10 extent: deskBuilder window bounds extent) withType: #normal.`

"Assign user specified applications to desks."

1 to 4 do: [:index | | classes | ((classes := classArray at: index) isNil or: [classes empty]) ifFalse: [classes do: [:class | desk waitingApps at: (Smalltalk at: class) put: index]].

"Open user-specified desk."

`desk openDesk: anInteger.`

`^deskBuilder`

The opening of the desk is executed by

**openDesk: integer**

"Switch to the specified desk or edit button label or move window."

`self perform: mode with: integer`

A part of the execution of new is execution of the hook method `initialize` which is defined as follows:

**initialize**

"Initialize registry of windows and desks, start in 'move' mode with existing windows in desk 1."

`waitingApps := Array with: Set new with: Set new with: Set new with: Set new`

`windowRegistry := IdentityDictionary new.`

`ScheduledControllers scheduledControllers do: [:controller | windowRegistry at: controller view put: 1].`
self changeSystemController.

mode := #changeTo:

The response to a desk button clicks is handled by methods desk1, desk2, desk3, and desk4 defined as follows:

**desk1**

"Action method of button desk 1."

self updateDesk: 1

The operation of updateDesk: depends on the current mode and its definition is as follows:

**updateDesk: integer**

"Depending on the context, switch to integer desk or edit button label or move window."

self perform: mode with: integer

Depending on the value of mode, this method performs one of changeTo: or moveto:. Method changeTo: switches from one desk to another and its definition is

**changeTo: newDesk**

"Switch to the specified desk if different from the current one."

newDesk = currentDesk ifTrue: [^nil].

"Change button colors."

currentDesk isNil ifFalse: [self colorButton: currentDesk color: ColorValue white]. self colorButton: newDesk color: ColorValue yellow.

"Update the registry - windows may have been added or removed."

self refreshWindowRegistry.

currentDesk := newDesk.

"Go through registry, mapping windows on the current desk and unmapping the rest. Always keep the Desk Manager - the window corresponding to self."

windowRegistry keysAndValuesDo: [:key :value | (key = self builder window or: [value = newDesk])
    ifTrue: [key = self builder window ifFalse: [key map]]
    ifFalse: [key unmap]].

"Open unopened applications waiting on this desk, if any."

(waitingApps at: currentDesk) do: [:app | self open: app]

An important part of the definition is method refreshWindowRegistry which is defined as follows:

**refreshWindowRegistry**

"We are switching to a different desk. Make sure to update the desk being abandoned - windows may have been added or deleted."

| newRegistry |

"Create a new empty registry."

newRegistry := IdentityDictionary new.

"Copy active windows in the registry and new windows to the new registry."

ScheduledControllers scheduledControllers do:

[:controller |

| window |

(windowRegistry includesKey: (window := controller view))
    ifTrue: [newRegistry at: window put: (windowRegistry at: window)]
    ifFalse: [newRegistry at: window put: currentDesk]].

"Replace old registry with new one."

windowRegistry := newRegistry

The open: method on the last line of changeTo: opens a waiting application, removes it from its array and adds the resulting window to the registry:

**open: anApplication**

"Open anApplication, remove it from waitingApps, and add its window to the registry."
| window |
/waitingApps at: currentDesk) remove: anApplication.
window := anApplication open.
windowRegistry at: window put: currentDesk

Note that we assume that all unopened applications open with the open message. This restriction
could, of course, be easily removed. The moveTo: method moves a selected window to a selected desk. Its
definition is

`moveTo: newDesk`

"Move previously selected window to the specified desk (registry) and hide it."
newDesk = currentDesk ifTrue: [^nil].
windowRegistry at: currentWindow put: newDesk.
currentWindow unmap.
mode := #changeTo:

Note that the method ends by switching to the default mode of operation.

Who sends the `moveTo:` message? Its trigger is the activation of the *Move* button whose action method `move` first lets the user select a window by waiting until `ScheduledControllers` reports that the user pressed the red button, and then identifies the active window. After this, the user is asked to click a desk button (to identify the destination desk). The mode changes to `#moveTo:`, and when the user clicks one of the desk buttons, this sends `updateDesk:` (as listed above), which then performs `moveTo:`. The action method `move` is defined as follows:

`move`

"User clicked Move to move a window to a different desk. Identify the window and ask user to select desk. The rest of the operation is handled in `moveTo:`."
Dialog warn: 'Select window to be moved'.
"Wait for user to press leftmost mouse button."
[ScheduledControllers activeController sensor redButtonPressed] whileFalse: [].

"Get active window, assuming that this is the one the user wants."
currentWindow := Window currentWindow.
"Don't let the user try to move the Desk Manager window to a different desk."
currentWindow = self builder window ifTrue: [^Dialog warn: 'You cannot move Desk Manager window'].
Dialog warn: 'Click destination desk'.
mode := #moveTo:

Finally, opening the browser and the workspace is done as follows:

`openBrowser`

"Open Browser on current desk."
(Smalltalk at: #Browser) open

and

`openWorkspace`

"Open Workspace on current desk."
(Smalltalk at: #ComposedTextView) open

Exercises

1. Design and write a method to open SmallDesk with applications whose opening message is not open.

2. An alternative to unmapping windows is to collapse them to icons. Change SmallDesk to use collapsing.

3. SmallDesk mechanism for moving a window to another desk is awkward: The user must click the *Move* button, complete the dialog, click a window, complete another dialog, and finally click the destination desk. To simplify the process, add a new *move to another desk* command to the `<window>` menu
when *SmallDesk* is running. When the user selects this command, *SmallDesk* asks him or her to click a desk button, and when the user does so, *SmallDesk* moves the window to that desk.

## 12.3 Principles of displaying - graphics contexts, geometric objects, and other concepts

In this section, we will explain the principles of displaying components inside a window and illustrate them on simple examples.

### Graphics context

When we want to display a geometric object such as a rectangle, we must specify which part of the display surface we want to paint on, and which color and line width to use. For text, we must also know the font. VisualWorks keeps this information in a GraphicsContext object attached to the display surface. The GraphicsContext knows about its graphics medium and is also responsible for all display operations. We will see numerous examples of the use of GraphicsContext but first, we will examine its definition. The place of GraphicsContext in the class hierarchy is as follows:

### Object

GraphicsContext
  HostPrinterGraphicsContext
  PostScriptGraphicsContext
  ScreenGraphicsContext

and the essence of its comment says

I display graphical objects on a display medium and maintain graphics state parameters specifying how graphical objects should be rendered.

### Instance variables:

<table><tr><td>medium</td><td>&lt;GraphicsMedium&gt;</td><td>where to render graphics</td></tr><tr><td>clipOriginX</td><td>&lt;nil | SmallInteger&gt;</td><td>in untranslated device coordinates</td></tr><tr><td>clipOriginY</td><td>&lt;nil | SmallInteger&gt;</td><td>In untranslated device coordinates</td></tr><tr><td>clipWidth</td><td>&lt;nil | SmallInteger&gt;</td><td></td></tr><tr><td>clipHeight</td><td>&lt;nil | SmallInteger&gt;</td><td></td></tr><tr><td>lineWidth</td><td>&lt;SmallInteger&gt;</td><td>draw lines with this width in pixels</td></tr><tr><td>capStyle</td><td>&lt;SmallInteger&gt;</td><td>style to render line caps</td></tr><tr><td>offsetX</td><td>&lt;SmallInteger&gt;</td><td>translation</td></tr><tr><td>offsetY</td><td>&lt;SmallInteger&gt;</td><td>translation</td></tr><tr><td>phaseX</td><td>&lt;SmallInteger&gt;</td><td>tile phase for tiling the background</td></tr><tr><td>phaseY</td><td>&lt;SmallInteger&gt;</td><td>tile phase</td></tr><tr><td>scaleX</td><td>&lt;Float&gt;</td><td>scaling factor</td></tr><tr><td>scaleY</td><td>&lt;Float&gt;</td><td>scaling factor</td></tr><tr><td>font</td><td>&lt;ImplementationFont&gt;</td><td>font for drawing strings</td></tr><tr><td>paint</td><td>&lt;Paint&gt;</td><td>color or tile for displaying graphical entities</td></tr><tr><td>paintPolicy</td><td>&lt;PaintPolicy&gt;</td><td>how to render paints that do not exactly correspond to host paints</td></tr><tr><td>fontPolicy</td><td>&lt;FontPolicy&gt;</td><td>how to map FontDescriptions to the fonts available on the graphics device</td></tr></table>

A more detailed description of some of the less obvious instance variables is as follows:

clipOriginX, clipOriginY, clipWidth, clipHeight describe the clipping rectangle - the part of the display surface on which the GraphicsContext will draw. This rectangle acts as a stencil and all drawing that falls outside it is ignored.

capStyle specifies the style used for straight line endings; it is visible only on thick lines.

offsetX, offsetY: translates coordinates of graphics context with respect to the origin of its medium

scaleX, translateY: scaling factor
paint: Default paint used when the visual part being drawn does not specify its paint. The concept of a paint requires a brief comment. Display surfaces can be painted either with colors or with patterns. The distinction is similar to the distinction between painting a wall and wallpapering it: A color paints the whole object the same color (filling a rectangle with red color, for example) whereas pattern repeats a tiling pattern across the whole area.

phaseX, phaseY: Tile phase specifies the starting point of the first tile of a painting pattern.

paintPolicy, fontPolicy: If the platform cannot provide the required color or font, this object finds a suitable available replacement.

Most display methods are defined in this class, and subclasses implement differences between screen display and printing such as conversion between pixels (screen distance units) and printer units (corresponding but different printer distance units).

We will now present two examples showing how to use the GraphicsContext to display text and geometric objects in a window, how to control display via the graphics context, and how to use patterns. You will note that our code does not provide automatic damage repair which means that if you change window size, collapse and expand it, or partially cover the window and then uncover it, the drawing will be lost. As we noted earlier, automatic damage repair is a separate mechanism that will be covered later.

### Example 1: Drawing geometric objects and controlling their color

**Problem:** Implement an application with the user interface in Figure 12.2. Clicking *Color* opens a multiple choice dialog listing all available colors and accepts the selected color for drawing. Clicking *Draw* opens a dialog requesting information needed to display the object selected by the radio button, for example the end points of a straight line when *Line* is selected. A predefined clipping rectangle inside the window is used to restrict display to the upper part of the window.

<figure><img src="image_228.png" /><figcaption>Figure 12.2. Example 1: Clipped drawing of a line with end points in the upper left and lower right corners of the application window.</figcaption></figure>

**Solution:** The solution revolves around the graphics context which controls the parameters and does the drawing. The graphics context is obtained from the window which, in turn, is known to the builder of the application. From within the application model, the graphics context of the window is thus accessed by

### builder window graphicsContext

Although this expression suggests that a window keeps its graphics context, this is not so. Instead, a window manufactures a graphics context with default parameters whenever it is asked for one. Changes made to a graphics context are thus transient, and when a graphics context is modified and then retrieved
another time, it will again be a graphics context with default parameters. This is an important but often
forgotten principle.

To implement the program, we painted the user interface installed it on class DrawObjects, and
defined instance variables color, clippingRectangle, object, and width and initialized them as follows:

initialize

"Set initial parameters for use on graphics context."

<table><tr><td>clippingRectangle := 20 @ 20 corner: 180 @ 100.</td><td>"Upper part of the window."</td></tr><tr><td>color := ColorValue black.</td><td>"Default color."</td></tr><tr><td>object := #Line asValue.</td><td>"Aspect variable of the radio button group."</td></tr><tr><td>width := 1</td><td></td></tr></table>

The color action method gets a color Symbol from a dialog and executes it to get a ColorValue:

color

"Get color from user and save it."

<table><tr><td>| colorName |</td><td>colorName := Dialog choose: 'Choose color' fromList: ColorValue constantNames values: ColorValue constantNames lines: 8 cancel: [nil].</td><td>"Names of built-in named colors."</td></tr><tr><td>colorName isNil ifFalse: [color := ColorValue perform: colorName]</td><td></td><td></td></tr></table>

The action method of the *Draw* button obtains the window's graphics context, changes its paint, line width, and clipping rectangle according to instance variables, and sends a message to draw the selected object:

draw

"Get graphics context, set its parameters, and execute appropriate drawing message."

<table><tr><td>| gc |</td><td>gc := self builder window graphicsContext.</td></tr><tr><td>gc paint: color.</td><td></td></tr><tr><td>gc linewidth: width; clippingRectangle: clippingRectangle.</td><td></td></tr><tr><td>self perform: 'draw', object value, 'On:' with: gc</td><td></td></tr></table>

The last statement is possible because we assigned radio buttons aspects to match the required display message, such as #Circle. As a consequence, when the *Circle* button is selected, the last line is equivalent to self drawCircleOn: gc. Each of the drawing methods then obtains the appropriate parameters from the user and asks the graphics context to draw the object. As an example, the method to draw a square is defined as follows:

drawSquareOn: aGraphicsContext

"Get origin coordinates and side length from user, draw a square."

<table><tr><td>| startx starty side |</td><td>'Obtain origin and side of square from user.'</td></tr><tr><td>startx := (Dialog request: 'Upper left x' initialAnswer: '30') asNumber.</td><td></td></tr><tr><td>starty := (Dialog request: 'Upper left y' initialAnswer: '30') asNumber.</td><td></td></tr><tr><td>side := (Dialog request: 'Side' initialAnswer: '20') asNumber.</td><td></td></tr><tr><td>"Draw unfilled square inside the clipping rectangle using current parameter settings."</td><td></td></tr><tr><td>^aGraphicsContext displayRectangularBorder: (startx @ starty extent: side @ side)</td><td></td></tr></table>

It is worth noting that instead of asking a graphics context to draw a visual object, we can also ask
the object to draw itself using the graphics context. As an example, the effects of

aGraphicsContext displayRectangularBorder: aRectangle

and
### aRectangle displayOn: aGraphicsContext

are exactly the same. The advantage of the second method is that it can be used even if we don't know the kind of visual part being drawn, as when we have a whole collection of objects to draw:

objects do: [:object| displayOn: aGraphicsContext]

We will use this approach in another example.

### Example 2: Drawing with patterns and drawing with color

**Problem:** Design an application with the user interface Figure 12.3. When the user selects a color via the *Color* button and clicks *Draw*, the program requests square parameters from the user and draws a square filled with the selected color. When the user clicks *Pattern*, the program displays the cross hair cursor and the user selects a rectangle on the screen. If the user then clicks *Draw*, the program requests square parameters and tiles the square with copies of the selected area.

<figure><img src="image_229.png" /><figcaption>Figure 12.3. Example 2: Displaying a square filled with a pattern copied from the screen.</figcaption></figure>

**Solution:** In class *Patterns*, we define instance variable *paint*, and initialize it to black color:

**initialize**

```
paint := ColorValue black
```

The *color method* is as in Example 1 and the *action method* of the *Pattern* button is as follows:

**pattern**

```
"Ask user to select screen area with desired pattern."
Dialog warn: 'Select a rectangle on the screen'.
paint := Screen default contentsFromUser asPattern
```

The *action method* of the *Draw* button obtains a rectangle from the user, assigns the paint to the graphics context, and asks the graphics context to draw the square with the assigned fill:

**draw**

```
"Obtain square parameters from user and draw the square filled with the current paint."
| startx starty side gc |
"Get parameters."
gc := self builder window graphicsContext.
```
gc paint: paint.

startx := (Dialog request: 'Upper left x' initialAnswer: '30') asNumber.

starty := (Dialog request: 'Upper left y' initialAnswer: '30') asNumber.

side := (Dialog request: 'Side' initialAnswer: '20') asNumber.

"Disply."

^gc displayRectangle: (startx @ starty extent: side @ side)

## Main lessons learned:

* All display within a window is performed by an instance of GraphicsContext.

* GraphicsContext is central to everything related to display of visual components.

* In addition to being the drawing engine, a GraphicsContext also holds display parameters such as line width, paint, and fault.

* Display parameters stored in the graphics context can be controlled programmatically.
## Exercises

1. Browse and explain the operation of GraphicsContext.

2. Browse and explain class Paint.

3. Modify Example 1 to obtain the clipping rectangle from the user rather than using a fixed preprogrammed value. (Hint: Look for implementers of method fromUser:.)

4. Modify Example 2 to ask the user to specify a rectangle interactively instead of obtaining square parameters via a dialog. (Hint: Look for implementers of method fromUser:.)

5. We mentioned that asking an object to draw itself on a graphics context is equivalent to asking the graphics context to draw the object. This suggests that one of the two approaches is implemented by double dispatching. Check whether this is indeed the case.

## 12.4 Images, pixmaps, masks, and paints

In addition to displaying geometric objects and text, we can also display images and use masks to control which part of the image is visible. In this section, we will explain the principles of these concepts and illustrate them on simple examples. We will also talk about cursors because they are related but we will leave an example of their use for later.

### Image

An Image (more accurately an instance of one of its concrete subclasses) is a rectangular array of pixels, each represented internally by a group of bits and displayed as a colored dot. Since different images may use a different number of bits to represent pixel color, a single class cannot represent all possible kinds of images. Image is thus an abstract class which leaves concrete image representation to its subclasses:

#### Image

* Depth1Image
* Depth2Image
* Depth4Image
* Depth8Image
* Depth16Image
* Depth24Image
* Depth32Image

The depth of an image is the number of bits used to represent each pixel. An image with depth 1 (monochrome image) uses one bit per pixel and allows only two colors (represented as 0 and 1 respectively), depth of 2 allows $2^2 = 4$ different colors encoded as 00, 01, 10, and 11, depth of 8 means that each pixel may have any one of $2^8 = 256$ colors, and so on. The greater the depth, the more subtle an image (if your computer can display them) and the more memory required to store the image. In ordinary situations, you don't need to be concerned about depth, and address all your messages to class Image.

The most common ways of creating an image are capturing an image on the screen or reading it from a file. We will see that cursors, which use an Image as a part of their definition, often create an image by specifying the individual pixels.

### Example 1: Capturing an image and painting it in a window

*Problem:* Write a program to let the user capture an image from the screen and paint it in the currently active window. The upper left corner of the image should be located in the center of the window. Figure 12.4 shows what the outcome might look like.
<figure><img src="image_230.png" /><figcaption>Figure 12.4. Example 2: Displaying image from user in the active window.</figcaption></figure>

**Solution:** To get an image from the user send message fromUser to Image. To find the currently active window, ask Window. To find its center, ask for its bounds rectangle and its center. Finally, get its graphics context and display the image. The graphics context displayOn:at: message displays the image at the specified point. The whole solution is as follows:

```
| view |
"Get current window, obtain a rectangular screen area from the user, and display it."
view := Window currentWindow.
Image fromUser displayOn: (view graphicsContext) at: (view bounds center)
```

The following example is from the library,

```
ScheduledWindow new
    component: ((BorderDecorator on: Image fromUser)
                useHorizontalScrollBar);
open
```

It opens a default-size window showing an image captured from the screen by the user. Note how we specify the image as the window's component. We can improve on this code a bit by making the window the same size as the captured window as follows:

```
| image |
ScheduledWindow new
    component: ((BorderDecorator on: (image := Image fromUser))
                useHorizontalScrollBar);
openIn: ((10@10) corner: image bounds extent)
```

### Example 2: Image from file

**Problem:** Write a code fragment to read a graphics file and display it in a scheduled window.

**Solution:** Abstract class ImageReader can read a limited number of image file formats, delegating its conversion to an Image to a subclass corresponding to the encoding. Sending message fromFile: to ImageReader returns an object containing the image; the image can then be extracted with message image and displayed in the usual way. As an example,

```
(ImageReader fromFile: 'C:\MSOffice\Clipart\flower.bmp' ) image
displayOn: Window currentWindow graphicsContext
```
pattern of 0's and 1's, you can see the crosshair shape easily. The 1's in the *mask* define the pixels that are opaque (*not* transparent), in this case the crosshair and a one-pixel band around it. 0's are transparent and allow you to see the background behind the cursor. You can use this same method to define your own cursors and Appendix 4 shows how you can create a cursor with the Image Editor tool.

Class **Cursor** is typically accessed to display a special cursor during a special operation. As an example, **VisualWorks** displays a special cursor while reading a file, while performing garbage collection, or while waiting for the user to select a point on the screen (for this, it uses the **crossHair** cursor above). The two common messages for changing the cursor are **showWhile:** *aBlock* and **show**. The **showWhile:** message changes the cursor for the duration of execution of its block parameter, and changes it back to its original shape when *aBlock* executes. A typical example of its use is in the following method from class **FileBrowser**:

**hardcopyStream: aStream**

"Use the 'wait' cursor while printing aStream. "

Cursor *wait*

showWhile: [aStream contents asText asParagraph hardcopy]

Method **show** also changes the cursor but the programmer must remember to change it back. Its typical use is shown in the definition of **showWhile:** itself:

**showWhile: aBlock**

"While evaluating aBlock, make the receiver be the cursor shape. Then revert to the original cursor."

| oldcursor |
oldcursor := self class currentCursor.
self show.
^aBlock valueNowOrOnUnwindDo:
[oldcursor show]

**Pixmaps – drawing surfaces behind the scenes**

A **Pixmap** is a display surface and you can 'display' on it, just as on the screen. However, displaying on **** does not show on the screen until you ask the screen's graphics context to display the **pixmap**. **Pixmaps** are thus used to construct displayable rectangles before they are displayed on the screen. The typical procedure is to transfer an **Image** to a **Pixmap**, modify it by a mask to display only some selected part of it, and ask a graphics context to display the result. We will show an example of this shortly. **Pixmaps** can also be created by copying the contents of the clipboard of your machine using the **clipBoard**

**Extracting a part of an image - masks**

To display a part of an image, transfer the original image to a **Pixmap**, combine it with a **Mask**, and display the result. If the desired mask is rectangular, a mask is not necessary because the **Pixmap** can do rectangular clipping itself. We will now describe and illustrate both techniques.

**Displaying a rectangular part of an image without using a mask**

1. Create the image as a **Pixmap** object or convert an **Image** to a **Pixmap** using **asRetainedMedium**.

2. Construct the clipping rectangle.

3. Apply the clipping rectangle to the **Pixmap** and extract the corresponding area as **Image**. Use message **completeContentsOfArea:** **clippingRectangle**.

4. Display the obtained **Image** on a graphics context.

**Example 3: Extracting a rectangular portion of an image without using a mask**

*Problem:* Display the image from the previous example and its lower half side by side (Figure 12.5).
<figure><img src="image_231.png" /><figcaption>Figure 12.5. Example 3: Image from the Microsoft Office clip art library and its rectangular part.</figcaption></figure>

**Solution:** Following the procedure described above and using a .bmp file, the solution is as follows:

```
gc clippingRectangle clippedImage original pixmap |
"Get image from file."
original := (ImageReader fromFile: 'C:\MSOffice\Clipart\flower.bmp' ) image.
"Display original image on window's graphics context."
gc := Window currentWindow graphicsContext
original displayOn: gc.
"In memory, construct a Pixmap containing the original image."
pixmap := original asRetainedMedium.
"Construct clipping rectangle."
clippingRectangle := (0 @ (original height / 2)) corner: (original width @ original height).
"Extract area corresponding to rectangle from pixmap."
clippedImage := p = pixmap completeContentsOfArea: clippingRectangle.
"Display the clipped image on the graphics context next to the original."
clippedImage displayOn: gc
    at: (original width @ 0)
```

An alternative approach is to use a Mask. This method is more powerful because it allows the clipping area to have any shape. During the discussion of Cursor, we saw that a mask acts as a decal – a display area painted in such a way that it is transparent in some places but opaque in others. A mask is thus like a surface covered with a 'paint' that is either transparent or opaque and this is why the paint is called coverage value.

### Extracting a non-rectangular part of an image with a mask

1. Create the image as a Pixmap object or convert an Image to a Pixmap using asRetainedMedium.

2. Construct the clipping mask. The shape drawn on the mask will be the transparent area.

3. Display the Pixmap overlaid with the mask on the graphics context using copyArea:from:sourceOffset: destinationOffset:. You can control the position of the mask with respect to the p by the argument of sourceOffset:, and the position of the p Normap with respect to the display area by the argument of destinationOffset:.
### Example 4: Extracting a triangular portion of an image with a mask

**Problem:** Display an image from user and its above-the-diagonal part in the current window (Figure 12.6). Shift the clipped part so that its origin coincides with the corner of the original image.

<figure><img src="image_232.png" /><figcaption>Figure 12.6. Example 4: Bitmap image clipped with a mask.</figcaption></figure>

**Solution:** Following the steps listed above we create a Pixmap, a Mask containing the masking triangle, and display the combined image and mask on the graphics context of a window:

```
| pixmap image window mask maskTriangle gc imageRectangle destOffset sourceOffset |
"First get the image and display it in the current window."
image := Image fromUser.
window := Window currentWindow.
gc := window graphicsContext.
gc display: image at: 10@10.
"Now start constructing the masked image. First transfer image to pixmap."
 := image asRetainedMedium.
"Find the bounding box of the image and use it to construct mask rectangle."
imageRectangle := image bounds.
mask := Mask extent: imageRectangle extent.
maskTriangle := Array
  with: 0 @ 0
  with: (imageRectangle width) @ 0
  with: imageRectangle width @ imageRectangle height.
"Display the triangle on the mask."
mask graphicsContext displayPolygon: maskTriangle.
"Display masked image on window's graphics context at an offset."
sourceOffset := 0@0. "No offset from pixmap origin to mask origin."
destOffset := imageRectangle extent + (10@10). "Window origin to pixmap origin offset."
gc copyArea: mask
  from: pixmap graphicsContext
  "Origin of mask when placed over the image (in image coordinates)."
  sourceOffset: sourceOffset
  "Origin of masked image on the display surface (in window coordinates)."
  destinationOffset: destOffset
```
## Main lessons learned:

* An Image is a pixel-by-pixel representation of an image displayed on screen or printed by printer.

* An Image may use one of several depths, resulting in greater or smaller color variety and a corresponding increase or decrease of memory requirements.

* An Image can be read from a file, copied from the screen, or constructed from pixels.

* Class Cursor defines the visual representation and behaviors of the mouse cursor.

* Cursor shape is defined by a 16@16 image and a 16@16 mask that determines which part of the cursor is transparent.

* An Image can be clipped to any shape with a mask.

* A Pixmap is a display surface stored in memory but not necessarily displayed on the screen.

## Exercises

1. You may have noticed that our image from use in Example 1 does not show the whole rectangle selected by the user. Why is it? Can you correct this shortcoming?

2. In Example 4, we set the offset of the origin of the mask at the origin of the pixmap. What happens when you use a non-zero offset?

3. Write a simple clip art browser that finds all .bmp files on a user-specified disk drive, lists their names in a multiple choice dialog, and displays user's selection in a new ScheduledWindow.

4. It would be nice if CoverageValue could handle any values between 0 and 1, making pixels less and less transparent as the value moves from 1 to 0. A reasonable approximation of this behavior is to make some of the pixels transparent and leave the remaining pixels opaque. As an example, if the coverage value of all pixels in a 10 by 10 rectangle is 0.6, we could make 60 percent of all pixels opaque and 40 percent transparent using random assignment of 0's and 1's in proportions corresponding to the specified coverage value. Implement this idea.

5. Explore class Pixmap, in particular its comment and displaying protocol. Write a summary.

6. Explore class Mask and write a summary.

7. Explore class Image, in particular its bit accessing and image processing protocols. Write a summary.

8. Write a method that creates a smooth visual transformation of one image to another. Allow several styles of transitions such as left to right, top to bottom, and so on.

## 12.5 Models, views, and controllers revisited

Although the graphics context, display surfaces, images, and the other concepts presented above provide a lot of power, they don't address three essential aspects of user interfaces: interactive user control, window damage repair, and automatic dependency on domain data. We will now explain what we mean by these concepts and how VisualWorks deals with them.

* By user control, we refer to the ability of the user to interact with the application using the mouse and the keyboard. Clearly, the programs that we wrote in the previous sections did not allow any user interaction with the painted objects and images. We will see shortly that the concept of a controller provides a mechanism for user control.

* Automatic damage repair is another GUI feature that we take for granted: If a window is 'damaged', for example collapsed to an icon and then expanded, or covered by another window and then uncovered, or simply resized, we expect that the damage will be automatically repaired. In other words, we expect a certain permanence of the GUI. You may have noticed that the drawing and painting in our example programs did not have this permanence. If you painted something on the sample window and then collapsed and restored it, the painting was gone. We will see that the basic mechanism for automatic repair can be obtained by subclassing views to the View class.

* Finally, we expect that if the value of the domain model changes in a way that should be reflected in the view, the user interface will automatically adjust to this change. As an example, if the window displays a
circle and the application changes its diameter, we expect that the circle will be automatically redrawn. The previous sections do not give any hints as to how this could happen but we will see that the principle of *dependency* implements this mechanism.

In the rest of this section, we will explain the principles of views, controllers, and their dependency on models. The remaining sections will then explain the basic details and demonstrate how they can be used to build new UI components by using the 'any component' widget of the UI painter called the *view holder* or the *subview*.

As we already know from Chapter 6, the principle of user interface components in Smalltalk is the *model-view-controller (MVC) paradigm*. According to this principle, every UI component that represents a value of a domain object uses this value as its *model*, the graphical representation of the model is the responsibility of a *view*, and the object that manages user interaction within the boundaries of a view is the *view's controller*. We will now outline how the MVC paradigm implements the three UI operations listed above.

## User interaction

User interaction via mouse and keyboard is constantly monitored by the operating system. When an event occurs, the operating system sends information about it to the running application, in this case VisualWorks. After some opening message sends, an instance of a concrete subclass of *Event* sends a message corresponding to the event that occurred to the active controller, usually the controller responsible for the screen area under the cursor. The message contains information about the kind of event that has occurred and its parameters. If the controller is interested in the event, it must have its definition of the method, otherwise a 'do nothing' hook definition inherited from class *Controller* is executed.

As an example, the controller of a chess board view will need its own definition of `redButton Pressed Event:` that will respond when the player presses the red button over a chess board square.

## Damage repair

When a window or its part is damaged, it records the smallest rectangle containing the damage. When processing priority allows it, the window redraws the damaged rectangle or the union of all damage rectangles. To do this, the window constrains its *GraphicsContext* to the clipping rectangle corresponding to the damaged area and sends a message to its components, asking them to redraw themselves.

Unless the window consists of only one widget, it contains a 'composite part' containing other parts, as in Figure 12.7. Damage repair sends message `displayOn: graphicsContext` down the tree to all parts of the window and eventually reaches all views. Each view must have a definition of `displayOn:` (possibly inherited) and this definition redraws the view within the limits of the clipping area of the graphics context.

<figure><img src="image_233.png" /><figcaption>Figure 12.7. Simplified structure of a multi-component window.</figcaption></figure>

## Redrawing caused by a change in the model
In an active application, the value of a model frequently changes. As an example, a stopwatch application using integer seconds as its model changes the value of the model every second. There are two ways in which this change may be translated into a view change:

The preferred approach is to use *dependency*, already introduced earlier. To use dependency, the programmer includes a *changed* message in each method that changes the model in a way that affects the view. In the chess example (Figure 12.8), the model method that changes the configuration of pieces on the board when a player makes a move calculates new piece positions and sends *changed* to self<fnref n="1" />. In response to this, the model automatically sends the *update* message to each of its dependents - in this case the chess board view. The chess board view's customized *update* method then asks the model for the necessary data and redraws itself appropriately. To redraw itself, the view usually sends *invalidate* to self and the *invalidate* message travels up the component tree to the window at the top, and the window then sends *display* down the component tree in the same way as after a request for damage repair.

<figure><img src="image_234.png" /><figcaption>Figure 12.8. Communication involved in moving a chess piece from one square to another. Redrawing based on *dependency* and *changed* message is assumed.</figcaption></figure>

The other way in which a model change may be translated into view redrawing is to send the *invalidate* message directly from the model's method when the change occurs. The result is the same as when we use *dependency* but the implementation is less centralized.

It is useful to note that invalidation provides two refinements. One is that we can specify whether the redrawing of the view must occur immediately or not. (This gives rise to the concept of 'lazy repair'.) The message may also specify that only a part of the view rather than the whole view should be redrawn and this can greatly improve the behavior of the user interface. As an example, when the position of a chess piece changes, only the start and the destination squares need to be redrawn. (The exceptions are capturing 'en passant' which affects three squares, and castling where four squares must be redrawn.) Invalidating only two squares instead of redrawing the whole board means that only 2/64 of the view must be redrawn and although this does not speed up the process, it eliminates unpleasant flashing that would otherwise occur. We use this technique in our implementation of the chess board in Appendix 3.

<footnote n="1">¹ The best way to do this is to change the aspect only via an accessing method that includes the *changed* message.</footnote>
After this general introduction, we are now ready for details and examples. In the next two sections, we will explain how to create views using the view holder widget and how to create controllers. A larger example showing the implementation of the chessboard is given in Appendix 3.

## Main lessons learned:

* The principle of Smalltalk's user interfaces is the separation of domain objects, display objects, and user interaction objects into a model, a view, and a controller. This principle is called the MVC paradigm.

* The model of a visual component holds the displayed domain object and treats the view as its dependent. When the model changes in a way that should affect the view, it sends itself a changed message which triggers notification of the view and any other dependents via update messages. The view is responsible for having an appropriate definition of update to redisplay itself.

* The view is responsible for drawing the model's visual representation on the screen. It should be a subclass of View to inherit basic damage repair mechanism and connections to its model and controller.

* The controller is responsible for handling mouse and keyboard events within the view's limits.

## 12.6 Creating UI components with the view holder widget

Older versions of Smalltalk did not have user interface painters and the only way to create a graphical user interfaces was programmatic. With the current technology, the UI Painter almost eliminates the need to program views and controllers because standard widgets are available from the Palette and non-standard UI components can be created rather easily with the *view holder (subview)* widget. (We will use the term view holder for the widget, and subview for the visual component displayed in the view holder.)

The process of creating a new component using the view holder widget is simple. It consists of painting the view holder on the canvas, defining its properties, creating the view that it will display, and defining its controller. We will now demonstrate the procedure on two simple applications that do not allow user interaction and therefore do not require a custom controller. In the next section, we will then give an example that requires a custom controller and show how to design it.

### Example 1: Image display.

**Problem:** Design and implement an application with the user interface in Figure 12.9. Before the window opens, the program asks the user to select two images on the screen. When the window opens, the view shows help text and when the user clicks one of the image buttons, the view displays the corresponding image.

<figure><img src="image_235.png" /></figure>
Figure 12.9. Example 1: Desired user interface when the application first opens (left), and when the user clicks an image button (right).

**Solution:** We will solve this problem by using dependency. The application model will be the model of the subview and when the user clicks an action button, the application will execute the *changed* message. This will trigger the *update* message in the subview which will then request the image from its model (the application) and invalidate itself. This will cause the subview to redraw itself.

The solution requires an application model (class *Twolmages*) and a view class (class *ImageView*) for the subview displayed by the view holder. Before discussing the details of implementation, we will first describe how to create the view holder:

### Creating a view holder

1. Create the canvas and paint the view holder (Figure 12.10).

2. Define view holder properties, at a minimum the *View* property which is the name of the method that returns the subview to be displayed in the view holder. We called this method *imageView*.

3. Install the canvas.

<figure><img src="image_236.png" /><figcaption>Figure 12.10. The view holder action button on the UI Palette.</figcaption></figure>

### Design

*Class Twolmages* is the application model class. It will have instance variables to hold the subview (*imageView*), the currently displayed image (*image*), and the two images selected by the user on the screen (*image1* and *image2*). During initialization, *Twolmages* will ask the user to select two rectangles on the screen and save the corresponding images in *image1* and *image2*. When the user clicks an action button, the corresponding action method assigns the appropriate image to *image* and sends the *changed* message.

Initialization must also create an instance of a view (class *imageView*), assign it to *imageView*, and assign itself as its model. (*ApplicationModel* inherits dependency from its superclass *Model*.) The definition of *initialize* is thus as follows:

#### initialize

```
"Get two images from user and define subview."
```

Dialog warn: 'Select the first image.'
image1 := Image fromUser.
Dialog warn: 'Select the second image.'.
image2 := Image fromUser.
imageView := ImageView new.
imageView model: self "Create dependency of the subview class on the model."

The action method for *Image 1* assigns image1 as the current image and triggers the change mechanism:

**image1**

"Assign image1 as current image and send change notification to dependent (subview)."
image := image1.
self changed

and image2 is similar. The remaining methods in Twolmages are only accessing messages for image and
imageView.

*Class ImageView*

Class ImageView implements the subview. As in all situations involving the view holder widget, the superclass of ImageView will be View because it implements the damage repair mechanism.

When a view is being built by the builder, a new instance is created and in the process, message
defaultControllerClass is sent. Our view does not allow any user interaction and we will thus return the
NoController class - a controller that ignores all input events:

**defaultControllerClass**

^NoController

Note that a controller is required even if it is not active.

When a change occurs in the model (Twolmages), the model sends itself the changed message which then sends an update message to its view holder dependent. In response to this, the view holder must redraw itself, usually by sending itself the invalidate message which travels up the component tree of the window and causes the window to send a display message with its graphics context clipped to the damage area down the tree again. The definition of update is thus simply

**update**

"Trigger a redisplay."
self invalidate

but since this behavior is inherited, we don't need this method at all.

The only remaining method (and the method that does all the work) is displayOn: which redisplays
the contents of the view holder whenever it is required (during initial display, during each model change,
and during invalidation). To display itself, our subview must ask its model for the current image and if the
image is not nil, ask the graphics context to display it. Since we have not initialized image, its value is
initially nil. In this case, we will display a help text in the center of the subview. To get the proper location
for the text, we will ask the subview for its bounding rectangle, ask the graphics context to measure the
width of the text, and display the text at an appropriate place. The whole definition is as follows:

**displayOn: aGraphicsContext**

"Display yourself on the graphics context supplied by the window."

| string width center |
model image isNil

ifTrue: "Display help text."
[center := self bounds center.
string := 'Click action button'.
width := aGraphicsContext widthOfString: string.
aGraphicsContext displayString at: (center x (width/2)) @ 30).
string := 'to display an image.'
``` FLASH
width := aGraphicsContext widthOfString: string.
aGraphicsContext displayString at: (center x (width/2)) @ 50)]
ifFalse: "Display currently selected image."
[aGraphicsContext displayImage: image at: 0@0]
```

The program is now fully functional. Note that unlike our previous programs it automatically repairs window damage due to collapsing, reshaping, and other window events.

## Improvements

We know that it is better to centralize shared behavior and in our case both image methods share the changed message which is always executed when image changes. In such a case, image should be changed by an accessing message which sends the changed message. As a consequence, image1 should be

### image1

self image: image1

and the corresponding accessing method should be

### image: anImage

image := anImage.
self changed

We conclude that the process of creating a subview and inserting it into a canvas is quite simple and can be summarized into the following three steps:

1. Paint the *canvas* with a view holder widget, specifying the name of the application model method that returns the corresponding view object as its *View* property.

2. In the *application model*,

a. create an instance variable to hold the view displayed in the view holder, and the corresponding accessing method specified as the *View* property in Step 1

b. initialize the view holder instance variable to an instance of the subview class and specify the application model (*self*) as the view's model (needed only if you use dependency)

c. when using dependency, every method that changes the model and affects the view must send *changed* to *self*. This is best implemented by busing accessing messages and including *changed* in them. When not using dependency, send *self invalidate* to the view when a change of the subview is desired. You can also redraw parts of the view selectively using *invalidateRectangle* or *invalidateRectangle:repairNow*.

3. In the definition of the subview class

a. specify *View* as the superclass

b. define a *controllerClass* method to return the class of the desired controller

c. define an *update* method if you use dependency to control the subview; if updating consists of sending *invalidate* and nothing else, this behavior is inherited and *update*: is not required.

d. define a *displayOn*: method (always); it generally obtains data from the model (the application) and uses it to redraw itself

As another illustration of the procedure, we will now do an example that shows how to display geometric shapes. The problem will give us an opportunity to see that geometric objects are not displayable in their raw form and require additional care.

## Example 2: Displaying geometric figures

*Problem:* Implement an application with the user interface in Figure 12.11. When the user selects a radio button and clicks *Draw*, the window displays the corresponding geometric shape.
<figure><img src="image_237.png" /><figcaption>Figure 12.11. Example 2: Display of stroked rectangle (left), display of filled wedge (right).</figcaption></figure>

**Solution:** We will use a subview with NoController to implement the drawing area. The application model (class GeometricFigures) keeps reference to the view in instance variable drawingView, and aspect variable object holds the current radio button selection. The draw action method will use dependency and send changed to the model. The corresponding update method will only invalidate the view so we don't have to define it. update will trigger displayOn: which will ask the application model for the selected geometric object (derived from the aspect value of the selected radio button), and display it. Implementation of the corresponding methods is as follows:

Initialization of GeometricFigures

initialize

"Select the top radio button, create a binding to the subview, and define yourself as the subview's model."
object := #line1 asValue. "Initial selection when the window opens."
drawingView := GeometricView new.
drawingView model: self

The action method of the *Draw* button simply triggers the dependency-based update mechanism:

draw

"To redraw the subview, trigger the dependency mechanism."
self changed

The `display-at:` method triggered by invalidate is defined in GeometricView as follows:

displayOn: aGraphicsContext

"Obtain currently selected geometric object and display it in the center of the view."
aGraphicsContext display: model currentObject at: self bounds center

The general-purpose `display-at:` method in GraphicsContext can display any displayable object. In our earlier example, we used shape-specific messages such as `displayLineFrom/to:` but this strategy would be very awkward in this case. The `currentObject` message is defined in GeometricFigures and uses the radio button selection to get the desired object as follows:

currentObject

"Use currently selected radio button value to obtain the corresponding geometric object."
^self perform: object value
If the selected button is, for example, *Rectangle* (aspect object == #rectangle), the last line executes

**self rectangle**

and to complete our implementation, we must define the methods that calculate and return the appropriate
displayable geometric objects. A very important feature of all of them is that `display-at:` cannot display
geometric objects in their raw state and the object must first be 'wrapped' by conversion messages
`asStroker` (for empty figures such as the rectangle on the left of Figure 12.11) or `asFiller` (for filled figures
such as the wedge on the right of Figure 12.11). As an example, the method creating the wrapped rectangle
is

**rectangle**

"Return a displayable rectangular border. The coordinates are calculated on the premise that the view displays the object in the center."

^(Rectangle origin: -60 @ -30 corner: 60 @ 30) asStroker

and the method creating the wrapped wedge is

**wedge**

"Return a displayable rectangle. The coordinates are calculated on the premise fact that the view displays the object in the center."

^(EllipticalArc

boundingBox: (-50 @ -20 corner: 50 @ 20)

startAngle: 0.0

sweepAngle: 300.0) asFiller

We leave the remaining methods as an exercise.

**Main lessons learned:**

* Custom components can be created with the view holder widget which contains a subview with a controller.

* The only required property of a view holder is *View*. Its value is the name of the method that returns the subview's view object.

* In order to inherit the damage repair mechanism, the subview should be a subclass of *View*.

* The view class must define the `displayOn:` message which is sent automatically whenever the window needs to draw or redraw the subview. Also required is message `defaultControllerClass`.

* If response to changes of the view's model uses dependency, the view must also define an update method. If the only required action is `invalidate`, the behavior is inherited and `update:` is not needed.

**Exercises**

1. Does *View* define a default controller class? If so, what consequences does it have for view design?

2. Describe in detail what the `asStroker` and `asFiller` messages do. Inspect the result of sending such a message to a rectangle.

3. Write a description of wrappers.

4. Reimplement Example 1 using direct invalidation instead of dependency.

5. Extend Example 2 by adding a group of radio buttons labeled *stroked* and *filled* that allow the user to select how the geometric object should be displayed.

6. Which class defines the `update` message inherited by views?

7. Record a complete trace of an update of a view.

**12.7 Controllers**
Controllers are responsible for managing user input and the class library contains many controller
classes for various kinds of widgets and tools. Only a few, however, are of interest to most programmers.
The essence of the controller class hierarchy (with controllers commonly needed in applications shown in
bold) is as follows:

Object ()
Controller ('model' 'view' 'sensor')
    ControllerWithMenu
        ModalController
        ParagraphEditor
            TextEditorController
                SequenceController
                LauncherController
                MenultemController
            NoController
                ScrollbarController
                StandardSystemController

The Controller class on the top defines all essential instance variables and behaviors and is often
used as the direct superclass of custom controllers. Its instance variables include model and view which are
responsible for communication with the other two components of the MVC triad, and sensor - an instance
of a concrete subclass of the abstract class InputSensor which handles mouse and keyboard input and can
provide information on such parameters as cursor coordinates.

In the past, all VisualWorks controllers used *polling*, a technique which constantly tests whether the user performed an input event, and takes the appropriate action if necessary. Polling requires extra processing time and this may cause some of several input events issued in quick succession to be missed. The preferred modern implementation of controllers is thus *event-driven* where VisualWorks is immediately notified by the operating system when an input event takes place, and sends this information to a controller in the form of an appropriate predefined notification message<fnref n="2" />. These notification messages are defined in the events protocol in class Controller and include entryEvent: and exitEvent: (automatically sent to the controller when the cursor enters or leaves the area of the controller's view), doubleClickEvent;, redButtonPressedEvent;, key Pressed Event;, and many others.

All event messages have a single argument called event - an instance of an appropriate Event subclass such as CloseEvent, Key Pressed Event, and Mouse Moved Event. The Event object contains information relevant for the event that triggers it such as the x and y coordinates of the current position of the hot spot of the cursor or the activated key. All of these predefined messages are stumps containing only an empty body, and concrete subclasses redefine them to perform whatever actions are necessary when the corresponding event occurs. In other words, event messages are hooks. We will see examples of them shortly.

Besides Controller, the most important controller classes are

*   **StandardSystemController** - in charge of window interaction, in particular its `<window>` pop up menu

*   **ControllerWithMenu** - has a menuHolder for the `<operate>` menu, and a performer (the object that performs menu commands)

*   **TextEditorController** - used by the text editor widget

*   **ParagraphEditor** - superclass of **TextEditorController** defining most of its functionality

*   **NoController** - controller that does not respond to any user input

In this chapter, we are interested in controllers in the context of creating new UI components. We have already seen that this is done by using the view holder widget and defining a view-controller pair for it. In the previous section, we used NoController because our subviews did not allow any user interaction. In

<footnote n="2">² The actual implementation of events in VisualWorks is still based on polling but the outward appearance is event-driven.</footnote>
the following example, we will create a UI component with an active user interface and show how to create
an active controller that responds to user input.

Example: Subview with clickable hot spots

*Problem:* Implement an application that displays a bordered subview (Figure 12.12) equipped with an `<operate>` menu and capable of responding to mouse clicks. When the subview is empty, the menu contains only the *Add hot spot* command. When the view contains at least one hot spot, the menu also displays a *Remove hot spot* command.

When the user selects *Add hot spot*, the program requests a string (hot spot name) and the cursor changes to cross hair. (The cursor changes back to the original shape whenever it leaves the subview area and changes back to cross hair when it re-enters the subview.) Clicking the left mouse button inside the subview creates a 'hot spot' and displays it as a small red rectangle. The cursor then changes back to its original shape.

When the user clicks *Remove hot spot*, the cursor changes to cross hair and clicking the left mouse button over a hot spot displays a warning with the name of the hot spot and deletes the hot spot from the subview. (During this procedure, the cursor again changes to the original shape whenever it leaves the subview area.) After redisplaying the view, the cursor changes back to its original shape.

<figure><img src="image_238.png" /><figcaption>Figure 12.12. Example: Clickable subview with four hot spots.</figcaption></figure>

*Solution:* We will need three classes - the application model ( ClickableImage ), the subview class ( ClickableView ), and its controller ( ClickableController ).

Class ClickableController

ClickableController will be a subclass of ControllerWithMenu because the specification requires an `<operate>` menu. It will define menu building methods, and event behaviors for subview entry and exit and for red button press.

To implement the desired cursor behavior, ClickableController must have an instance variable to
hold the shape of the cursor before it changed to cross hair so that we can restore it upon exit from the
subview or when a hot spot related operation ends. We will call this variable cursor. Since cursor is
undefined before the first user action, the exitEvent: method will access it via the following lazy accessing
method:

cursor

"Lazy initialization of cursor."

^cursor isNil

ifTrue: [Cursor current cursor] "Get current cursor shape from the system."
`ifFalse: [cursor]`

The `enterEvent:` message is sent when the cursor enters the subview. It must ask the application model which command is currently executing (we will call this method `state`), and display the cross hair cursor if the command is not `nil`:

**enterEvent: event**

> "If the model is executing the Add or the Remove command, change cursor to cross hair."
model state isNil

`ifFalse: [cursor := Cursor currentCursor.`
`Cursor crossHair show]`

Since this method is defined in the subview's controller, it will only be executed when the controller is in charge and this happens only when the cursor is in the subview's area. The `exitEvent:` method resets the cursor if `state` is not `nil`. Note that we would get the same result if we did not check `state` at all:

**exitEvent: event**

> "If the model is executing Add or Remove, reset cursor on exit from subview."
model state isNil

`ifFalse: [cursor show]`

The state method in ` ClickableImage` returns `#addHotSpot`, `#removeHotSpot`, or `nil`, depending on the command currently being executed.

The handler of the red button click event (again executed only if the cursor is within the subview) asks the model to execute the operation corresponding to the current state (`add/removed hot spot` or `do nothing`), sending the coordinates of the cursor as the argument:

**redButtonPressedEvent: event**

> "Send message with cursor coordinates to model."
|position|

`position := self sensor cursorPointFor: event.``Extract position from event.''

`model executeOperationWith: position` "Result depends on the state of the model."

The next task of the controller is to implement the menus. Since the menu depends on the state of the application, we will leave it to the application to construct the menu as necessary. We are thus finished with the controller!

Class ClickableView

Our next class is ClickableView, naturally a subclass of View. From the previous section, we know that its responsibilities include accessing its controller class, responding to update (we will again use dependency), and responding to `displayOn:`. The controller is ClickableController, hence

**defaultControllerClass**

`^ ClickableController`

There is no need for update since it only invalidates and this is an inherited behavior. And the display message displays all hot spots held by the model as little red squares:

**displayOn: aGraphicsContext**

> "Display all hot spots as red squares centered at hot spot coordinates."
`aGraphicsContext paint: ColorValue red.`

`model hotSpots do: [:hotSpot]`

`aGraphicsContext displayRectangle: (hotSpot at: 1) -2 extent: 4 @ ]`

Class ClickableImage
The last undefined class is ClickableImage - the application model. Its instance variables will be clickableView (for accessing the view displayed in the subview - the view holder's View property), hotSpots (a collection of two-element arrays containing hot spot coordinates and names), and state. We will also find useful two extra variables: hotSpotName will hold the name of a new hot spot, and controller will provide direct access to the controller for menu control - although the controller could be accessed via imageView, the need is frequent and 'caching' the controller in a variable is better.

Variable state requires closer attention. As we noted above, Add and Remove actions are triggered by message executeOperationWith: from the controller. The easy way to execute a message that depends on a Symbol is perform: aSymbol. Since the operation requires cursor coordinates, the perform message must be a keyword message with cursor coordinates. To satisfy the different possible states, we will use the following three values of state: nil (not executing a menu command), #addHotSpotAt:, and #removeHotSpotAt:, and define methods corresponding to the last two values.

We are now ready to implement ClickableImage. Initialization involves setting up instance variables and telling the controller that the performer of the menu messages (the executor of menu commands) will be the model. We must also initialize the menu:

### initialize

hotSpots := OrderedCollection new.

imageView := ClickableView new.

imageView model: self.

controller := imageView controller. "Construct and assign the controller."

controller performer: self. "The performer of menu commands is the application model."

controller menuHolder value: self menuWithAdd "The initial value of <operate> menu."

The following two methods construct the <operate> menu when there are no hot spots and the menu for a view with at least one hot spot. The definitions are as follows:

### menuWithAdd

"Menu when no hot spots exist."

| menuHolder |

menuHolder := MenuHolder new.

menuHolder add: 'add hot spot' -> #addHotSpot.

^menuHolder menu

and

### menuWithRemove

"Menu when at least one hot spot exists."

| menuHolder |

menuHolder := MenuHolder new.

menuHolder add: 'add hot spot' -> #addHotSpot.

menuHolder add: remove hot spot' -> #removeHotSpot.

^menuHolder menu

When the user clicks add hot spot or delete hot spot in, the controller sends addHotSpot or removeHotSpot to its menu performer - the ClickableImage object and our next step is to define these two methods. Method addHotSpot asks the user for the name of the new hot spot and changes state to #addHotSpotAt:. Control then passes into the hands of the controller (defined above) which tracks cursor entry and exit into the subview and sends executeOperationWith: to ClickableImage when the user clicks the red mouse button in the subview. This analysis leads to the following definition:

### addHotSpot

"Start 'add hot spot' operation - get hot spot name and change state; leave the rest to the controller."

hotSpotName := Dialog request: 'Enter hot spot name.'

state := #addHotSpot "Name of method to be executed on click button event."
Method `removeHotSpot` only changes the state:

**removeHotSpot**

"Start remove hot ' operation - change state; leave the rest to the controller."

state := #removeHotSpot "Name of method to be executed on click button event."

After the execution of any of these two methods, the application waits for the user to
move the cursor or click the mouse button which then sends the appropriate mouse event to the controller.
This triggers `redButtonPressedEvent`: defined above, which sends `executeOperationWith: aPoint`. As we
already explained, the method simply performs the state symbol with a Point argument provided by the
controller:

**executeOperationWith: aPoint**

"Execute state operation with point supplied by controller. Ignore if not in 'add' or 'remove' state."

state isNil "If nil, we are not in executing a menu command – no action."

ifFalse: [self perform: state with: aPoint.

self changed]

The real work of adding and removing hot spots is performed by `addHotSpotAt:` and `RemoveHotSpotAt:` and by the dependency mechanism invoked by `changed`. Method `addHotSpotAt:` resets the cursor to what it was before it changed to cross hair. It then adds the new hot spot to the collection, makes sure that the `<operate>` menu now includes *remove* because we now have at least one hot spot, and resets state to nil (end of add state):

**addHotSpotAt: aPoint**

"Add hot spot to collection, reset state and cursor, update menu."

controller cursor show.

hotSpots add: (Array with: aPoint with: hotSpotName).

controller menuHolder value: self menuWithRemove.

state := nil

Method `removeHotSpotAt:` is slightly more complicated because it must check whether the clicked point lies within one of the red squares:

**removeHotSpotAt: aPoint**

"Check if aPoint corresponds to a hot spot and if it does, inform user, display name, remove hot spot from
collection, reset state and cursor, and update menu if necessary."

| hotSpot |

controller cursor show.

(hotSpot := self hotSpotAt: aPoint) isNil "Return hot spot under cursor or nil."

ifTrue: [^Dialog warn: 'Not a hot spot'].

"Display name of hot spot being removed."

Dialog warn: 'Hot spot to be removed: ', (hotSpotAt: 2).

hotSpots remove: hotSpot.

hotSpots empty "Change menu if there are no more hot spots."

ifTrue: [controller menuHolder value: self menuWithAdd.

state := nil

The only remaining method is `hotSpotAt:` aPoint which checks whether aPoint corresponds to a hot spot. It returns a hot spot corresponding to the position if the cursor is in a small area surrounding the hot spot, and nil otherwise:

**hotSpotAt: aPoint**

"Is aPoint within 2 pixels from a hot spot center? If so, return the hot spot."

^hotSpots detect: [:hotSpot | (aPoint - (hotSpot at: 1)) abs <= (2 @ 2)]

ifNil: [nil]

Note the use of point arithmetic and comparison.
## Main lessons learned:

* Although VisualWorks provides polling as one of the ways to program controllers, event driven controllers are now preferred.

* Every view must have a controller, even if it does not allow user interaction.

* Custom controllers are usually subclasses of `Controller` or `ControllerWithMenu`.

* Class `Controller` defines many stump event methods that are triggered by the operating system when a input event occurs. When defining a custom controller, redefine those events methods that must be handled by the subview.

* All event methods have an `event` argument which contains all necessary information about the event that has occurred.

* Class `ControllerWithMenu` adds a menu holder for the `<operate>` menu, and a reference to the performer of the menu commands. The performer is often the application model but the default is the controller itself.

* Class `ParagraphEditor` and its subclass `TextEditorController` add text editing and code execution menu and its support.

## Exercises

1. Reimplement our Example without lazy accessing and evaluate both approaches.

2. Reimplement the hot spot example by replacing the array representation of hot spots with a class called `HotSpot` and consisting of a name and a rectangle with the appropriate dimensions.

3. Draw a scenario diagram showing what happens when the user clicks `add` or `remove` in our example.

4. Explore classes `Controller`, `ControllerWithMenu`, `StandardSystemController`, and `NoController` and write a description of their essential behaviors.

5. It is not always necessary to specify the performer of a menu because `performer` has a default. What is this default and what does this definition imply?

6. Explore classes `ParagraphEditor` and `TextEditorController` and write a description of their essential behaviors.

7. Explore event methods and event classes and write a description of their essential behaviors.

8. Method `displayOn`: in our example redraws the whole subview which is not necessary. If we just added a new hot spot or deleted an existing one, we only need to redraw the corresponding small part of the subview and this can be achieved by using `invalidateRectangle`: instead of `invalidate`. Modify the program to use this approach and comment on the result.

9. Add command `change background color` to the `<window>` menu. The command will display a multiple choice dialog listing all predefined colors and when the user selects one, it will change the color of the currently active window. (Notes: You must reinitialize class `StandardSystemController` after changing the menu - why? After changing the color, send `display` to the window for the change to take effect.)

10. The concept of a clickable view has uses beyond our simple example. List several applications that could use mouse sensitive areas and design a general purpose ` ClickableController` suitable for as many uses as possible.

11. Develop an application for drawing curves defined by mathematical formulas. We leave the detailed specification to you but the general principles should be as follows: The user interface allows the user to enter a function as a one-argument block, the start and end points of the argument, and the color in which the curve is drawn. Several curves may be drawn in the window at the same time and on the same scale. The window is self-repairing.

12. The previous exercise suggests the idea of a curve-drawing widget, along the lines of the Calendar widget described in Appendix 2. Write the specification and develop such a widget. Test your widget by reimplementing the previous exercise.

13. Reimplement the Calendar widget using a subview.

14. The `TextCollector` class which is the model of the Transcript is a very interesting counterpart of the Text Editor because it maintains focus at the end of its text contents: All output to it is automatically
appended at the end. This is very useful in applications such as system logs or logs of network activity which must be maintained in the order in which they are generated. Unlike the Text Editor, however the TextCollector does not have its own button on the UI Palette and if you wish to use it, you must use other means. Give a detailed description of how you could incorporate Transcript-like behavior in an application window.

## Conclusion

VisualWorks user interfaces are based on three kinds of objects: display surfaces, the objects being displayed, and the object doing the drawing and holding the drawing parameters.

Display surfaces can be divided into those dedicated to displaying on the screen and those dedicated to printing. Screen related display surfaces include windows, masks, and pixmaps. Among the several kinds of windows, ApplicationWindow is most important because it implements canvases drawn with the UI Painter. Pixmaps are used to construct displayable objects in memory before they are displayed on the screen, and masks are used to control which part of an underlying image is displayed and which is hidden or transparent.

Programming of a window is usually restricted to controlling window parameters such as window label or background paint. The programming of a usually consists of displaying an image on it and masking it with a mask. Sometimes, a pixamp is used just to construct an image, for example as a composition of geometric objects. The programming of a mask usually consists of displaying a shape object that defines the mask's transparent area.

The drawing of visual objects is performed by a GraphicsContext. This crucial class defines methods for displaying objects such as images, text, and geometric objects, and holds display parameters such as line width, text font, color, and clipping rectangle which defines the part of the window or subview used for display. All of these parameters can be modified programmatically at run time. GraphicsContext is essential for the design of custom GUIs because it implements all display.

Visual objects that can be drawn by a GraphicsContext on a display surface include strings, geometric objects, and images. Geometric objects can be drawn either as anonymous objects using display:at: or by object-specific messages such as displayRectangle:at:. In the first case, the object must first be wrapped as a stroker or as a filler. Object-specific methods such as displayRectangle:at: include this operation.

Although most applications of paint use instances of ColorValue and paint the surface or display the object in a single uniform color, paint can also be an instance of a Pattern. A Pattern is essentially an image used as a tile to wallpaper a rectangular area on a display surface.

Images are pixel-by-pixel representations of rectangular areas. Their public protocol is defined in the abstract class Image which transparently delegates work to its concrete subclasses that implement images for specific image depth. (Image depth is the number of bits used to represent the color of one pixel.)

Another object useful in GUIs is the cursor representing the position of the mouse on the screen. Class Cursor contains a variety of predefined cursor shapes, and new ones can be created by specifying cursor image, mask (defines the see-through area), hot spot (the Point returned when the cursor is queried for its position), and name. A convenient way to create a cursor shape is by using the Image Editor tool which contains a special command for saving an image as a mask.

To create a new GUI component such as a chess board or the face of a clock, select the subview widget (view holder) from the UI Palette, paste it on the canvas, and define at least its *View* property - the method that returns the view object displayed in the subview. As the next step, construct your view class, defining at least method displayOn: aGraphicsContext which is the basis of the display of the subview. The method displays on a GraphicsContext supplied by the application window, with its default parameters which can be changed in displayOn:. If you use dependency, you must also sometimes define an update method. In most cases, update sends invalidate to self and this behavior is defined in a superclass of View and inherited. If your update is more complex, for example requiring invalidateRectangle: you must define update in your view class. Invalidate messages travel up the component tree to the window which then calculates the clipping area of its GraphicsContext and sends displayOn: aGraphicsContext down the tree to
its components with the clipped GraphicsContext. Each component then redraws that part of itself which falls within the clipping rectangle, using the supplied default graphics context parameters or redefining them temporarily.

Older versions of VisualWorks used polling controllers which repeatedly queried the sensor for UI events such as mouse movement or button clicks. Since polling requires extra overhead and since UI events occur fast, this approach often missed UI events. Modern implementations still provide polling controllers but add event-driven controllers which obtain event notification directly from the operating system, eliminate the polling overhead, and thus provide better performance. All you have to do to create a custom event-driven controller is create a new controller class subclassed to ControllerWithMenu (if you require an <operate> menu) or Controller (if you don't need an <operate> menu) and redefine those of its event tracking methods that are important for your subview. If your subview does not have an active user interface, specify NoController as the default controller class.

**Important classes introduced in this chapter**

Classes whose names are **boldfaced** are very important, classes whose names are printed in *italics*
are less important, classes whose names are printed in regular font are not of much interest.

**ApplicationWindow**, **Controller**, **ControllerWithMenu**, **CoverageValue**, **Cursor**, **GraphicsContext**, **Mask**, **NoController**, **Paint**, **Pattern**, **Pixmap**, **TextCollector**.

**Terms introduced in this chapter**

**controller** - part of GUI responsible for user interaction via mouse and keyboard events

**clipping rectangle** - rectangle restricting display area

**cursor hot spot** - Point reported by cursor when it is queried about its position

**damage repair** - repainting of surfaces damaged by closing or overlaying a window

**display surface** - screen or printer page on which painting is taking place; memory representation in the case of mask

**event-driven controller** - controller whose UI event messages are triggered by the operating system (see also *polling controller*)

**filler** - filled geometric object (see also *stroker*)

**graphics context** - object that holds display parameters and performs the display

**mask** - shape overlaid over display area; determines which part of area is visible or transparent

**image depth** - number of bits used to represent one pixel

**invalidation** - the process of notifying a window that a view or its part should be redisplayed; the propagation of this request to the components

**pattern** - a form of paint, a tile that can be used to wallpaper a rectangular area

**paint** - a color used to paint a surface or a pattern used to tile it

**pixel** - picture element, an addressable dot on the screen

**polling controller** - a controller that repeatedly asks its event sensor for UI events and responds appropriately; outdated (see *event-driven controller*)

**stroker** - contoured geometric object (see also *filler*)

**subview** - contents of a *view holder*

**view** - object responsible for displaying a predefined or custom GUI component

**view holder** - widget used to create custom visual components
# Chapter 13 - Processes and their coordination, additional UI topics

## Overview

Up to now, we were developing applications that ran until completion, run for a while, switch to another application, run the other application, return to the original application, and so on. However, there are many applications that should run while other applications run, automatically taking turns with them, using their share of CPU time when they need it and returning control to other applications when they don't need it any more. Some examples of this are network communication (the network communication program must be ready to run at any time and give up control when it does not need the CPU any more), printing while another program is running, and getting data from the web while the user is doing something else. Simpler examples include a computer clock that displays time without interfering with other programs and losing seconds, a stopwatch running in parallel with other programs, and alarms that run in the background, marking time and producing screen notification at a time previously specified by the user.

There are even applications that require that several of their own components run 'simultaneously'. As an example, it is not unreasonable to design a simulation of an airport with multiple airplanes landing, taking-off and circling the airport in such a way that each airplane behaves as if it had its own computer supporting it. And the Smalltalk environments itself has several 'applications' running at the same time - if we consider, for example, the independent process of garbage collection as an 'application'.

All of these examples use the principle that a CPU does not have to be dedicated to a single program or a single thread within one program, but can switch between any number of programs or threads of execution in an organized way, giving the appearance that they all run independently of one another and in parallel. In computer science terminology, these independent threads of execution are called processes and this chapter gives an introduction of their implementation in VisualWorks Smalltalk.

Please note that this chapter is not a complete treatment of processes but rather an introduction that illustrates the concept on several intuitive problems. We also use our examples to introduce some new aspects of the construction and control of custom UI components.

## 13.1 A stopwatch and the concept of a Process

Some very natural programming problems require splitting the use of the CPU among several threads of tasks. The following example illustrates one such a situation.

### Example: Analog and digital stopwatch

**Problem:** Design a stopwatch with analog and a digital components as in Figure 13.1. Clicking *Start* starts the stopwatch, updating both the analog and the digital part in one-second increments. Clicking *Stop* freezes the display, and clicking *Reset* resets the display to 0 seconds. The stopwatch must have minimal effect on the operation of the rest of the environment in that it should be possible to run other applications while the stopwatch is running, and running another application should not stall the stopwatch.

<figure><img src="image_239.png" /></figure>
<caption>Figure 13.1. Stopwatch example.</caption>

**Solution:** The principle is straightforward: When the user clicks *Start*, the program begins an infinite loop that increments time every second and updates the display. *Stop* and *Reset* have obvious effects. The solution will require only an application model class (*StopWatch*) and a view class for the display of the analog watch (*WatchView*). Since the user cannot directly interact with the analog display, the subview does not need an active controller.

Although the principle is simple, the new problem is that the program must act independently of other programs.

To start, we painted the canvas and assigned the following properties: *time* is the Aspect of the input field showing digital time, *watch* is the Aspect of the view holder, and *start* and *reset* are the names of the action methods. We then proceeded to design and implement the two classes as described next.

### Class WatchView

As a view class, *WatchView* requires a *displayOn:* method to display the analog face and handle damage repair. Updating will use dependency and method update:

Method *displayOn:* will draw the seconds marks around the face of the clock, and the hand. The hand (but not the marks) must also be redisplayed when time changes and when the user clicks *Reset*. Displaying the hand in a new position requires erasing the current hand, and drawing a new one in the new position. Erasing can be done by redrawing the hand in the color of the background. We will implement this mechanism by four methods: two low-level methods *erase:* and *draw:* that draw and erase the hand, and two high-level methods *move:* and *reset:* that 'move' the hand to a specified second or to the starting position respectively. All four will take integer time in seconds as its arguments, and *move:* and *reset:* will combine erasing the hand in its current position and redrawing it in the new position. Method *move:* will be used during regular stopwatch operation, *reset:* for resetting.

Let's analyze the details of drawing. The marks around the face are drawn in one-second intervals. Since there are 60 seconds in a minute and one minute corresponds to a full circle on the stopwatch, mark locations are separated by $2\pi/60 = \pi/30$ radian increments. The point at which the mark is drawn is best obtained by using polar coordinates - the radius (constant) and the angle (60 increments of $\pi/30$ each). The angle of the hand is calculated similarly - by multiplying integer seconds by $\pi/30$.

We must now consider how to implement this operation efficiently because we want the display to take as little time as possible, partly to eliminate flashing, and partly to take away as little CPU time as possible from other running programs. Calculating $\pi$ divided by 30 every time we want to display a mark or the hand is obviously unnecessary since we can pre-calculate this constant and cache it in an instance variable. Another constant can also be pre-calculated: The starting point of the stopwatch (0 seconds) is at the top of the circle, in other words, at -90 degrees with respect to the x axis. 90 degrees corresponds to one quarter of a circle, in other words $2\pi/4 = \pi/2$. We will calculate this value during initialization and save it in another instance variable. We also need a variable to hold the coordinates of the center, the length of the hand, and the radius of the circle of the marks around the face. These variables must all be initialized before we can execute the *displayOn:* method.

Considering that the center of the subview can only be calculated when the subview has been created (either before or after the window opens), we will trigger this initialization from the *postBuildWith:* method in the *StopWatch* application model; it would not work if triggered from *initialize* which is executed before the subview is built. We will call this initialization method *setParameter* and its definition in the subview class *WatchView* is as follows:

### setParameters

“Precalculate essential parameters of stopwatch geometry.”

#### | bounds |

<table><tr><td>bounds := self bounds.</td><td>"Obtain square containing the subview."</td></tr><tr><td>center := bounds center.</td><td></td></tr><tr><td>radius1 := bounds width - 20/2.</td><td>"End point of hand."</td></tr><tr><td>radius2 := radius1 + 6.</td><td>"Circle on which marks are drawn around the face."</td></tr><tr><td>const2 := Float pi / 2.</td><td>"90 degrees in radians - corresponds to 15 seconds."</td></tr></table>
const1 := const2 / 15

"Separation of seconds in radians."

Method `displayOn:` which redraws the whole face of the digital clock with the marks and the hand is as follows:

**displayOn: aGraphicsContext**

"Display markings and hand."

| theta |

"Draw marks around the face."

theta := 0. "Angle at which the next mark will be drawn."

1 to: 60 do: [:sec] | endPoint |

theta := theta + const1. "Increment by $\pi/30$."

endPoint := center + (Point r: radius1 theta: theta).

aGraphicsContext displayDotOfDiameter: 3 "Seconds mark."

at: endPoint].

self drawHand: model time value. "Draw hand, getting seconds from the application model."

Note that we used addition to calculate `theta` because it is faster than multiplication. The low-level drawing method `draw:` displays the hand by drawing a straight line from the center of the face to the endpoint at an angle given by the time:

**draw: integerSeconds**

"Draw hand in position corresponding to seconds in integerSeconds and update input field."

| endPoint |

endPoint := center + (Point r: radius1 theta: integerSeconds * const1 - const2). "Angle corrected by 90°."

self graphicsContext displayLineFrom: center to: endPoint.

model time value: integerSeconds "Display digital value in input field."

Method `erase:` is very similar and the only differences are that it changes the paint of the graphics context to that of the background (to make the old hand invisible) and that it does not rewrite the input field. We found how to get the background color of a widget in the Cookbook.

**erase: anInteger**

"Erase hand in position corresponding to seconds in anInteger."

| backgroundColor endPoint |

backgroundColor := (model builder componentAt: #watch) lookPreferencesbackgroundColor.

endPoint := center + (Point r: radius1 theta: anInteger * const1 - const2).

(gc := self graphicsContext)

paint: backgroundColor;

displayLineFrom: center to: endPoint

Note that if we were sure that we always wanted stopwatch background to have the same color, for example white, we could assign it directly as `ColorValue white`. The more general approach that we have used will work even if we change the background color property of the widget.

As we already said, the high level drawing and resetting is done by `move:` and `reset:`. The `move:` method first erases the old hand and then displays the new hand in the new position:

**move: anInteger**

"Move' hand to position defined by anInteger."

self erase: anInteger - 1;

draw: anInteger

The `reset:` method erases the hand at the old position (argument) and redraws it at 0 seconds as follows:

**reset: anInteger**

"Reset hand from position defined by anInteger to start position."

self eraseHand: anInteger;

drawHand: 0
The only remaining WatchView method is `update:`. This method is executed when the model sends itself changed: and this happens when the user clicks `Reset`, and inside the infinite loop when time increments. Each of these two behaviors is different and we must thus use changed: with a symbolic argument to trigger the proper response. We must also include an argument representing the time from which the stopwatch is being reset (in the case of `reset:`) or the new time in regular operation (method `move:`). If we use `#reset:` or `#move:` as the symbolic argument, the definition can be as simple as follows:

**update: aSymbol with: integerSeconds**

"Update the hand in a way appropriate for the current context represented by aSymbol."

self perform: aSymbol with: integerSeconds

This will require that the model must send changed: with: with arguments `#reset:` or `#move:`, and time.

### Class StopWatch

During the start up of the application, we must initialize the `View` variable to a new instance of `WatchView`, set its model, and initialize the input field aspect variable.

**initialize**

watch := WatchView new.

watch model: self.

time := 0 asValue "The type property of the input field is number."

We have already mentioned that when the builder is finished building the window, we must ask the subview to initialize its parameters in `postBuildWith`:

**postBuildWith: aBuilder**

watch setParameters

where `setParameter`s is the `WatchView` method defined above. Except for accessing methods, it now only remains to write action button methods. The `Start` button opens an infinite loop in which it creates a one-second delay, updates time, and sends itself the changed: with: message with appropriate parameters. This is easy except for two things: we must be able to stop the loop by clicking the `Stop` button, and it must be possible to run another program when the stopwatch is active but not running. In the second case, the stopwatch program must wrestle control back when it needs to update.

In essence, what we need is to package the iteration that triggers the watch motion as a separate program, store access to this separate program in an instance variable, and use this instance variable to 'kill' the program from the `Stop` action method. Separately packaged and independently running programs are called *processes* (more on this shortly), and we will thus use the name *process* for the name of the instance variable referring to this separately running block. The whole definition of `start` is as follows:

**start**

"User clicked Start. Start a new separately running unending process and create a reference to it."

process := [(Delay forSeconds: 1) wait.

time value: time value + 1.

self changed: #move: with: time value] repeat] fork

As you can see, VisualWorks' style of creating a process is to send the `fork` message to a block whose contents is the program corresponding to the process. This schedules the block for evaluation (more on this later) and makes it accessible as a process. Our particular block (process) runs only long enough to execute the `Delay` statement which stops the process for one second - allowing any other waiting independent program (process) to start running (Figure 13.2). (This description of the operation of processes is simplified and we will give a more accurate description later.)

stopwatch process

<figure><img src="image_240.png" /></figure>

some other process

<figure><img src="image_241.png" /></figure>

<figure><img src="image_242.png" /></figure>

<figure><img src="image_243.png" /></figure>
<figure><img src="image_244.png" /><figcaption>Figure 13.2. Simplified behavior of processes: Stopwatch runs until stopped by Delay. At this point another process can start execution and the stopwatch resumes when the one second delay expires.</figcaption></figure>

The *Stop* button stops the infinite iteration process started by the *Start* button by sending it the *terminate* message:

**stop**

"Terminate the process implementing continuous stopwatch operation."
process terminate

By the way, stop does not send changed:with: because it does not have any effect on the display - it just freezes the iteration. Action method reset:, on the other hand, sends changed:with: with #reset: and the current value of time because it must erase and redisplay the hand. The time argument is used to determine where the hand to be erased is:

**reset**

"Use dependency to reset stopwatch display to 0."
self changed: #reset: with: time value

where the 0 argument of with: is irrelevant because the message eventually executes reset in WatchView which resets to 0 anyway.

The program is now complete and ready to run! It works almost perfectly and allows us to execute short messages while the iteration is delayed. There is, however, one problem: If we start executing a longer message while the stopwatch runs, the display will freeze and continue incrementing only when the other program stops. To check this out, start the stopwatch, type

[3 factorial] repeat

into a Workspace, and execute it. The stopwatch will complete its current display, the factorial loop will jump in, and the stopwatch will never again get a chance to update. This behavior violates the part of the specification that states that execution of another program should not affect the stopwatch. Let's explore what is happening and how we can correct it.
When we start the stopwatch, the block

[(Delay forSeconds: 1) wait.
time value: time value + 1.
self changed: #move: with: time value]

starts executing and stops when it reaches

(Delay forSeconds: 1) wait

At this point, another program (such as our factorial loop - call it X) can take over. If this happens, we now have two programs competing for the CPU - the stopwatch, and program X. In computing terminology, these two independent programs are processes.
Roughly speaking, Smalltalk processes are designed in such a way that if one process runs, all other processes must wait until it is completed.¹This arrangement is acceptable in most but not in all situations and our case is one of the exceptions: Since the stopwatch must not get behind, its process must be able to execute whenever it needs to update time and this means that the stopwatch process must have priority over other processes. If another process with lower priority is executing when stopwatch needs to update, the stopwatch process must assume control and execute. This is indeed possible because VisualWorks implements the concept of process priority and allows us to assign any one of 100 different priorities to any process.

With the concept of *process priority*, we can describe the operation of Smalltalk processes more accurately as follows: A process runs to completion, unless another process with higher priority wants to gain control. If this happens, the higher priority process becomes active and the lower priority process yields and waits until the higher priority process terminates or yields control. (In the stopwatch infinite loop, this happens when the process executes the *Delay* expression). This principle is illustrated in Figure 13.3.

<figure><img src="image_245.png" /><figcaption>Figure 13.3. Processes and priorities: When a high priority process yields, lower priority processes can start execution. When a higher priority process requires processing, lower priority processes yield.</figcaption></figure>

As we said, VisualWorks distinguishes 100 different levels of priority. Priority 1 is the lowest and 100 the highest. To maintain consistency with previous releases and to provide protection from changes of numbering in new releases, some priorities used by the system have names and can be assigned by sending the appropriate message to Processor. These named priority levels should be used whenever possible and their names and uses are as follows:

<table><thead><tr><td>Priority number</td><td>name (method)</td><td>Purpose</td></tr></thead><tbody><tr><td>100</td><td>timingPriority</td><td>Processes dependent on real time.</td></tr><tr><td>98</td><td>highIOPriority</td><td>Critical I/O such as network input.</td></tr><tr><td>90</td><td>lowIOPriority</td><td>Normal I/O such as keyboard input.</td></tr><tr><td>70</td><td>userInterruptPriority</td><td>High priority user tasks such as window management.</td></tr><tr><td>50</td><td>userSchedulingPriority</td><td>Normal user programs - default assigned by VisualWorks.</td></tr><tr><td>30</td><td>userBackgroundPriority</td><td>Background user processes.</td></tr><tr><td>10</td><td>systemBackgroundPriority</td><td>System background processes.</td></tr></tbody></table>

As an example, priority 50 can be assigned as

### Processor userSchedulingPriority

although this particular priority normally does not have to be assigned because it is the default assigned when a user program starts executing. A user program with higher than normal priority - such as our stopwatch which should be able to interrupt other user programs - should thus run at *user interrupt priority* or at *timing priority* if the displayed time is really critical and must not suffer from any interruptions at all. We will run our stopwatch process at *user interrupt priority*.

<footnote n="1">¹ Process designs that do not allow other processes to run until the running process stops are called *non-preemptive*. In spite frequent statements to the contrary, we will see that VisualWorks processes are preemptive because a running process can be interrupted by a higher priority process.</footnote>
In our modified start method, we want to create the stopwatch process, assign priority to it, and
schedule it immediately. The simplest way to do this is to use forkAt: aPriority instead of fork as follows:

forkAt: Processor userInterruptPriority

The only change in our program is thus in process creation which now becomes

start

"Start a new separately running unending process and create a reference to it."

process := [Delay forSeconds: 1.
             time value: time value + 1.
             self changed: #move: with: time value] repeat] forkAt: Processor userInterruptPriority

When you now start the stopwatch and execute, for example,

[Transcript show: 'test'; cr] repeat

the stopwatch will continue running without interruption and the Transcript will continue printing, being interrupted (imperceptibly) by the higher priority stopwatch whenever its Delay expires (Figure 13.4).

<figure><img src="image_246.png" /><figcaption>Figure 13.4. Higher priority stopwatch process taking turns with a lower priority process.</figcaption></figure>

After this introduction to processes, we are now ready to take a closer look at their role and
operation in VisualWorks.

Processes

Let's summarize what we have learned about Smalltalk processes so far: A process is a standalone thread of execution that can run on the same CPU and in the same Smalltalk image independently of other Smalltalk programs. Processes can be assigned priorities between 1 and 100 and at any given time, the highest priority process that is ready for execution runs. If a higher priority process requires processing, the running lower priority process is interrupted and its execution resumes when its priority becomes the highest waiting priority.

To create a process, send a process creation message to a block. We have introduced two process creation messages - fork and forkAt: The first creates a process with the same priority as its parent process (the process from which it was forked) and starts its execution. The second assigns the process the specified priority and starts its execution if appropriate.

Creation of Smalltalk processes using fork essentially evaluates a block and message fork is thus quite similar to message value.<fnref n="2" /> There is, however, an essential difference the two messages: The value message immediately evaluates its block and the message following the block must wait until block evaluation is finished. As an example,

[3 timesRepeat: [Transcript show: 'test')] value.
Transcript show: 'end of test'

prints

test

<footnote n="2">² Just as there are variations of value that send one or more arguments to a block, there is also a process creation message that allows forking a process with any number of arguments</footnote>
test
test
end of test

This is not so for forking processes. As an example,

[3 timesRepeat:

[Transcript show: 'test'] forkAt: Processor userBackgroundPriority.
Transcript show: 'end of test'

creates a *low priority* process, delays its execution until the parent process (the encompassing program) ends execution, executes the last line (thus ending the parent process), and if there are no other higher priority processes waiting, execution now returns to the suspended lower priority process and executes it.
The code will thus print

end of test
test
test
test

in the Transcript. In fact, we will get the same result even if the new process does *not* have lower priority
than the parent process as in

[10 timesRepeat:

[Transcript show: 'test'] forkAt
Transcript show 'end of test'

because the new process will be placed in a queue behind the parent process.

The only fundamental question that remains unanswered is who manages the processes and their priorities. Implementing process management is the role of class ProcessorScheduler which schedules the order in which the processor (the CPU executing the image) executes all existent processes. Since a single Smalltalk image needs only one scheduler, an image has only one instance of ProcessorScheduler and its name is Processor. Processor is a global variable like Smalltalk.

To close this section, we will now examine how Processor handles processes starting with a look at class Process. Class Process has several instance variables but for our purpose the most important ones are priority (an Integer between 1 and 100, naturally), and lst. Variable priority is, of course, process priority, and lst is a list holding all processes of the same priority in the order in which they will be activated after this process. When a new process with this priority is created, it is added to the end of this queue. This is why the forked process in our last example executed after its parent.

Normally, the first process in the queue must execute to completion before the process following it
in the queue can start executing. One can, however, suspend a process (blocking its operation until it gets a
receive message) or send it to the back of the queue by executing

Processor yield

To force a process that has not yet terminated to stop and to remove it from the queue, send it the message terminate. This is what we did in our stopwatch program - the stopwatch process runs in an infinite loop and the only way to stop it is to terminate it. Finally, for each priority, Processor (the single instance of ProcessorScheduler) keeps an array holding all process queues and uses it to determine which process to execute next.

This completes our introduction to processes and we will give further examples of their use and
explain additional concepts in the remaining sections of this chapter. One important point that we have not
covered at all is the coordination of processes. The point is that processes sometimes depend on one another
and one cannot proceed before another completes some operation. As an example from the real world,
consider the following somewhat unusual arrangement of traffic lights at a crossing of a road and a railroad
designed to prevent the possibility of a collision: When a train or a truck arrive at the crossing, they change
the other vehicle's traffic lights to red to prevent collision. Upon leaving the crossing, the vehicle changes the other vehicle's traffic lights back to green. We will write a computer simulation of this arrangement in Section 13.3 and show how the desired coordination can be achieved by using the concept of a semaphore.

## Main lessons learned:

* A process is a block of code – a program that shares the CPU processor and other resources with other programs under the control of a processor scheduler.

* Non-pre-emptive process scheduling lets a process run until it terminates or until it suspends its execution.

* Smalltalk processes are instances of class *Process*; they are pre-emptive because an available higher priority process interrupts a lower priority one.

* Three important *Process* messages are *suspend*, *resume*, and *terminate*. Message *suspend* takes its receiver out of the queue of runable processes but does not destroy it. Message *resume* puts a suspended process at the end of the queue of runable processes, and shuts down and destroys the process.

* Smalltalk processes have priorities between 1 (the lowest) and 100. By default, user processes run at priority 50, I/O processes such as mouse and keyboard events run at priority 90, real-time processes run at priority 100, and system background processes run at priority 10.

* To create a process, send fork or forkAt: priority to a block. This puts the new process at the end of the queue of runable processes of the same priority and schedules it for execution.

* Smalltalk processes are managed by *Processor*, the single instance of class *ProcessScheduler*.

* *Processor* always selects for execution the first process in the highest priority non-empty queue of runable processes.

## Exercises

1. In our reset method, we used `reset: time value`. Shouldn't this be `reset: 0`?

2. Modify the stopwatch program to eliminate any effect of clicking *Start* or *Reset* while the stopwatch is running.

3. Add numeric time marks next to 0, 15, 30, and 45 second ticks.

4. Reimplement the stopwatch by creating an array of 60 hands, one for each possible hand position and each capable of drawing itself in a specified color. Then implement drawing and erasing by redrawing the current hand object in the background color and displaying the new one in the foreground color. Comment on the relative speed of both approaches.

5. Generalize the stop watch design by letting the creation method specify both the foreground and background colors.

6. Develop a digital version of a minute/hour watch.

7. Develop a Watch application with an analog face for hours and minutes.

8. Combine *StopWatch* and *Watch* into one application showing hours, minutes, and seconds.

9. Create other user interfaces for digital or analog hours, minutes, and seconds, and allow the user to select any desired combination.

10. Combine stopwatch, watch, and calendar into a new time machine application widget.

11. Redefine the background paint of the stopwatch to a nice inconspicuous *pattern*.

12. Assume that an application can get blocked because a process that it is running can be suspended. Assume that the user interface contains an action button designed to restart the application. Since the button is a part of the application, the button cannot be used to unlock the process. True or false?

13. Assume that a forked off process has higher priority than the parent process. Will the parent process run to completion before the child process starts executing?

## 13.2 Alarm Tool
After implementing a stopwatch, the obvious next candidate in our exploration of time machines is an alarm clock. Since a flexible implementation of automated alarms opens some interesting new questions and helps to explore the concept of processes, we will dedicate this section to the design of a simple application allowing the user to create, edit, and delete any number of alarm notifications that pop up on the screen when their time arrives.

## Specification

Develop a tool for creating, modifying, and deleting alarm events ('alarms', for brief), each characterized by expiry time and a brief description. The user can create any number of alarms at any time and in any order, and they will mature in the order of their expiry time, independently of the order in which they were created. When an alarm matures, it opens a notifier displaying its description. If two or more alarms mature at the same time, their notifiers are displayed in the order in which they were created. The operation of alarms must not interfere with user programs running at default priority, and operation of user default priority programs must not interfere with the alarms.

The desired user interface is as in Figure 13.5 and its behavior is as follows: The list of unexpired alarms has a pop up menu with command *add*. When an alarm is selected is the list, the pop up menu displays additional commands *edit* and *remove*. When the user selects *add*, the program opens the form shown on the right and when the user completes the form and clicks *Accept*, the new alarm is added to the list at a place corresponding to its maturity time. The window opens with current hours and minutes and does not accept time earlier than *Time now*. Clicking *Cancel* closes the form and aborts the operation. Editing uses the same form but the form opens with the parameters of the selected alarm. An attempt to remove an alarm elicits confirmation from the user.

<figure><img src="image_247.png" /><figcaption>Figure 13.5. Main Alarm Tool window (left), form for creating and editing alarms (right).</figcaption></figure>

The first iteration of design and implementation described below is intended as a test of the concept and implements the required functionality in a minimal way and further expansion is proposed as a series of exercises. The reason for this progressive enlargement of functionality is that the problem opens unfamiliar questions and we don't want to get bogged down in too many new questions at once.

## Scenarios

For lack of space, we will leave out the more obvious scenarios and restrict ourselves to the more challenging ones.

### Scenario 1: User adds an alarm to a non-empty list

1. User clicks *add* in the list pop up menu.
2. *Program* opens form.

3. *User* enters information and clicks *Accept*.

4. *Program* closes form, inserts new alarm in the list, and displays the updated list in the main window.

### Scenario 2: User edits an alarm in the list

1. *User* selects an alarm and clicks *edit* in the list pop up menu.

2. *Program* opens the form, showing all parameters of the selected alarm. All fields are editable.

3. *User* edits the form and clicks *Accept*.

4. *Program* closes the form and changes the list to show the new alarm.

### Scenario 3: User deletes an alarm

5. *User* selects an alarm and clicks *remove* in the list pop up menu.

6. *Program* displays a confirmation dialog.

7. *User* confirms.

8. *Program* deletes the alarm and changes the list to show the new alarm.

### Context Diagram

The specification is so simple that there is no need to draw the Context Diagram.
## Preliminary design

This application can be implemented with a single class - the application model (to be called AlarmTool). We don't even need to develop custom view classes because the user interface uses only standard widgets and windows. For cleaner design, however, we will add a class called Alarm to represent individual alarm objects. The preliminary description of our two classes is as follows:

* AlarmTool manages the Alarm Tool user interface, allows the user to create, edit, and delete alarms, and schedules and terminates alarms.

* Alarm holds all information about a single alarm.

## Class level scenarios

### Scenario 1: User adds an alarm to a non-empty

1. User clicks add in the list pop up menu.

2. AlarmTool opens a form (a modal dialog window) with default parameters.

3. User enters information and clicks Accept.

4. AlarmTool closes the form.

5. AlarmTool creates new alarm process with specified parameters and schedules it.

6. AlarmTool creates new Alarm with maturation time, description, and process created in the previous step.

7. AlarmTool adds the new Alarm to its collection and displays the new collection list ordered by alarm maturation time.

### Scenario 2: User edits an alarm in the list

1. User selects an alarm and clicks edit in the list pop up menu.

2. AlarmTool terminates the process associated with the selected Alarm.

3. AlarmTool opens a form with parameters of the selected alarm.

4. User edits the form and clicks Accept.

5. AlarmTool closes the form.

6. AlarmTool deletes the selected alarm from its collection.

7. AlarmTool creates a new alarm process with specified parameters and schedules it.

8. AlarmTool creates a new Alarm with maturation time, description, and process created in the previous step.

9. AlarmTool adds the new Alarm to its collection and displays the new list.

Scenario 3 is similar to Scenario 2 and we leave it as an exercise.

## Preliminary specification of class responsibilities

The class scenarios and the implied behaviors suggest the following responsibilities:

### Alarm

<table><tr><td>Responsibilities</td><td>Collaborators</td></tr></table>

* Know and provide access to maturation time, description, and scheduled process.

### AlarmTool

<table><tr><td>Responsibilities</td><td>Collaborators</td></tr></table>

* Create and manage user interface including the main window and the form.

* Maintain list of alarms in order of maturation. Alarm

* Schedule alarms according to their maturation times.
## Design Refinement

### Class Hierarchy

AlarmTool is an application model, and its superclass is therefore ApplicationModel. Alarm does not have any relatives in the class library and it will thus be a subclass of Object (Figure 13.6).

<figure><img src="image_248.png" /><figcaption>13.6. Hierarchy Diagram.</figcaption></figure>

### Specification Refinement

#### Alarm

This class is a simple data model and requires only accessing methods. There is no need for further details.

#### AlarmTool

The first two groups of responsibilities (user interface and management of alarm list) are rather routine and we will thus start by examining how to implement scheduling, maturation, and expiry of alarms.

Each alarm is a simple process independent of others. Because of the requirement that an alarm be independent of other user programs, alarm processes must run at user interrupt priority. We will use Delay to schedule an alarm for maturation and proper action. The question is whether this will ensure that alarms will indeed mature in the correct order. The point is that alarms may be added in arbitrary order independent of the order of their maturation, and that the process scheduler activates processes in the order in which they occur in the queue which is normally the order in which they were added. It seems that our situation is peculiar in that all alarm processes immediately delay themselves and wake up only when their Delay expires. As a consequence, alarm processes automatically schedule themselves in the correct order.

To test this hypothesis, we will write a code fragment to simulate a mismatch between the desired order of execution of alarms and the order in which they are created by the following code fragment:

```perl
|seconds|
"Create an array of alarm maturation times." seconds := #(7 4 9).
"Use these maturation times to create alarm processes and schedule them in the given order." 1 to: seconds size do:
    [:index | | block |
        block := [(Delay forMilliseconds: (seconds at: index) * 1000) wait.
                   Dialog warn: 'Alarm number: ', index printString].
        block fork]
```

We expect that the alarm created second will mature first, the alarm created first will mature next, and the alarm added last will also mature last. And this is indeed what happens. The basic idea thus works - but there are two little twists that we must examine next:

1. What if the user edits the maturation time or the description of a scheduled alarm before it matures?

2. What if the user deletes a scheduled immature alarm?
Our view of alarm editing is that editing an alarm is equivalent to deleting an existing alarm and adding a new one. Since we have just tested how to add a new alarm at any time, the only remaining question is deletion of an existing alarm.

To delete an existing alarm, we must terminate its corresponding process and remove the alarm from the list. Quite frankly now, we don't yet have enough experience with processes to satisfy ourselves with this formulation - we are not confident that this strategy will really work. We will thus create a skeleton of AlarmTool implementing just this part of its behavior and test that it works before we proceed to the design of the user interface and the rest. Our implementation may have little to do with our definitive implementation because the user interface will eventually be totally different and there will be some additional requirements, but that's OK. We only jump into the implementation phase for a moment to test the concept, and return to the rest of design later. Don't feel bad about this violation of orderly development, it is quite normal. We cannot continue designing responsibly if we don't know whether an essential part of our construction is valid or not and this detour is thus not only legitimate but even necessary. It also demonstrates that realistic development is loosely structured.

## Partial and preliminary implementation of AlarmTool

For our test, AlarmTool needs only one instance variable (alarms), initialized to a SortedCollection of Alarm instances sorted by maturation time. (Since this is a test class, we called it AlarmToolTest.) According to our analysis, we only need two methods to handle alarm management - one for adding, and one for removing. After the user enters time and comment, the add operation creates and schedules a new process, combines it with time and description into an Alarm, and adds it to alarms. For the time being, we replaced Alarm with a three-element array and implemented the idea as follows:

### addAlarm: anArray

"Create and schedule a process corresponding to alarm (represented by anArray with time and description), and the complete alarm information to alarm collection."

| process |
process := self scheduleAlarmProcessWith: anArray.
alarms add: (anArray copyWith: process) "Concatenate 2-element array into 3-element array."

where the scheduling of the process is performed as follows:

### scheduleAlarmProcessWith: anArray

"Create and schedule a process with parameters described by anArray."

| block |
block := [:seconds :comment |
(Delay forMilliseconds: seconds * 1000) wait.
alarms removeFirst. "Time expired, remove yourself from the list."
Dialog warn: 'Alarm: ', comment].
^(block newProcessWithArguments: anArray)
priority: Processor userInterruptPriority; resume

where newProcessWithArguments: is a message defined in BlockClosure that constructs a process from a block with arguments, in our case time and comment. Since VisualWorks does not have a method to create a process over such a block and schedule it in a single step, we created the process without scheduling it and sent it resume to schedule it. To test our ideas we now used the fragment

| alarm alarms |
"Create an instance of the application."
alarm := AlarmToolTest new.
"Create suitable alarm descriptions."
alarms := #(#(5 'number 1') #(2 'number 2') #(7 'number 3')).
"Add and schedule these alarms one-by-one."
alarms do: [:anAlarm | alarm addAlarm: anAlarm]
This should open alarm notifiers in the order 'number 2', 'number 1', and 'number 3' - and it does.
Everything is OK thus far.

Now for *alarm removal*. In the final implementation, the alarm will be obtained from the user interface and thus guaranteed to be an element of *alarms*. For our test, we must remove alarms by manipulating them very explicitly as in

```
| alarm alarms |
"Create an instance of the application."
alarm := AlarmToolTest new.
"Create suitable alarm descriptions."
alarms :=.#(6 'number 1')#(2 'number 2')#(10 'number 3').
"Add and schedule these alarms one-by-one."
alarms do: [:anAlarm | alarm addAlarm: anAlarm].
"Remove and terminate an alarm."
alarm removeAlarm: #(6 'number 1').
"Add and schedule a new alarm."
alarm addAlarm: #(7 'number 4')
```

The *remove* operation must find the appropriate element in the collection, get its process component, and terminate it:

**removeAlarm: anArray**

"Remove alarm from collection and terminate its alarm process."

```
| anAlarm |
anAlarm := alarms detect: [:el | (el at: 1) = (anArray at: 1)].
(anAlarm at: 3) terminate.
alarms remove: anAlarm
```

With this method, we can now run the test to add, remove, and add processes listed above. We are extremely happy to report that everything works as desired: Notifiers 2, 4, and 3 open in this order and nothing else. However, we must also warn you about another crucial experience.

During the coding and testing, we made a few small mistakes and it turns out that mistakes that affect process scheduling and termination may have very serious consequences because they can affect the operation of the whole VisualWorks environment which is itself based on processes. We suggest that in situations such as these you use the following strategy: Write your code but before you run it, file it out. Then do the test. If a disaster occurs, interrupt or terminate your session and exit without saving if necessary, restart and file in your code, correct it, file out the 'correct' code, and repeat. This way, you will not lose your work or damage the image.

We are now confident that our approach works and we return to design to refine the description of class Alarm.

## Back to design refinement

Our current description of AlarmTool is as follows:

**AlarmTool**

**Responsibilities**

**Collaborators**

* Provide and manage user interface including the main window and the form.

* Maintain list of alarms in order of maturation.

**Alarm**

* Schedule alarms for maturation according to their maturation times.

We will now explore the three responsibilities further and summarize our findings in a final class description.

*Provide and manage user interface including the main window and the form*

The user interface consists of two windows:
*   **Main window.** This window does not need any special widgets but the list requires two different menus depending upon whether an item is selected in the alarm list or not. The list itself is interesting because it displays only one of the three components of each Alarm object, namely its time of maturation. This will be easy to achieve because the list widget displays its constituent objects by sending them displayString and we can redefine displayString in Alarm to return only the Time component. This will ensure the desired display, while selection in the list will return the whole Alarm object.

*   **Form.** This is a dialog window rather than the usual application window and its only special features are as follows:

*   When the form opens on a new alarm, it shows the current time but no description. When the form opens on an existing alarm, it shows the alarm's current parameters.

*   Time control buttons must not allow time less than Time now.

*   Time controls are restricted to 0 - 23 hours and 0 to 59 minutes. We will design them so that when the user clicks ^ at the end of the scale, the time component (minutes or hours) will start wrapping around. As an example, incrementing 59 minutes will produce 0 minutes, and decrementing 0 minutes will give 59 minutes.

*Maintain list of alarms in order of maturation.*

We will keep Alarm instances in alarms, a SortedCollection sorted by maturation time.

*Schedule alarms for maturation according to their maturation times.*

A summary based on our earlier experimental findings is as follows:

*   Adding a new alarm: Open an un-initialized form. If the user closes the form by *Accept*, schedule a process with time and description obtained from the form, create an Alarm with these parameters and the process, and add it to *alarms* and the list widget.

*   Removing an alarm: Ask user for confirmation, extract the process part of the selected Alarm and terminate it, remove selection from *alarms* and the list widget.

*   Editing an alarm. Extract the process from the selected Alarm. Open a form with existing time and description. When the user accepts, terminate the selected alarm and remove it from the list, schedule a new process and construct new Alarm object with new parameters, and add the new Alarm to *alarms*.

We can now write our final class descriptions.

**Alarm:** I provide access to all information about a single alarm.

Components: time (Time), description (String), process (Process).

Responsibilities

Collaborators

*   Creation - with time, description, and process. Method time:desc:process:

*   Accessing: time, description, and process

*   Printing: return string representation with time only via method displayString

**AlarmTool:** I manage the Alarm Tool user interface, allow the user to create, edit, and delete
alarms, and schedule and terminate alarms.

Components: alarms (SortedCollection), aspect variables of main window and alarm form.

Responsibilities

Collaborators

*   Creation and initialization - as for an application model.

*   User interface including the main window, the form, menus, etc.

*   Main window (windowSpec)

*   Form window (form)

*   Resources (*up* and *down* buttons)

*   Pop up menus for the list widget in the main window (add, add/edit/remove)

*   Menu methods

*   Add alarm: open form initialized to current time, get data on *Accept*, schedule new process, create new Alarm, update list; ignore on *Cancel*. Alarm
Method addAlarm.

* Remove alarm: terminate selection's process, update list. Method removeAlarm. Alarm

* Edit alarm: open form initialized to current parameters, get data on Accept, extract process from selection and terminate it, remove selection from alarms, schedule new process, create new Alarm, update list.
  Method editAlarm.

* Maintain list of alarms in order of maturation.

Implementation

We will now show details of implementation of selected methods in the order of classes and their
specification.

Class Alarm

This is a simple domain class and its only interesting method is displayString which is used by the
list widget in the main window. It extracts the time component and converts it to String:

displayString

"Returned time component for display in List."

^time printString

The creation method initializes all three instance variables:

time: aTime desc: aString process: aProcess

"Create fully initialized Alarm."

^ (self new) time: aTime; description: aString; process: aProcess

Class AlarmTool

User interface specs

User interface creation is routine, except for the *up* and *down* buttons in the form window which
use images created with the Image Tool. The corresponding action methods are as follows:

hoursInc

"User clicked to increment hour. Keep hours within limits and wrap around if necessary."

| h |

hours value: ((h := hours value + 1) > 23

ifTrue: [0]

ifFalse: [h]

and

hoursDec

"User clicked to decrement hour. Keep hours within limits and wrap around if necessary."

| h |

hours value: ((h := hours value - 1) < 0

ifTrue: [23]

ifFalse: [h]

for hour buttons, and the action methods for minute buttons are similar.

Initialization
The initialization process is standard; `alarmList` is the Aspect of the alarm list widget:

**initialize**

"Initialize alarms and aspect variables, define change behavior and initial pop up menu."

alarms := SortedCollection sortBlock: [:x:y | x time < y time].

alarmList := SelectionInList new.

alarmList selectionIndexHolder onChangeSend: #changedAlarmSelection to: self.

alarmListMenuHolder := self menuWithAdd asValue.

description := " asValue

## User interface behaviors

According to initialization, the following message is sent when the user makes a new alarm list selection:

**changedAlarmSelection**

"The list widget has a new selection. Update pop up menu and alarm description aspect variable."

| selection |

selection := self alarmList selection.

self alarmListMenu value: (selection isNil

ifTrue: [self menuWithAdd]

ifFalse: [self menuWithAll]).

description value: (selection isNil

ifTrue: [alarms empty

ifTrue: ["]

ifFalse: ['Select alarm to see its description.']]

ifFalse: [selection description])

where `description` is the Aspect of the description widget. Method `alarmListMenu` was specified as the `Menu` property of the list widget when we painted the interface, and the menu building methods are as follows:

**menuWithAdd**

"No alarm is selected, only `add` is allowed."

| mb |

mb := MenuBuilder new.

mb add: 'add' -> #addAlarm.

^mb menu

and

**menuWithAll**

"An alarm is selected, all three commands are available."

| mb |

mb := MenuBuilder new.

mb add: 'add' -> #addAlarm; add: 'edit' -> #editAlarm; add: 'remove' -> #removeAlarm.

^mb menu

The critical operations of adding, editing, and deleting menus are all triggered by the messages specified in these methods and are implemented as follows:
**removeAlarm**

"To remove an alarm, terminate its process and remove it from the alarms collection and the list widget."

| selection |
selection := alarmList selection.
selection process terminate.
alarmList list: (alarms remove: selection; yourself)

Note that we have not included a confirmation dialog; this is left as an exercise.

Adding and editing have much in common (both open the dialog window, respond to *Accept* and *Cancel*, schedule a new process, and update the list) and factoring the shared behavior out simplifies their definition. We define the *add* operation as follows:

**addAlarm**

"Prepare parameters for form (current time, no description) and send doAddition to do the rest."

self initializeFormForNewAlarm.
self doAdd

where the critical work is done in

**doAdd**

"Shared part of add and edit. Open form as dialog, ignore on Cancel, obtain time from hours and minutes, schedule new process, update list."

| process |
(self openDialogInterface: #form)
ifFalse: [^self].
time := Time fromSeconds: hours value * 3600 + (minutes value * 60).
process := self scheduleAlarmProcessWith: (Array with: time with: descriptionInForm value).
alarms add: (Alarm
    time: time
desc: descriptionInForm value
process: process).
alarmList list: alarms

where hours and minutes are Aspect variables of the corresponding input fields. This method is also used by the editing method:

**editAlarm**

"To execute *edit*, prepare parameters for form and send doAddition to do the rest."

self initializeFormForExistingAlarm.
self doAddition

Initialization of the form for a new alarm (see addAlarm) is done by

**initializeFormForNewAlarm**

"Prepare form aspect parameters for new alarm."

self hours value: Time now hours.
self minutes value: Time now minutes.
self descriptionInForm value: "

and editing an existing alarm uses

**initializeFormForExistingAlarm**

"Prepare form aspect parameters for alarm selected in list."

| selection |
selection := alarmList selection.
alarms remove: selection.
selection process terminate.
self hours value: selection time hours.
self minutes value: selection time minutes.
self description value: selection description
By the way, when you open your alarm tool, you may get strange values for starting hours and minutes. To correct this, change you VisualWorks time zone. We leave this as an exercise.

The only remaining action methods are for the *Close* button in the main window, and for *Accept* and *Cancel* in the form. The *Close* button closes the application in the usual way. *Accept* and *Cancel* use the standard behavior of dialog windows and use methods *accept* and *cancel* defined in *SimpleDialog*; these methods close the window and return true (on *Accept*) or false (on *Cancel*). We relied on this behavior in the first statement in method *doAdd* above.

This leaves `scheduleAlarmProcessWith:` which uses the data obtained from the form to create a new process with given maturation time and description, and schedules it. Its definition is

### scheduleAlarmProcessWith: anArray

"Create and schedule a process with data obtained from the form and returned in anArray."

```
| block |
block := [:maturationTime :comment |
    (Delay forMilliseconds: maturationTime asSeconds - Time now asSeconds * 1000) wait.
    Dialog warn: ('Alarm at: ', Time now printString, '\', comment) withCRs].
^ (block newProcessWithArguments: anArray)
priority: Processor userInterruptPriority; resume
```

When the process resumes after the prescribed delay, it open the notification window showing the alarm's time and description, and terminates, removing itself from the process queue.

### Main lessons learned:

* Since processes are created from blocks and blocks may have arguments, processes may also be created with arguments using `newProcessWithArguments`.

* Method `newProcessWithArguments:` creates but does not schedule a process. Scheduling is by message resume.

### Exercises

1. Processes are expensive in terms of CPU requirements and having many of them will slow down VisualWorks. A better approach is to have a single process keeping all alarms in a list and handling them as needed. Reimplement Alarm Tool using this alternative approach.

2. Extend the alarm's notion of time to include date.

3. Add the following behaviors if they are not yet functional:

a. Set alarms remain functional even when Alarm Tool is closed.

b. Set alarms remain functional from session to session.

c. When the Alarm Tool is closed and then reopened later in the same session, non-expired alarms remain set and can be individually edited and modified.

d. Extend Exercise c across sessions.

4. Make it possible to specify alarms in relative terms such as 'in ten minutes'.

5. Add recurrent alarms of various kinds such as 'every ten minutes', 'every ten minutes until canceled by user', and 'every ten minutes, five times in a row.'

6. Add alarms that occur on a specified schedule.

7. According to the current specification, there is no restriction on alarm interference with other higher priority processes such as the stopwatch. Comment on the possibility of running alarms and the stopwatch at the same time, possible problems (if any), and possible remedies.

8. Add a digital clock to Alarm Tool.

## 13.3 Coordinating mutually dependent processes - Train Simulation

In many important situations, two or more processes are mutually dependent because they share some resource. In situations like these, conflicts may arise and processes require coordination. A typical
example of such a situation is printing on a shared network printer. A print job cannot be started while another job is printing and the processor managing the print queue must therefore wait for the printer to signal that it is ready before it sends another job.

A physical world example of the need to coordinate processes is the previously mentioned crossing of railroad track and a highway road: If a train is crossing, vehicles coming to the crossing must stop and this is normally implemented by a semaphore - a traffic light activated by the arriving train, and deactivated when the train leaves the crossing area. We will use this example to illustrate process coordination in Smalltalk. First, however, some background.

## Semaphores

A Smalltalk semaphore is an analogy of a train crossing semaphore implemented as an instance of class Semaphore. The essential messages understood by semaphores include new (to create a new instance), wait (to indicate the desire to use the resource guarded by the semaphore and to turn the 'red light' on), and signal (to indicate that the resource is no longer needed and to turn the 'green light' on).

When two or more processes interest in the same resource, they use a shared Semaphore. Note that a Semaphore has no explicit connection to a process and behaves very much like a portable signaling device that can be carried around and placed to guard a resource anywhere the user wants.

When a process needs to use the resource guarded by a Semaphore, it sends wait to it. The wait message performs two functions: It first checks whether the 'light is green', in other words, whether the resource is available. If it is, it 'turns the light to red' to indicate that the resource is now in use and unavailable to other processes, and the process that sent it can continue executing its block. If the light is red, the Semaphore puts the process in a queue waiting for this Semaphore to turn green again.

When a process wants to indicate that it is finished using the protected resource, it sends the Semaphore the signal message. This message turns the light to green, and if there are any processes waiting at this Semaphore, the one at the head of the queue (the one that was the first to send wait) can resume. As we can anticipate from our knowledge of processes, the process will actually resume only when the currently active process gives up control by terminating, by suspending itself, or by being sent to the end of the queue by Processor yield. Note that the consequence of the described mechanism is that a process that issues wait can only proceed if the semaphore has received a signal message before.

The principles that we have just explained are summarized in Figure 13.7.

<figure><img src="image_249.png" /><figcaption>Figure 13.7. Operation of Semaphore. Any number of semaphores may exist at a time.</figcaption></figure>
## Train simulation - specification

After this introduction, we are now ready to give an example of the use of semaphores.

**Problem:** Develop a simulation of crossing truck and train tracks with the user interface in Figure 13.8. At the beginning of the simulation, the truck is at the top of its track and the train at the left end of its track. When the user clicks Go, the train and the truck start moving at a constant speed, changing direction when they reach the end of the track. If a vehicle reaches the beginning of the crossing area (its own traffic light) and its semaphore light is green, it turns the other vehicle's semaphore lights to red. It then proceeds, moving in the usual way. When it reaches the end of its crossing area, it turns the lights on the other vehicle's track back to green. If the semaphore light on a vehicle's track is red when it arrives at the crossing, it stops and waits until the light turns green.

Simulation continues until the user clicks *Stop* and may be restarted by clicking *Go* again.

<figure><img src="image_250.png" /><figcaption>Figure 13.8. User interface for train simulation.</figcaption></figure>

## Scenarios

### Scenario 1: Starting or restarting simulation

1. User clicks Go.

2. Program starts moving both vehicles from their current position. Motion is 'simultaneous'.

### Scenario 2: Stopping simulation

1. User clicks Stop.

2. Program stops both vehicles in their current position.

### Scenario 3: Changing speed

1. User moves the speed control slider to a new position.

2. Program changes speed setting for both vehicles and if the simulation is running, the new setting immediately takes effect.

### Scenario 4: Train reaches the start of the crossing and its semaphore light is green

1. Program changes light on the truck side to red.

2. Program advances train to next position.
### Scenario 5: Train reaches the start of the crossing and its semaphore light is red

1. *Program* stops the train at the light.

2. *Program* keeps advancing the truck until it reaches the end of the crossing zone.

3. *Program* turns train lights to green.

4. *Program* moves train and turns truck lights to red.

### Scenario 6: Train reaches end of crossing

1. *Program* changes truck lights to green.

### Scenario 7: Train reaches end of track

1. *Program* reverses direction of train motion.

Truck scenarios are analogous to train scenarios.

## Preliminary Design

The domain objects include the train and the truck and we will leave it to the application model to take care of the simulation. It could be argued that the simulation object could be a part of the domain model and separate from the application model but we don't expect it to be complex. Finally, a layout view will implement the track layout subview but there is no active controller because the simulation subview does not allow any user interaction. Altogether, we need the following classes:

**Train.** Knows its position and direction of motion, knows how to calculate its next position, check that it reached the end of the track or the beginning or the end of its crossing, and how to turn around (change direction). Knows its simulation parameters such as its length (the length of the rectangle).

**Truck.** Same responsibilities as *Train* but truck-specific.

**TrainSimulation.** Application model. Contains the specification of the window and knows about the view class implementing the simulation subview. Knows global simulation parameters including start and length of train and truck tracks, and the limits of the crossing area (distance from crossing to lights). The *Go* action button is the heart of the simulation, *Stop* suspends simulation.

**LayoutView.** Display tracks, lights, and vehicles.

We will now examine the role of our classes in the previously listed scenarios.

## Scenarios

### Scenario 1: Starting or restarting simulation

1. *User* clicks *Go*.

2. *Program* starts moving both vehicles from their current position. Motion is 'simultaneous'.

This scenario is one of the essential components of the problem and before we start expanding it, we must have a conceptual model of the simulation. The problem invites the use of processes, one for each vehicle; after all, each vehicle is autonomous. The requirement that vehicle motion be 'simultaneous' means that the two processes must systematically take turns.

An important part of the motion of vehicles in our problem is the crossing and we must thus consider it in this context. The behavior of the crossing suggests the use of a semaphore shared by the two vehicles. When a vehicle reaches the crossing zone, it tells the semaphore about its desire to move and if the semaphore is ready (has been signaled), it allows the vehicle to proceed and change lights.

Another question is the relation of the train and truck *processes* to the *Train* and *Truck* *classes*. Our train process will use the *Train* domain object to perform all the calculations and trigger the display, and the
truck process will use the Truck class in a similar way. Process objects thus use domain objects but are separate from them. With this background, we can now expand Scenario 1.

### Scenario 1: Starting or restarting simulation

1. User clicks Go.

2. TrainSimulation creates a train process and a truck process.

3. TrainSimulation starts the train process and the vehicle processes automatically start taking turns executing single step motion while respecting the semaphore at the crossing.

### Scenario 2: Stopping simulation

1. User clicks Stop.

2. TrainSimulation terminates the two processes.

The immediate question is whether clicking Stop can actually freeze simulation. The point is that while the application is executing the simulation loop, other methods can only be accessed from the loop. Under these conditions, will the application be able to respond to the Stop button? The answer is that the Stop button will still be effective because our processes run at UserSchedulingPriority (the priority of their parent process - our simulation), whereas keyboard and mouse events are managed by a process running at LowIOPriority whose priority is higher. Clicking Stop will thus take precedence over simulation and activate the action method of the Stop button even though the train and the truck processes are running<fnref n="3" />.

### Scenario 3: Changing speed

1. User moves the slider to a new position.

2. Program changes speed setting and if the simulation is running, the new setting immediately takes effect.

To control speed, we will end each simulation step with a Delay whose duration will be derived from the setting of the slider. For slow speed, the delay will be long, for fast speed, the delay will be short. Since slider control runs under LowIOPriority, the effect of dragging the slider marker to a new position immediately changes its value aspect. To control speed during simulation we must thus obtain the current slide position in each simulation step and use it to control the delay. (We will explain the simple rules for using a slider in the Implementation section.) We can now expand the scenario as follows:

### Scenario 3: Changing speed

1. User moves the slider to a new position.

2. The active vehicle Process reads the new slider setting and uses it to create and schedule a Delay of appropriate duration.

### Scenario 4: Train reaches the start of the crossing area and its semaphore lights are green

1. The Train object checks its current position and finds that it has just reached the start of the crossing zone. This test involves checking the current direction of motion, the position of the crossing, its width, and the length of the vehicle.

2. The Train object sends a wait message to the semaphore.

3. Since the semaphore is presumably *not* blocked, the train process is not suspended, wait turns the semaphore lights on the truck track to red, and the process advances the train to the next position.

### Scenario 5: Train reaches the start of the crossing area and its semaphore lights are red

1. The Train object checks the current train position and finds that it has just reached the start of the crossing zone.

2. The Train object sends wait to the semaphore.

3. Since the semaphore is *is* blocked, it suspends the train Process.

<footnote n="3">³ We now realize that we should have asked the question of how the user interface can take over from the application a long time ago - this principle is the essence of all interactive applications.</footnote>
4. The truck which is moving through the crossing eventually leaves the crossing area, sends **signal** to the semaphore (Scenario 6), and suspends itself temporarily (see the alternation of train and truck processes in Scenario 1). The blocked train **Process** resumes, turns the semaphore lights on the truck track to red, and advances the train to the next position.

**Scenario 6: Train reaches the end of the crossing area**

1. The Train object recognizes the ‘end-of-crossing’ position, changes truck lights to green, and sends **signal** to the semaphore, unblocking a truck possibly waiting for green light.

**Scenario 7: Train reaches end of track**

1. The Train object recognizes the ‘end-of-track’ position. It changes its direction state which controls whether the next position is calculated by incrementing or decrementing the current position, and makes a move.

*Preliminary object diagram*

Except for the class hierarchy and possible abstract classes, we now have a good understanding of the mutual relationships of our classes. The corresponding diagram is in Figure 13.9.

<figure><img src="image_251.png" /><figcaption>Figure 13.9. Preliminary Object Model of train simulation.</figcaption></figure>

**Design refinement**

*Class hierarchy*

Domain objects *Train* and *Truck* have a lot in common: Both use the same principle to move, and both must know how to check crossing boundaries and end of track, and how to turn. We will thus create an abstract class called *Vehicle* implementing as much of the shared functionality as possible, and delegate vehicle-specific details to *Train* and *Truck*. *TrainSimulation* is our application model and a subclass of *ApplicationModel*, *LayoutView* is a subclass of *View*. The resulting class hierarchy is as in Figure 13.10.
<figure><img src="image_252.png" /><figcaption>Figure 13.10. Class Hierarchy Diagram with classes to be designed shown as heavy rectangles.</figcaption></figure>

Refined class descriptions

We now understand enough of the behaviors of our classes to be able to start refining class descriptions:

Domain classes

**Vehicle.** Abstract superclass of domain classes *Train* and *Truck*. Factors out shared behavior and knowledge such as vehicle position and direction of motion, calculation of the next position, testing for the end of the track or crossing, and turning.

Superclass: Object

Components: limit (distance from crossing to lights), position (distance of vehicle from start of track), direction (#incrementing or #decrementing position), vehicleLength (pixels), model (reference to the TrainSimulation object running the simulation).

Behaviors

Collaborators

* initialization - position and direction; method initialize

* moving - trigger display in new position, activate *Delay*

TrainSimulation,
Train, Truck

* move - check position with respect to crossing, send wait or signal to semaphore and redisplay lights if necessary, check for end of track and turn if necessary, move one step

* `moveOneStep` - calculate new position using direction information, display (ask vehicle for its display object), delay

Train, Truck

* semaphores: `aColorValue` - ask model to change color of semaphore lights

TrainSimulation

* testing - check whether entering or leaving crossing zone (*enteringSemaphore*, *leavingSemaphore*), check for end of track (*endOfTrack*)

Vehicle-specific parts of these behaviors are left as subclass responsibilities for *Train* and *Truck*.

One note is in order about our design: The name of the variable model suggest dependency and a possible better implementation. We leave this question as an exercise.

**Train.** Concrete train vehicle. Implements train specific responsibilities and specializes those inherited from *Vehicle*.

Superclass: Vehicle

Components: *Inherited components*

Behaviors

Collaborators

* initialization - execute inherited *Vehicle* initialization and initialize train specific track and vehicle parameters - method `initialize`
**Truck.** Same as Train but truck-specific.

## Application model

Before we can write the description of *TrainSimulation*, we must refine our understanding of its critical task - the creation and nature of the processes that represent the train and the truck. We have already decided that each of these processes will be implemented as an infinite loop that asks the corresponding domain object (a *Train* or a *Truck*) to make a move (including redisplay), and then somehow yields to the other process. Our first hunch is to do something like

```
trainProcess := [ [train move.
    trainProcess suspend.
    truckProcess resume] repeat] fork.
truckProcess := [ [truck move.
    truckProcess trainProcess suspend.
    trainProcess resume] repeat] fork
```

In this implementation, a vehicle process performs a one-step move, resumes the other process (currently suspended), and suspends itself, activating the resumed process. Unfortunately, this approach will not work: Assume that the truck is in the middle of the crossing zone when the train reaches the semaphore light. Its move operation issues

### semaphore wait

and is placed in the semaphore waiting queue because the light is red. The truck makes one step, suspends itself (see above), and waits for the train to resume it. However, the train is waiting for the truck to turn the semaphore to green – which it can’t do because it cannot move. The train and truck processes become *deadlocked*. To solve this problem, we decide to rely entirely on the semaphore and code the processes as follows:

```
trainProcess := [[train move.
    Processor yield] repeat] fork.
truckProcess := [[truck move.
    Processor yield] repeat] fork
```

To understand how this approach works, assume that none of the vehicles reached the crossing zone yet and that it is the train’s turn to move. The following then happens:

1. The train makes a move and executes

### Processor yield

asking *Processor* to put it at the end of the queue of runable processes (processes that are not suspended or waiting for a semaphore). This leaves the truck at the head of the queue.

2. Since the truck is not waiting on the semaphore, it executes a *move* and asks *Processor* to put it at the end of the queue. This leaves the train at the head of the queue, the operation proceeds as described in Step 1, and so on.

Eventually, one of the vehicles (for example the train) reaches the beginning of the crossing zone. As a part of executing its *move* (see description of *Train* and *Vehicle* above), it sends

### semaphore wait

turns the truck’s light to red, and yields via *Processor*. If the truck is not yet in the crossing zone, it makes its *move*, yields to the train, and so on. If the train reaches the end of the crossing zone before the truck reaches its start, it turns truck’s light to green and everything proceeds as in steps 1 and 2. If, however, the truck reaches the crossing zone while its semaphore is still red, its
## semaphore wait

blocks the truck process. The truck process is now removed from the queue of runable processes and the train is now the only process in that queue. The train makes a move, and yields. But since there is no other process in the queue of runable processes, this does not stop it, it executes another `move`, and yields. If the did not reach the end of the crossing zone, the `yield` again does not change anything and the train makes another `move` (while the truck is still waiting on the semaphore). Eventually, the train reaches the end of the crossing zone, sends

## semaphore signal

and this removes the truck from the semaphore queue and puts it in the queue of runable processes. When the train now executes

## Processor yield

and goes to the end of the queue, it now finds itself behind the truck which makes its `move`, yields, the train makes its `move` and yields, and so on.
With this background, we can now write a description of class `TrainSimulation` as follows:

**TrainSimulation.** Responsible for user interface and simulation.

**Superclass: ApplicationModel**

**Components: delay (Integer - current value of delay between vehicle moves - controlled by slider), layout (View), semaphore (Semaphore), train (Train), truck (Truck), trainTrackLength (Integer), truckTrackLength (Integer), trainTrackStart (Point), truckTrackStart (Point), trainProcess (Process), truckProcess (Integer), speed (Integer), aspect variables.**

### Behaviors

* initialization - initialize instance variables, send signal to semaphore to get it ready to pass the first incoming vehicle; method `initialize`

* displaying - pass requests to display vehicle or change lights to `LayoutView`; methods `display:` and `semaphores:for:` (color and type of vehicle)

* actions - response to action buttons

* go - create the two mutually yielding processes and start one of them

* stop - terminate both processes

### Collaborators

Train, Truck, Vehicle, LayoutView

Train, Truck

At this point, we realize that we should not allow the user to close the window and terminate the application if the processes are running. We thus add a `changeRequest` method that intercepts each attempt to close the window, checks whether there are any running processes (`Boolean variable oKToClose`), and if there are, warns the user and refuses to close the window.

## User Interface

**LayoutView.** Display tracks, lights, and vehicles.

**Superclass: View**

**Components: Parameters of layout geometry obtained from TrainSimulation - cached for greater efficiency.**

### Behaviors

* displaying - view, train and truck in new position, lights

* `displayOn:` - the standard required display method

* `display Train` or `Truck` upon direct request from domain objects via `TrainSimulation`; method `display:` with type of vehicle

* `display` of semaphore lights; methods `displayTrainSemaphores:` and

### Collaborators

LayoutView
`displayTruckSemaphores:` with a `ColorValue` argument

* controller specification - specify `NoController` in method `defaultControlleClassr`

## Object Model Diagram

The only difference from the preliminary diagram is that we now know that `Train` and `Truck` have superclass `Vehicle` (Figure 13.11).

<figure><img src="image_253.png" /><figcaption>Figure 13.11. Final Object Model of train simulation.</figcaption></figure>

## Implementation

We will now show implementation details of several selected methods class by class.

### Class Vehicle

`Initialization` is shared between the abstract vehicle and the concrete subclass. The shared part is as follows:

#### initialize

“Initialize parameters shared by `Train` and `Truck`. Leave the rest to `Train` and `Truck` classes.”

`position := 0.`

`direction := #incrementing`

Most of the `move` operation is implemented in this abstract class and we split it into methods that perform tests for crossing limits and end of track, and a method that performs a one-step `move`. The principle is
### move

"Test for crossing limit and end of track, make one-step move if appropriate."

"Test whether we are at a crossing limit. If so, message semaphore and make one-step move."

self comingToSemaphore

ifTrue: [model semaphore wait.
self semaphores: ColorValue red].

self leavingSemaphore

ifTrue: [model semaphore signal.
self semaphores: ColorValue green].

"Check for end of track and 'turn' if required."

self endOfTrack ifTrue: [self turn]

"Make a one-step move."

self moveOneStep.

but to speed up execution, we perform one-step moves on each successful test and exit to avoid further tests:

### move

"Test for crossing limit and end of track, make one-step move if appropriate."

"Test whether we are at a crossing limit. If so, inform semaphore and make one-step move."

self comingToSemaphore

ifTrue: [model semaphore wait.
self semaphores: ColorValue red.
^self moveOneStep].

self leavingSemaphore

ifTrue: [model semaphore signal.
self semaphores: ColorValue green.
^self moveOneStep].

"Check for end of track and 'turn' if required."

self endOfTrack ifTrue: [self turn]

"No special situation - make a one-step move."

self moveOneStep.

The move itself is performed by moveOneStep as follows:

### moveOneStep

"Move one step in the current direction and pause briefly to provide the required control of speed."

direction == #decrementing

ifTrue: [position := position - 1]

ifFalse: [position := position + 1].

model display: self symbol.

(Delay forMilliseconds: model delay) wait

We delegate display to TrainSimulation by asking it to display a vehicle identified by its symbol.

The symbol method is defined in Train as

### symbol

^#train

and similarly in Truck. The turn that occurs at the end of the track is implemented as follows:

### turn

"Reached end of track, turn."

direction == #incrementing

ifTrue: [direction := #decrementing]

ifFalse: [direction := #incrementing]

and the test methods are

comingToSemaphore
"Return true if I have just reached the start of the crossing zone."

```pascal
^(direction == #incrementing and: [position = (crossing - limit - vehicleLength)])
```

or: [direction == #decrementing and: [position = (crossing + limit)]]

and

### endOfTrack

"Return true if this is the end of the track. Remember that vehicle might have turned."

```pascal
^(direction = #decrementing and: [position = 0])
```

or: [direction == #incrementing and: [position = (trackLength - vehicleLength)]]

The method that triggers redisplay of semaphore lights uses the same principle as `moveOneStep` and asks `TrainSimulation` to display semaphores controlled by the vehicle in the specified color:

### semaphores: aColorValue

"Ask `TrainSimulation` to display semaphores controlled by vehicle identified by its symbol."

model semaphores: aColorValue for: self symbol

As you can see, `Vehicle` implements most of the functionality of its concrete subclasses.

### Class Train

Initialization performs inherited initialization and sets initial values of train-specific parameters:

### initialize

"Initialize vehicle parameters via super and `Train` specific values."

super initialize.

limit := model trainCrossingLimit.

vehicleLength := 20.

crossing := model truckTrackStart x - model trainTrackStart x.

trackLength := model trainTrackLength

The rest are accessing methods including `symbol` (listed above) and

### crossing

"Calculate distance from start of track to intersection."

```pascal
^model truckTrackStart x - model trainTrackStart x
```

### Class Truck

All methods in class `Truck` are truck-specific variations on `Train` methods.

### Class TrainSimulation

Initialization of `TrainSimulation` consists of setting the parameters of the layout, creating a `Train` and a `Truck`, creating a `Semaphore` and signaling it to get ready for the first train, creating and assigning the layout view, assigning initial simulation speed, and setting the variable that reflects the state of the train and truck processes and determines whether the application can be closed or not.

### initialize

trainTrackLength := 160.

truckTrackLength := 130.

trainTrackStart := 15 @ 80.

truckTrackStart := 90 @ 25.

trainCrossingLimit := 25.

truckCrossingLimit := 20.

truck := Truck newOnModel: self.

train := Train newOnModel: self.

semaphore := Semaphore new.
semaphore signal.
`layout := LayoutView new.`
`layout model: self.`
`speed := 25 asValue.` "Aspect variable associated with the speed slider."
`oKToClose := true`

The essential go action method is designed along the lines decided during Design Refinement:

**go**

"User clicked Go. Clear oKToClose to indicate that we have active processes and cannot close the application, create and schedule the train and truck processes."

OKToClose := false.
trainProcess := [ [train move.
Processor yield] repeat] fork.
truckProcess := [ [truck move.
Processor yield] repeat] fork

The *Stop* button terminates the two processes and sets `oKToClose` to indicate that the application can now be closed:

**stop**

"Terminate train and truck processes, set OKToClose to True to indicate that the application may close."
trainProcess terminate.
trainProcess terminate.
OKToClose := true

Attempts to close the window send `changeRequest`. The method returns `true` when it is OK to close the application (when the vehicle processes are not running), and `false` otherwise:

**changeRequest**

"Decide whether it is OK to close the application."
`oKToClose`
`ifFalse: [Dialog warn: Click Stop to stop running processes before closing.]`
`^oKToClose`

Class `TrainSimulation` also defines a group of methods responsible for display. In essence, they pass requests for display coming from vehicles to the layout view. They include

**display: aSymbol**

"Pass request to display a Train or a Truck to layout view."
`layout display: aSymbol`

and

**semaphores: aColorValue for: aSymbol**

"Pass request to display lights to the layout view."
`layout displaySemaphoresFor: aSymbol inColor: aColorValue`

Finally, the implementation of the slider. The slider is a simple widget whose value holder holds a number corresponding to the position of the marker of the slider bar. When the position of the marker changes, the value changes and when the value is changed programmatically, the marker moves to the corresponding position. All we need to do to implement a slider is to specify the start and the end values corresponding to the left and right endpoints using the Properties Tool, define the aspect variable (we called it *speed*), and define its initial value which we did in the initialization method above. When assigning the endpoint values, don't forget that the leftmost slider position represents the *slowest* speed which corresponds to the *largest* value of delay. The value of *speed* is used to calculate the delay

**delay**
## ^speed value

and both vehicles use this method to determine the length of their delay.

### Class LayoutView

In addition to specifying `NoController` as the default controller class, the major responsibility of `LayoutView` is displaying. This responsibility can be divided into two tasks: Response to damage notification, and request for redisplay initiated from the domain model by vehicle motion. Response to damage notification is, as always, implemented by a `displayOn:` aGraphicsContext method. It draws the layout, the train and the truck, and the lights, and its definition as follows:

### displayOn: aGraphicsContext

"Display tracks, vehicles, semaphores, and labels."

aGraphicsContext

displayLineFrom: self trainStart to: self trainEnd;

displayLineFrom: self truckStart to: self truckEnd;

displayString: 'train' at: self trainStart x + 4 @ self trainStart y - 6;

displayString: 'truck' at: self truckStart x + 4 @ self truckStart y + 6.

self displayTrainSemaphoresOn: aGraphicsContext withColor: model trainSemaphoreColor;

displayTruckSemaphoresOn: aGraphicsContext withColor: model truckSemaphoreColor;

displayTruck;

displayTrain

The display a vehicle is implemented as follows:

### displayTrain

self graphicsContext displayRectangle: (0 @ 0 corner: self trainLength @ 5)

at: self trainStart + (self trainPosition @ -2)

and the methods displaying the semaphores are explained below.

Vehicle requests for redrawing arrive from `TrainSimulation` but are triggered by the vehicles. They require redrawing of vehicles and semaphore lights. To redraw the vehicles, we can use one of two approaches. One is based on the view that we want to move a geometric object (the train rectangle) from one position to another. This high level approach deals with redrawing as a simple form of animation and to implement it, we can use methods `follow:while:on:` or `moveTo:on:restoring:` defined in class `VisualComponent`. We leave this approach as an exercise and use the low level perspective explained next.

Instead of thinking about moving a visual object, we can take the view that moving a rectangle horizontally or vertically by one pixel which requires only erasing the trailing edge and drawing a line in front of the leading edge. If the current position of the trailing edge corresponds to the crossing, we don't erase it because it also represents the track. To 'erase' the back of the vehicle, we redraw the corresponding line in white. After erasing the end of the rectangle, we must also redraw the piece of track that we erased in the process. This brings up the important fact that to display a point in `VisualWorks`, you must draw a one-pixel long line. The whole definition, as implemented for the train, is as follows:

### displayTrainOn: aGraphicsContext

"Calculate the position of the trailing and leading edges and redraw them to simulate motion."

| length direction position oldEnd newStart |

length := model trainLength.

direction := model trainDirection.

position := model trainPosition.

direction == #incrementing

ifTrue: [oldEnd := position - 1.

newStart := oldEnd + length]

ifFalse: [oldEnd := position + length + 1.

newStart := position].

"Erase end of train except when on crossing."

oldEnd = (crossing x - trainStart x) ifFalse:
``` FLASH
[aGraphicsContext paint: ColorValue white;
  displayLineFrom: self trainStart + (oldEnd @ -2)
    to: self trainStart + (oldEnd @ 2)].
aGraphicsContext paint: ColorValue black;
  "Draw leading edge of train."
  displayLineFrom: self trainStart + (newStart @ -2)
    to: self trainStart + (newStart @ 2);
  "Redraw erased part of track."
  displayLineFrom: self trainStart + (oldEnd @ 0)
    to: self trainStart + (position @ 0)
```

We had to correct the arithmetic expressions in this method several times to get the display to work properly, mainly because we did not bracket the binary messages `+` and `@` and they executed differently from our expectations. This is a frequent and annoying mistake.

To display semaphore lights, we wrote separate methods for the train and for the truck. They calculate some essential points and display a colored rectangle along the track on each side of the crossing:

### displayTrainSemaphoresWithColor: aColorValue

"Display train semaphore lights as rectangles of specified color."

``` FLASH
| limit rectangle |
limit := self trainCrossingLimit.
rectangle := 0 @ 0 corner: 4 @ 4.
self graphicsContext
  paint: aColorValue;
  displayRectangle: rectangle at: self crossing + (limit negated @ 3);
  displayRectangle: rectangle at: self crossing + (limit - 4 @ -6)
```

The application now works and it only remains to improve the implementation of a few awkward methods. As an example, the last method could be improved by defining a lightRectangle variable and calculating the coordinates from the rectangle instead of using fixed numeric values. We could then change the size of the lights without having to change the calculation of the position of the rectangle. We leave this as an exercise. A non-trivial improvement of the user interface is the subject of the next section.

### Main lessons learned:

* Class Semaphore makes it possible to synchronize processes.

* The essence of the operation of a Semaphore are messages signal and wait.

* If a Process sends wait to a Semaphore and the difference between the number of signal and wait messages received by the Semaphore is not at least 1, the Process is suspended until the condition is satisfied.

* A deadlock occurs when several processes wait for one another and cannot proceed.

* Processor yield moves the currently active process to the end of its queue of runable processes.

### Exercises

1. Complete and clean up the implementation as suggested. One of the questions to resolve is whether the model instance variable of Vehicle implies that TrainSimulation should be a Model of Vehicle and if so, how this part of the application should be reimplemented.

2. Replace train and truck rectangles with arrows and reverse their direction at the end of the track.

3. Modify Train and Truck so that they know how to display themselves.

4. Change the simulation program so that the train and the truck each have their own speed controlled by its own slider.

5. Implement vehicle redrawing using VisualComponent's animation protocol. In our case, the code is not simpler but its implementation is an interesting introduction to animation.

6. In our design, the train and the truck can never collide or become deadlocked. Real life crossing are not, unfortunately, so perfect because a driver can overlook a traffic light and a traffic light may misfunction. Modify our design so that the user can set a <0,1> probability that a failure occurs.
7. Add an animation of a crash of the two vehicles.

8. Extend the simulation program to several parallel train and truck tracks with a single shared crossing. Each track has its own vehicle running at its own speed and each vehicle's speed is controlled independently. Speed is controlled by a single slider and a set of radio buttons, one for each vehicle.

9. Simulation of events occurring in parallel, such as our train simulation, are common. Define a simulation framework in which such simulations could be implemented as easily as possible and test it on our example.

10. Does process priority have any effect on its behavior with respect to Semaphore?

11. What would happen if we removed a Semaphore with a queue of waiting processes?

12. What happens when you close the simulation window without terminating the two vehicle processes?

13. Study class Semaphore and provide detailed answers to the following questions:

a. What happens to a process when it sends wait and the Semaphore is ready to let it run?

b. What happens if the Semaphore is not ready to let the process run?

c. What happens to the process that sends signal to a Semaphore?

d. What happens when several wait messages are sent to the same Semaphore?

e. What happens when several signal messages are sent to the same Semaphore?

### 13.4 Making train simulation layout customizable

We now have a working train simulation but we would like to make it more flexible by allowing the user to customize it. In particular, we would like to make it possible to stop simulation and grab the line representing a track and move it to a new position without changing its length, and grab an endpoint of a track and move it to change the track's length.

In both cases, horizontal lines will remain horizontal and vertical lines will remain vertical. Both actions will use a special cursor displayed only when the mouse moves over a track or a track endpoint (Figure 13.12).

<figure><img src="image_254.png" /><figcaption>Figure 13.12.Special cursor shapes for dragging tracks and changing their length.</figcaption></figure>

The details of the desired behaviors are described in the following scenarios:

**Scenario 1: User moves the cursor over the layout subview**

1. User moves cursor into the hot area around the right end of the horizontal track.

2. Program changes cursor to the shape shown on the left in Figure 13.12.

3. User continues moving the cursor until it leaves the hot area.

4. Program changes cursor to normal shape.

5. User moves mouse cursor into the hot area surrounding the inner portion of the horizontal track.

6. Program changes cursor to the shape shown in the center of Figure 13.12.

7. User moves mouse cursor into the hot area surrounding the inner portion of the vertical track.

8. Program maintains cursor shape unchanged.

9. User moves mouse cursor into the hot area around the upper end of the vertical track.

10. Program changes cursor to the shape shown on the right of Figure 13.12.

**Scenario 2: User drags the horizontal track to a new position**

1. User moves mouse cursor into the hot area surrounding the inner portion of the horizontal track.

2. Program changes cursor to the shape shown in the center of Figure 13.12.

3. User presses the <operate> button and moves the mouse while holding the button down.

4. Program drags the track maintaining its length and horizontal orientation, and follows the cursor without changing its shape. The program also moves semaphore lights and the vehicle and enforces constraints such as keeping the track within the subview.
5. *User* releases the mouse button.

6. *Program* stops dragging the track.

**Scenario 3: User drags the left end of the horizontal track to a new position**

1. *User* moves the cursor into the hot area surrounding the left end of the horizontal track.

2. *Program* changes cursor to the shape shown on the left of Figure 13.12.

3. *User* presses the <operate> button and moves the mouse while holding the button down.

4. *Program* drags the end of the track, restricting motion to horizontal displacement and following the cursor without changing its shape. The program also moves the vehicle if necessary and enforces constraints such as limiting the track to the subview. The rest of the track is not affected.

5. *User* releases the mouse button.

6. *Program* stops dragging the endpoint of the track.

The scenarios show that the details are not as trivial as we may have thought - and this is one of the reasons why scenarios are useful. Among other things, the definitive solution will require a specification of the details of the constraints on track motion. We suspect that the details will not be trivial and we will thus ignore them in the first iteration (leaving them as an exercise:), concentrating on unconstrained motion and leaving the details for another iteration.

**Preliminary Design**

The major modification of the existing implementation is that we must replace the current NoController with a controller that can handle the desired interactions. We will call this controller LayoutController and its essential responsibilities are as follows:

1. Control the shape of the cursor.

2. As the mouse moves in the dragging mode of operation, keep redrawing the appropriate part of the layout without affecting the rest. This task consist of

a. continuously redrawing a *track*,

b. redrawing the *vehicle* on the track being moved,

c. redrawing *semaphore lights* of both tracks, and

d. updating instance variables describing the tracks.

To get a better understanding of the details, let's refine our scenarios into class level conversations.

**Scenario 1: User moves the cursor over the layout subview**

1. *User* moves cursor into the hot area around the right end of the horizontal track.

2. *LayoutController* tracks mouse position and changes cursor to the appropriate shape.

3. *User* continues moving the cursor until it leaves the hot area.

4. *LayoutController* tracks mouse position and changes cursor to normal shape.

5. etc.

**Scenario 2: User drags the horizontal track to a new position**

1. *User* moves mouse cursor into the hot area surrounding the inner portion of the horizontal track.

2. *LayoutController* tracks mouse position and changes cursor to the appropriate shape.

3. *User* presses the <operate> button and moves the mouse around while holding the red button down.

4. *LayoutController* detects mouse button press and starts converting mouse movement events into the following actions:

a. Calculate mouse displacement with respect to the previous position.

b. Obtain current track position and other relevant information from *TrainSimulation* and combine it with mouse displacement to calculate new parameters.

c. Send new layout parameters to *TrainSimulation*.

d. Calculate rectangles that must be redrawn and ask *LayoutView* to invalidate them immediately.

5. *User* releases the mouse button.

6. *LayoutController* senses mouse button release and changes its mode of operation to 'no dragging.'
### Scenario 3: User drags the left end of the horizontal track to a new position

Very similar to Scenario 2; only the details of parameters and calculations are different.

The preliminary Object Diagram updated from previous section is shown in Figure 13.13.

<figure><img src="image_255.png" /><figcaption>Figure 13.13. Preliminary Object Model of customizable train simulation.</figcaption></figure>

## Design Refinement

### Class Descriptions

The existing classes don't acquire any new responsibilities but this does not necessarily mean that we don't have to change them. In our existing implementation, we cached layout parameters in the view and initialized them once and for all; this would not work any more. Those cached layout parameters that affect the display and the simulation now have to be accessed via accessing methods, and the code that uses them must change accordingly. We are getting a lesson in the usefulness of using accessing methods rather than accessing variables directly.

Since the essential responsibilities of existing classes don't change, we will not rewrite their descriptions and limit ourselves to the controller class.

### LayoutController
Responds to mouse events by displaying appropriate cursor shapes and performing drag operations on tracks and their end points. Does not have an `<operate>` menu.

#### Superclass: Controller

Components: endArea (Rectangle) holds the shape of the hot area for end points, isDragging (Boolean) keeps track of current mode of operation, crossArrowsCursor (Cursor), horizontalArrowsCursor (Cursor), verticalArrowsCursor (Cursor), tracking (Symbol such as #horizontalTrack or #topVerticalEnd) - keeps track of the kind of area under the cursor, newCursorPosition (Point) - current position, oldCursorPosition(Point) - cursor position before last mouse move event

### Behaviors

### Collaborators

* initialization - initialize cursors and define end point area

* event handling - intercept cursor movement and red button events

* cursor display - display area-dependent shape

* dragging - drag tracks or their endpoints in response to mouse move event LayoutView
* accessing – obtain layout parameters from domain model TrainSimulation

The Hierarchy Diagram updated from the previous section is shown in Figure 13.14; the Object Model does not change.

<figure><img src="image_256.png" /><figcaption>Figure 13.14. Class Hierarchy Diagram with classes to be designed shown as heavy rectangles.</figcaption></figure>

Implementation

We will now show some of the code of LayoutController. Note again that this is a first iteration and
that we are leaving many details such as dragging constraints and optimized invalidation for the next
iteration.

Class LayoutController

Initialization

initialize

"Initialize mode of operation, cursors, and hot area rectangle for track end points."

isDragging := false.

horizontalArrowsCursor := Cursor

image: self class horizontalArrows asImage

mask: self class horizontalArrowsMask asImage

hotSpot: 8 @ 8

name: 'horizontal arrows'.

verticalArrowsCursor := Cursor

image: self class verticalArrows asImage

mask: self class verticalArrowsMask asImage

hotSpot: 8 @ 8

name: 'horizontal arrows'.

crossArrowsCursor := Cursor

image: self class crossArrows asImage

mask: self class crossArrowsMask asImage

hotSpot: 8 @ 8

name: 'horizontal arrows'.

endArea := -4 @ -3 corner: 4 @ 3

As in the Chess program, we used Image Editor to create cursors and their masks.

Mouse events

These methods redefine some of the events protocol inherited from class Controller.

mouseMovedEvent: event

"The mouse has moved. Respond according to current mode of operation."
| areaCursor |

isDragging

ifTrue: "Perform method corresponding to parameter being tracked."

[newCursorPosition := self sensor cursorPointFor: event.

self perform: tracking. "Execute message appropriate for parameter."

oldCursorPosition := newCursorPosition]

ifFalse: "We could have entered a new area. Check and update cursor."

[areaCursor := self hotSpotAreaCursorFor: (self sensor cursorPointFor: event).

Cursor currentCursor: areaCursor]

The auxiliary method hotSpotAreaCursorFor: selects cursor shape according to cursor location:

**hotSpotAreaCursorFor: aPoint**

"Determine which parameter is being tracked from the location of the cursor. Return appropriate cursor."

self getTrackingParameterFor: aPoint.

^self selectCursor

where

**getTrackingParameterFor: aPoint**

"Return symbol representing tracking parameter."

(self bottomVerticalTrackAreaEndpoint containsPoint: aPoint)

ifTrue: [^tracking := #bottomVerticalEnd].

(self topVerticalTrackAreaEndpoint containsPoint: aPoint)

ifTrue: [^tracking := #topVerticalEnd].

(self verticalTrackArea containsPoint: aPoint)

ifTrue: [^tracking := #verticalTrack].

(self leftHorizontalTrackAreaEndpoint containsPoint: aPoint)

ifTrue: [^tracking := #leftHorizontalEnd].

(self rightHorizontalTrackAreaEndpoint containsPoint: aPoint)

ifTrue: [^tracking := #rightHorizontalEnd].

(self horizontalTrackArea containsPoint: aPoint)

ifTrue: [^tracking := #horizontalTrack].

^tracking := nil

determines which parameter we are tracking, and

**selectCursor**

(#(#leftHorizontalEnd #rightHorizontalEnd) includes: tracking)

ifTrue: [^horizontalArrowsCursor].

(#(#bottomVerticalEnd #topVerticalEnd) includes: tracking)

ifTrue: [^verticalArrowsCursor].

(#(#horizontalTrack #verticalTrack) includes: tracking)

ifTrue: [^crossArrowsCursor].

^Cursor normal

finds the appropriate cursor. We think that the two last methods could be implemented more elegantly and leave this task as an exercise. Individual hot areas are returned by auxiliary methods such as

**bottomVerticalTrackAreaEndpoint**

"Calculate hot area for the bottom of the current vertical track by shifting the pre-initialized endArea rectangle by a value equal to the end point of the vertical track."

^endArea translatedBy: (self truckTrack at: 2)

where truckTrack is an array of track end points calculated dynamically from current track coordinates as follows:

**truckTrack**

"Return top and bottom end points of vertical track."

| start |
```
^Array with: (start := model truckTrackStart)
  with: start + (0 @ model truckTrackLength)
```

Returning now to red button mouse events, the following method is responsible for starting dragging:

### redButtonPressedEvent: event

"User pressed red button. If we are 'tracking' (cursor in a hot area), change mode to 'dragging' and initialize oldCursorPoint for further updates of layout during dragging. The rest is done by mouseMovedEvent:."

```
tracking isNil "Are we 'tracking' a parameter - is cursor in a hot area?"
```

```
isFalse: [isDragging := true.
  oldCursorPosition := self sensor cursorPointFor: event]
```

and once this method is executed, dragging occurs via the mouseMovedEvent: defined above. When the user releases the button, the following event method returns operation to the non-dragging mode:

### redButtonReleasedEvent: event

"User released red button, switch mode to non-dragging."

```
isDragging := false
```

### Dragging

Dragging operations are performed only in the dragging mode (see mouseMovedEvent: above) and are based on invalidation. As an example, dragging of the entire horizontal track is implemented as follows:

### horizontalTrack

"User moved the cursor in the dragging mode. Drag the horizontal track to reflect the new cursor position."

```
| shift track trackStart trackLength |
```

"Update affected track parameters in the model - TrainSimulation."

```
shift := newCursorPosition - oldCursorPosition.
```

```
trackStart := model trainTrackStart + shift.
```

```
model trainTrackStart: trackStart.
```

```
trackLength := model trainTrackLength.
```

"Calculate the end points of the track for the view."

```
track := Array with: trackStart with: trackStart + (trackLength @ 0).
```

"Ask LayoutView to invalidate the area around the track immediately."

```
view invalidateRectangle: (self horizontalTrackInvalidationAreaForTrack: track)
```

```
repairNow: true
```

We use horizontalTrackInvalidationAreaForTrack: to calculate the rectangle to be invalidated by LayoutView. Since the current iteration is just a proof of concept, we define the area simply as the rectangle given by the old and the new cursor position, the track, and 30 pixels around on all sides. This area includes all semaphore lights, the vehicle, and the text. It is, of course, unnecessarily large since we only need to redraw the track itself, the vehicle, the lights, and the text, and the combination of these rectangles is only a fraction of the rectangle that we are invalidating. We leave an optimized implementation as an exercise.

### horizontalTrackInvalidationAreaForTrack: anArray

"This is the area to redraw. It is too generous but it works. Optimization is left until next iteration."

```
^Rectangle origin: (anArray at: 1) + (-30 @ -30)
```

```
corner: (anArray at: 2) + (30 @ 30)
```

As another example of dragging, the left end of the horizontal track is dragged as follows:

### leftHorizontalEnd

"Drag the left end point of the horizontal track. Position of vehicle is not changed because it is relative to the start of the track."

```
| shift trackStart trackLength track |
```

"Recalculate affected TrainSimulation parameters."

```
shift := newCursorPosition x - oldCursorPosition x.
```

```
trackStart := model trainTrackStart + (shift @ 0).
```
trackLength := model trainTrackLength - shift.

model trainTrackStart: trackStart.

model trainTrackLength: trackLength.

"Calculate both end points of track."

track := Array with: trackStart with: trackStart + (trackLength @ 0).

"Ask view to redraw the affected part."

view invalidateRectangle: (self horizontalTrackInvalidationAreaForTrack: track)

repairNow: true

The method for dragging the right end is slightly simpler because it affects only the length and not the start of the track:

### rightHorizontalEnd

"Drag the right end point of the horizontal track. Position of vehicle is not changed because it is relative to the start of the track."

| shift trackStart trackLength track |

"Recalculate affected TrainSimulation parameters."

shift := newCursorPosition x - oldCursorPosition x. "Movement is restricted to horizontal."

trackStart := model trainTrackStart.

trackLength := model trainTrackLength + shift.

model trainTrackLength: trackLength.

"Calculate both end points of track."

track := Array with: trackStart with: trackStart + (trackLength @ 0).

"Ask view to redraw the affected part."

view invalidateRectangle: (self horizontalTrackInvalidationAreaForTrack: track)

repairNow: true

The two methods for dragging track end points are almost identical and it would be better if we factored out the shared behavior. This would make the code more readable and guarantee that if we make changes, they will affect both methods identically. This stylistic improvement is left for the next iteration.

The remaining dragging methods follow the same pattern and the only other required code is simple accessing methods.

### Main lessons learned:

* To simulate dragging of geometric objects, monitor the mouse-moved event and repeatedly erase the object in its original position and redraw it in the new position. Use invalidateRectangle: aRectangle repairNow: true to obtain clean response. An alternative approach is to use the animation protocol in class VisualComponent.

* Using accessing methods instead of accessing instance variable directly makes code easier to modify.

### Exercises

1. Test and explain what happens if we don't specify immediate invalidation.

2. Complete and test the implementation.

3. Optimize the calculation of the invalidation area and implement constraints on moving a track and its end points. Note that if the user moves the cursor fast and the invalidation rectangle is not sufficiently large, invalidation may not be satisfactory.

4. Extend the controller to allow the user to grab a crossing light and move it to a new position. Its corresponding other light should move accordingly.

5. Extend the controller to allow the user to reposition a vehicle on its track. Note that the operation of the crossing imposes constraints on allowed vehicle configurations.

### Conclusion