**BiColorForm foreColor: fColor backColor: bColor**

Creates a 16 x 16 BiColorForm with all 1 bits, fColor as its foreground color and bColor as its background color.

**BiColorForm gray foreColor: fColor backColor: bColor**

Creates a 16 x 16 BiColorForm with alternating 0 and 1 bits, with fColor as its foreground color and bColor as its background color.

This last mask form can be used to mix two colors together. For example,

**(BitBlt destForm: Display sourceForm: nil)**

mask: (BiColorForm gray foreColor: 1 backColor: 4);

extent: 100 @ 100;

copyBits

paints a purple block (mixing blue and red) on the screen. By using this technique, you can expand the number of simultaneously displayable colors from 16 to 120.

## Combination Rules

A *combination rule* is an *Integer* which specifies how the source form bits (after being merged with the mask form) are combined with the destination form bits. Since a bit value of 1 represents white and a bit value of 0 represents black, if you OR white with black, the result is white. If you AND them together, the result is black. To specify what you want to happen in different situations, send a message to class *Form*:

<table><tr><td>Form over</td><td>destination becomes source</td></tr><tr><td>Form orRule</td><td>source OR into destination</td></tr><tr><td>Form andRule</td><td>source AND into destination</td></tr><tr><td>Form under</td><td>source AND into destination</td></tr><tr><td>Form erase</td><td>if source is 1 then destination becomes 0</td></tr><tr><td>Form reverse</td><td>source XOR into destination</td></tr><tr><td>Form orThru</td><td>first erase without specifying mask form, then OR with mask form specified</td></tr></table>

All the examples we've used so far in this chapter have assumed the *combination rule over*. To see the effect of different *combination rules*, evaluate the following expression, which repeatedly displays the number "8" on a background of white with a black band in the middle, each time with a different *combination rule*:
'8' displayAt: 0 @ 0 font: Font fourteenLine.

F := (Form width: 8 height: 14) fromDisplay.

F := (F magnify: (0 @ 0 extent: 8 @ 14) by: 7 @ 7).

Display white: (0 @ 0 extent: 640 @ 104);

black: (0 @ 24 extent: 640 @ 30).

(BitBlt destForm: Display sourceForm: F)

mask: Form lightGray;

combinationRule: Form over;

copyBits;

destOrigin: 70 @ 0;

combinationRule: Form orRule;

copyBits;

destOrigin: 140 @ 0;

combinationRule: Form under;

copyBits;

destOrigin: 210 @ 0;

combinationRule: Form erase;

copyBits;

destOrigin: 280 @ 0;

combinationRule: Form reverse;

copyBits;

destOrigin: 350 @ 0;

combinationRule: Form orThru;

copyBits

Another interesting example is to swap the left half of the display screen with the right half, without using an intermediate Form:

| aRectangle |

aRectangle := Display width // 2 @ 0 corner: Display extent.

(BitBlt destForm: Display sourceForm: Display)

combinationRule: Form reverse;

destRect: aRectangle;

copyBits;

sourceRect: aRectangle;

destOrigin: 0 @ 0;

copyBits;

sourceOrigin: 0 @ 0;

destRect: aRectangle;

copyBits.

Menu message: 'continue'.

Scheduler systemDispatcher redraw
There is an extra dimension in dealing with multi-bitmap forms. With a single-bitmap form, pixels are represented by binary numbers since they can assume only two colors: black (0) and white (1). Thus when the 8 pixels 10101010 in the source are ORd into the 8 pixels 11110000 in the destination, the destination becomes 11111010 (5 black pixels followed by white, black and white). A color screen or form, however, has four bitmaps. Each pixel must be represented by a hexadecimal number. For example, when the 8 pixels E0E0E0E0 are ORd into 33330000 you get F3F3E0E0. Following are some examples dealing with multi-colored forms. They will not work if your machine does not support color.

The `over` rule is used to copy the source rectangle over the destination rectangle regardless of which colors are contained in the destination area. When you create a `BitBlt` instance, it defaults to this rule. For example:

```
(BitBlt destForm: Display sourceForm: Display)
  destOrigin: (Display extent // 2);
  copyBits
```

The `opaque` rule (called `orThru` for black and white) first blanks (zeroes) out the destination area corresponding to the 1 bits of the source area, then combines the source area with the repeated bits of the mask form by logical AND, and finally ORs the result to the destination area. In short, the 0 bits of the source form will appear to be transparent after the move. For example:

```
| aForm |
aForm := (BiColorForm width: 100 height: 100)
        foreColor: 5.
(BitBlt destForm: aForm sourceForm: nil)
  mask: Form black;
  destRect: (20 @ 20 extent: 60 @ 60);
  copyBits;
  destForm: Display;
  sourceForm: aForm;
  mask: nil;
  destRect: Display boundingBox;
  combinationRule: Form opaque;
  copyBits
```

first makes an inner rectangle of 0 bits in `aForm` and then copies `aForm` to the screen. You can see that the portion of 0 bits is transparent. This rule also has many other uses. For example:
| aForm |
(BitBlt
  destForm:
    (aForm := BiColorForm new extent: Display extent)
    sourceForm: Display)
    mask: (BiColorForm color: 5; "extract blue color"
      copyBits.
aForm foreColor: 4. "change foreground color to red"
(BitBlt
  destForm: Display
  sourceForm: aForm)
    combinationRule: Form opaque; "change only 1 bits"
    copyBits

changes blue to red on your screen.

The orRule merges two colors together. For example, combining blue (1) with green (2) yields cyan (3). Try the following:

| aForm |
aForm := (BiColorForm width: 100 height: 100)
  foreColor: 1. "a blue single-bitmap form"
aForm displayAt: 0 @ 0.
aForm foreColor: 2.
(BitBlt destForm: Display sourceForm: aForm)
  combinationRule: Form orRule;
  copyBits

The andRule is usually used to extract a base color. For example:

| aForm |
aForm := (BiColorForm width: Display width height: Display height)
  foreColor: 1. "a blue single-bitmap form"
(BitBlt destForm: Display sourceForm: aForm)
  combinationRule: Form andRule;
  copyBits

causes any pixel in the destination rectangle having an odd numbered color (a trace of blue) to become blue and all other pixels to become black.

The reverseRule, as its name implies, is usually used to reverse the destination color, for example:
(BitBlt destForm: Display sourceForm: nil)
combinationRule: Form reverse;
extent: 100 @ 100;
copyBits

reverses the color in the top left corner of your screen.

## Clipping Rectangle

BitBlt lets you use *clipping rectangles* to restrict your bit transfer to a designated rectangle on the destination Form. For example, each pane within a window sets up a clipping rectangle every time the window is opened or moved or reframed; you never have to worry about writing or drawing beyond that rectangle.

For example, look at the following code, which expands all the black areas in your active window to the left by one pixel. Thus black characters in the window will appear to be bolded, while white ones appear to be thinned:

(BitBlt destForm: Display sourceForm: Display)
clipRect: Scheduler topDispatcher pane frame;
combinationRule: Form andRule;
destOrigin: -1 @ 0;
copyBits

In the example, Scheduler topDispatcher returns the Dispatcher controlling the active window, the pane message returns the pane associated with the Dispatcher, and the frame message asks its receiver pane to answer the rectangle surrounding the active window. (Dispatchers control keyboard and mouse input; they are described in detail in Part 3.) This final rectangle is used as the clipping area. Now evaluate the expression; you'll see that only the content of the active window is affected, even though the entire screen is copied.

You should note two things from the above example. First, this example copies a form to itself, overlapping a large portion of the source and destination rectangles. BitBlt handles this situation properly as if a copy of the source form was made before transferring the bits.

Second, both the source and destination origin can be somewhere outside their forms. The only restriction is that the coordinates of origin points must be small integers. The final rectangular area affected by the BitBlt is the intersection of the destination form, source form (with the source origin aligned with the destination origin), source rectangle, destination rectangle, and clipping rectangle.
## Extension of BitBlt

You've now seen some of BitBlt's raw power. It still, however, needs to be extended to provide an easier user interface for handling tasks like drawing lines or displaying characters. Smalltalk/V therefore provides four BitBlt subclasses: CharacterScanner, Pen, Commander, and Animation.

### CharacterScanner

Class CharacterScanner converts a character's ASCII representation into its graphical, readable form. For example, a message such as:

'Hello' displayAt: 0 @ 0

actually creates a CharacterScanner and tells it to display the String 'Hello' at screen location 0 @ 0.

One of CharacterScanner's major additions to BitBlt is an instance variable containing the font to use when displaying characters. It is an instance of class Font, containing a particular version of the bitmap representation of all characters, which provides information about how to retrieve each character. When a CharacterScanner is told to display a string, it literally uses the ASCII value of each character in the string as an index, then uses its own BitBlt to copy the bitmap pointed to by the index onto the destination form. For example:

CharacterScanner new
initialize: Display boundingBox
font: Font eightLine;
display: 'Hello' at: 0 @ 0;
setFont: Font fourteenLine;
display: 'Hello' at: 40 @ 0

displays Hello at the top left corner of the screen using a font 8 lines high, switches to a font of 14 lines, and displays the same string next to the previous one.

You can use the setFontColor:backColor: message to set the foreground and background colors for the characters to be displayed:

CharacterScanner new
initialize: Display boundingBox
font: SysFont
dest: Display;
setFontColor: (Display compatibleMask color: 1)
backColor: (Display compatibleMask color: 14)
display: 'abc' at: 0 @ 0. "display on screen"
In a monochrome system, the colors numbered 0-7 are mapped into black and colors numbered 8-15 are mapped into white.

## Pen

In the previous chapters, you have seen many examples of turtle graphics. Although you may not have known it, you were using an instance of class Pen. When you tell a pen to draw a line from one place to another, the pen actually uses its BitBlt to copy from its source form to its destination form at each position along the line. For example, to draw from 0 @ 0 to 9 @ 0, the pen copies from its source form to its destination form 10 times, starting at 0 @ 0 and moving right by one pixel for each of the successive copies. The end result looks like a straight horizontal line. When you tell a pen to draw diagonally, it moves to successive positions in as straight a line as possible. The algorithm also makes sure that there are no gaps in the line.

Because a pen uses its source form to draw, changing the shape or tip size of a pen simply means changing its source form. To change the color of a pen, use a mask form with the desired gray tone. Of course, you can also change the combination rule and clipping rectangle to get different effects.

A pen always remembers its current *location*, *direction*, and *downState*. Location tells it where to start for the next movement. Direction allows it to calculate the ending point when the *go* message is used, where only units of movement are specified. The angle is expressed in degrees, with east equal to 0 and north equal to 360. If *downState* is true, the pen draws while it moves; otherwise, it moves without drawing. The mandala drawing method illustrates this:
"Pen method"
mandala: sides diameter: diameter
| vertices radius center angle color |
"initialize local variables"
center := self location.
vertices := Array new: sides.
radius := diameter // 2.
angle := 360 // sides.
"set down state to false in order to use Pen to
locate vertices without drawing"
self direction: 270; up.
1 to: sides do: [ :i |
self go: radius. "move to next new vertex"
vertices at: i put: self location. "remember it"
self place: center; "change location to center"
turn: angle]. "increase direction by angle"
"draw from each vertex to every later one"
self down.
color := 1.
1 to: sides - 1 do: [ :j |
j + 1 to: sides do: [ :i |
halftone foreColor:
  (color := 5 - color).
self place: (vertices at: j);
goto: (vertices at: i)]]

To execute this method, evaluate the following expression:

Pen new mandala: 30 diameter: 300

When you send the message go: to a pen, be aware that the actual pixels drawn may not be the same as you specified. This is because the number of pixels drawn are adjusted by a global variable, Aspect, which describes the aspect ratio of your display. Aspect contains a fractional number, determined by the video adapter board your system uses. The go: message multiplies the vertical distance of its argument by Aspect to yield the real number of pixels to draw. Because of this adjustment, you are able to draw squares and circles without worrying about your screen's aspect ratio. To find the correct Aspect to use with your video adapter board, evaluate the following expression:

Display black: (2 @ 2 extent: 150 @ 150)

With a ruler, measure the width and height of the black rectangle; then set:
**Aspect := widthMeasured / heightMeasured**

Aspect must be an integer or a fraction, not a floating point. For example, if the black rectangle measured 1 3/4" by 2 1/4" you would set:

**Aspect := (7/4) / (9/4)**

## The Network Example Revisited

With all the graphics techniques we have learned so far, we can now actually draw the network system you saw in Chapter 7.

In class NetworkNode, add the following methods to draw the node itself:

**draw**

"Draw the receiver node with a circle around its name."

| font r aspect pen |

font := Font eightLine.

(pen := Pen new)

defaultNib: 2;

mask: Form white.

pen place: position.

pen solidEllipse: (r := name size * font width + 15 // 2)

aspect: (aspect := font height + 16 / r / 2 / Aspect).

pen mask: Form black.

pen centerText: name font: font.

pen ellipse: r aspect: aspect

**position**

"Answer the position of the receiver node."

^position

The method draw draws a solid white ellipse large enough to contain the node name, displays the text for the name in black, and finally redraws the border of the ellipse in black. The method position answers the position of the node.
Then, in class Network, add another draw method to first draw arcs and nodes:

```pascal
draw
    "Draw the network. For each node, it draws
    all the arcs and then the node. All the
    nodes visited are remembered to avoid double
    drawing."`
| visited pen |
pen :=Pen new
    defaultNib: 4 @ 3.
visited := Set new.
connections keys do: [ :nodeA |
    visited add: nodeA.
    (connections at: nodeA) do: [ :nodeB |
        (visited includes: nodeB)
    ifFalse: [
        pen place: nodeA position;
        goto: nodeB position]].
nodeA draw]
```

In Chapter 7 you assigned a network of nodes to the global variable Net. Display the network by evaluating all of the following:

```
Display white.
Net draw.
Menu message: 'continue'.
Scheduler systemDispatcher redraw
```

The result should be as follows.

<figure><img src="image_43.png" /><figcaption>Figure 9.2
Displaying a Network</figcaption></figure>
## Commander

Commander is a subclass of Pen. A Commander commands an array of pens. It has messages to either fan out or line up all the pens under its command. It also reimplements messages like place:, turn:, down, up, go:, and goto: to pass the message to all of its pens. When the units of movement are small, this creates an illusion of all pens drawing simultaneously. For example, evaluate the following example, which draws five dragons fanning out:

Display white: Display boundingBox. "blank screen"

(Commander new: 5)

fanOut; "set fan out direction"

up;

go: 60; "set starting point"

down;

dragon: 9. "draw dragon"

Menu message: 'continue'.

Scheduler systemDispatcher redraw

The result should be as follows.

<figure><img src="image_44.png" /><figcaption>Figure 9.3
Dragon Curves</figcaption></figure>
## Animation

Animation is another subclass of Pen. An Animation contains several collections of images of objects in motion. Each collection of images (each image is an instance of class Form) represents the motion of a single object as a series of still frames. When the forms are displayed continuously, it creates the illusion of a moving object, as in a cartoon. You can send messages to an Animation to tell any of its objects to move, or change its position or direction.

Movement in class Animation differs from the one in Pen in that an animation always erases the old image of an object before moving it to a new position. The following example illustrates four walking dogs. The pictures of these dogs were drawn using the FreeDrawing tool included on your Smalltalk/V diskettes. Evaluate these expressions to create an animation called Animator, with four dogs under your command:

| dogImages |

dogImages :=
Array "build an Array of pictures of a walking dog"
with: (GraphDictionary at: 'dog1')
with: (GraphDictionary at: 'dog2')
with: (GraphDictionary at: 'dog3')
with: (GraphDictionary at: 'dog2').

Autorender := Animation new
initialize: Display boundingBox. "init clipping rectangle"

Autorender add: dogImages "add first dog"
name: 'Snoopy' " with a name Snoopy"
color: #lightGray. " and a lightGray color"

Autorender add: dogImages
name: 'Lassie'
color: #black.

Autorender add: dogImages
name: 'Bow'
color: #darkGray.

Autorender add: dogImages
name: 'Wow'
color: #white.
Now evaluate the following expressions, to issue commands to your dogs:

"issue commands"

Display gray. "make whole screen gray"

Animator

speed: 16; "each picture is displayed 8 pixels apart"

shiftRate: 2; "display picture twice before going to the next (slows down the leg and tail motion)"

setBackground; "use current screen as background"

tell: 'Snoopy' place: 0@0;

tell: 'Snoopy' direction: 45;

tell: 'Lassie' place:
0 @ (Display height - 60);

tell: 'Lassie' direction: -45;

tell: 'Bow' place:
(Display width - 100) @ 0;

tell: 'Bow' direction: 135;

tell: 'Wow' place:
Display extent - (100 @ 60);

tell: 'Wow' direction: -135;

tell: 'Snoopy' go: 350;

tell: 'Lassie' go: 350;

tell: 'Bow' go: 350;

tell: 'Wow' go: 350.

Menu message: 'continue'.

Scheduler systemDispatcher redraw

The above expressions command the four dogs to move towards the center of the screen.

The following commands cause Snoopy to bounce around the screen at a quicker pace:

Display gray. "make whole screen gray"

Animator

speed: 24;

shiftRate: 1;

tell: 'Snoopy' bounce: 4000.

Menu message: 'continue'.

Scheduler systemDispatcher redraw
The following commands make all four dogs bounce together.

80 times Repeat: [
Animator
tell: 'Lassie' bounce: 16;
tell: 'Bow' bounce: 16;
tell: 'Wow' bounce: 16;
tell: 'Snoopy' bounce: 16].

Menu message: 'continue'.

Scheduler systemDispatcher redraw

Global variables like Animator have varying amounts of memory assigned to them. To free this space after you have finished exploring the Animator variable here, type the following and evaluate it:

Animator := nil. "get rid of the instance"

This will set the global variable to nil, the undefined object, freeing up space as you continue.

## What You've Now Learned

By the end of this tutorial, you should be familiar with:

* Point, Rectangle, Form, and Bitmap

* BitBlt

* Halftone (mask)

* Combination Rules

* Clipping rectangles

* BitBlt subclasses CharacterScanner, Pen, Commander, and animation

As always, you can review any of these topics by repeating the corresponding section of the tutorial, or by referring to a detailed description in Part 3, The Smalltalk/V 286 Reference.

If you are going to exit Smalltalk/V before proceeding on to the next tutorial, be sure to save the image.
# 10 WINDOWS

As you've seen throughout these tutorials, windows provide the major interface between you and Smalltalk/V. For example, you use the Class Hierarchy Browser window to enter programs into the system, and the Disk Browser window to browse and manipulate files. In the previous chapters, we used standard, supplied windows for the tutorial examples. In this chapter, you'll find out how to make your own windows.

As always, the examples for this tutorial are stored in a disk file. To retrieve these examples, simply use the Disk Browser to load the contents of the file chapter.10.

You will also again be making modifications to the Smalltalk/V environment in this tutorial. Be sure to save the image when you exit the environment; if you want to repeat any section of this or any tutorial, it will already be there for you.

This tutorial builds on several of the previous tutorial examples in chapters 6, 7, and 9. If you have not done the tutorials in those chapters and saved the image, you should evaluate the following expression to install the needed classes:

(File pathname: 'class10.st') fileIn

The later part of this tutorial extends the animal and animal habitat classes with several new methods. Evaluate the following expression to file in all of the new methods:

(File pathname: 'animal10.st') fileIn

We will explain the new methods as they are used below.

## The Prompter

Prompters are a special kind of window which lets you ask a question and wait for a single response. For example, evaluate the following expression:

Prompter prompt: 'Do you know Smalltalk/V?'
default: 'Yes, I've done a tutorial.'

A window pops up with the *prompt*: argument as the window label, and the *default*: argument shown below it. The line containing the default is a standard text pane, which means that its contents can be edited. The user can either accept the response, or edit it. When you press the *return* or *enter* key, or select *accept* on the text pane menu, the prompter accepts your answer and displays it as the result of *show it*.

A unique characteristic of a prompter is that as long as it lives, no other windows can be activated. You must either accept or cancel the answer (which closes the prompter window) before you can activate another window.
# Single Pane Window

Other windows in Smalltalk/V are not as restrictive as prompters. They are more under user, rather than program, control. For instance, often a window may ask a question which you cannot answer without first consulting another window. You would then simply select or open another window, get your answer, then switch back to the previous window and enter the answer.

Let's start with a window with only a single text pane. Evaluate the following expression:

```
LearnDispatcher :=
    TextEditor
    windowLabeled: 'Learning Status'
    frame: (0 @ 0 extent: 400 @ 100).
```

TextEditor is one of the Dispatcher classes whose instance variables, when paired with a TextPane, provide text editing capabilities. Sending the windowLabeled:frame: message to it creates a window occupying a rectangular area (0 @ 0 extent: 400 @ 100) on the screen, with the specified label on the top.

To write text from your current window into the text pane of this new window, evaluate the following expression:

```
LearnDispatcher
    nextPutAll: 'I have learned everything about Smalltalk.';
    cr.
```

LearnDispatcher is used like a Stream, which you saw in Chapter 7. As you recall, the message nextPutAll: adds its String argument to the end of the receiver contents. The message cr then adds a line feed. For a TextEditor, cr also displays the buffered nextPutAll: strings immediately; without it, the display will be delayed until the next line feed is received.

You can now move the cursor over the new window and select it, which activates the window and lets you edit the text in it.

To retrieve the contents of this window from another window, activate a different window and evaluate the expression:

```
LearnDispatcher contents.
```

To close the window, select close from the window menu or select the close button on the label bar. Or, if you are in another window, simply evaluate the expression:

```
LearnDispatcher closeIt.
```
# Single Pane Window with More Interaction

The window you just created automatically inherits a window's standard text editing capabilities. In many cases, however, you'll want to customize a window to suit your application's needs.

In Chapter 7, you developed an animal habitat with a script to command animals. In this section we will build a custom window for editing and playing animal habitat scripts that uses its own menus. We will build the window in stages adding features as we go.

## Opening the Animal Habitat

The new window we are creating is for editing and playing scripts for animals. Since the class **AnimalHabitat** contains the methods for playing scripts we will add the methods for the new window to this class rather than create a new class. By convention, the message usually used to open a window is either **open** or **openOn**. Since we will want to pass a default or initial script to the window, we will implement a method for the **openOn** message. The following expression files in the first version of the **openOn** method for the **AnimalHabitat** class:

```javascript
(File pathName: 'window1.st') fileIn
```

This method opens a window that behaves in the same way as a workspace or System Transcript window.

```javascript
openOn: aString
    "Create a single pane window with aString
    as its initial script."

| topPane |
inputString := aString.
topPane := TopPane new label: 'Habitat'.
topPane addSubpane: TextPane new.
topPane dispatcher open scheduleWindow
```

Every window has a **TopPane** which encompasses the entire window, including the label. In addition to initializing the window label, it acts as a local scheduler inside the window to pick the active pane. To do this, it needs to know the existence of every subpane. Hence, we send the **addSubpane** message to the **TopPane**.

The last line first sends the **open** message to the dispatcher associated with the **topPane**, which prompts for the window area and displays the window. It then sends the **scheduleWindow** message to let the **Scheduler** activate the window. The global variable **Scheduler** is an instance of class **DispatchManager**; only the windows known to it can be activated.
Now, to open the window, evaluate:

AnimalHabitat new openOn: 'Snoopy be quiet'

A blank window with the label "Habitat" will appear.

Congratulations, you have created your first customized window! However, this window
cannot do much, except some text editing. It does not even display the initial script, the
argument aString, since we have not defined a mechanism for the habitat to
communicate with the window.

Before proceeding, close the window by selecting close from the window menu or select
the close button on the label bar.

## Connecting the Habitat to the Window

Our next version of the openOn: method adds a means of communication between the
habitat and the window. The following expression files it in.

(File pathname: 'window2.st') fileIn

Here is the new code:

openOn: aString
"Create a single pane window with aString
as its initial script."

| topPane |
inputString := aString.
topPane := TopPane new label: 'Habitat'.
topPane addSubpane:
 meaning to add a TextPane
model: self;
name: #input).

topPane dispatcher open scheduleWindow

In this method, we've sent two additional messages to the newly created TextPane:

model: self tells the text pane the identity of the controlling application (in our case, an
instance of AnimalHabitat) so that the text pane can send messages to the application.

name: #input lets the text pane identify itself with the name #input. The text pane also
uses its name as a message to the controlling application to retrieve the pane contents
when it is first opened. Hence we implemented the message input in the application class,
AnimalHabitat, to initialize the contents of the text pane:
### input

```pascal
"Initialize inputPane with inputString."
^inputString
```

We can now open the new window with the following expression:

```pascal
AnimalHabitat new openOn: 'Snoopy be quiet.'
```

Now the initial script appears when the window is opened.

## Customizing the Habitat Pane Menu

If you pop up the pane menu of the above window, you still see a standard text editor menu. In order to play the script, you'll want to customize this menu.

The following expression files in the final version of the `openOn:` method:

```pascal
(File pathName: 'window3.st') fileIn
```

We now present the final version of the `openOn:` message for `AnimalHabitat`:

```pascal
openOn: aString
    "Create a single pane window with aString
    as its initial script."
| topPane |
inputString := aString.
topPane := TopPane new label: 'Habitat'.
topPane addSubpane:
    (inputPane := TextPane new
        model: self;
        name: #input;
        menu: #inputMenu).
topPane dispatcher open scheduleWindow
```

We have made two changes to the `openOn:` method. We send the additional message `menu:` to the text pane to inform it that the controlling application has a method called `inputMenu` to create the menu for the input pane. We also assign the instance variable `inputPane` to point to the input pane, so that we can communicate with the pane.

Here is the `inputMenu` method which returns the new menu for the input pane.
### inputMenu

"Answer a Menu for the input Pane."

### ^Menu

labels: 'copy\cut\paste\play selection\play all' withCrs

lines: # (3)

selectors: # (copySelection cutSelection
 pasteSelection playSelection playAll)

The method inputMenu simply returns a menu. In this menu, the first three selectors copySelection, cutSelection, and pasteSelection are the standard TextEditor messages for copying, cutting, and pasting, which we do not need to implement again in class AnimalHabitat. We do, however, need to implement the following two new methods:

### playSelection

"Accept selected string as the script
and play it to animals."

CursorManager execute change.

self script: inputPane selectedString.

self play.

CursorManager normal change

### playAll

"Accept the entire content of the pane
as the script and play it to animals."

CursorManager execute change.

self script: inputPane contents.

self play.

CursorManager normal change

The playSelection method plays only the selected text in the input pane, while playAll plays the entire text in the pane. Notice that we change the cursor shape to execute (shown as an hour glass) while playing the script, and then change it back to normal when finished.

The window is now defined. To try it, first set up the global variable Habitat and teach the animals commands in the same way as you did in Chapter 7, and then open the window:

Snoopy := Dog new name: 'Snoopy'.

Polly := Parrot new name: 'Polly'.

Habitat := AnimalHabitat new

add: Snoopy;

add: Polly.
### Snoopy

learn: 'barking' action: [Snoopy talk];

learn: 'quietly' action: [Snoopy beQuiet; talk];

learn: 'is upset' action: [Snoopy beNoisy; talk].

### Polly

learn: 'to be pleasant' action:

[Polly vocabulary: 'Have a nice day'; talk];

learn: '*' nasty' action:

[Polly vocabulary: 'Why are you bothering me'; talk].

### Habitat openOn:

'Snoopy is upset about the way that Polly is behaving. It is as if whenever anyone asks Polly to talk, Polly will be nasty. Maybe if instead of Snoopy barking at Polly when he wants Polly to talk, Snoopy quietly asks Polly to be pleasant for a change, things would go better. Now maybe Snoopy barking quietly will not make Polly nasty.'

After the window is displayed, pop up the pane menu and select the **play all** choice. You'll then see the dialogue in the Transcript window. Next, try selecting only a portion of the script; pop up the same menu, pick the **play selection** choice, and watch the dialogue.

## Multi-Pane Windows

*Multi-pane* windows, such as the Class Hierarchy Browser or the Disk Browser, group related functions into several panes within a single window. This gives the user a clear picture of the application, since panes within one window do not overlay each other. Multi-pane windows are also ideal when panes within the window interact with one another to a high degree. For example, when you select a directory in the Disk Browser's directory list pane, the contents of two other panes (the file list pane and the bottom text pane) are automatically updated.

We'll now create a window which groups the animal pattern matching you saw in Chapter 7 with the animation from Chapter 9. In this window, we add two more panes to the previous example:
<figure><img src="image_45.png" /><figcaption>Figure 10.1
Animation
Window</figcaption></figure>

The *input pane* is the same as the one in the previous example, where you can edit the script to be played to the animals. The *reply pane* contains the dialogue from animals, so that we don't have to use the System Transcript window any more. The *animation pane* is the stage where animals can perform in response to commands in the script.

The following expression files in a new `openOn:` method which opens the above multi-paned window and `initWindowSize` which establishes its default size:

```
(File pathname: 'window4.st') fileIn
```

Here is the new code:
```java
openOn: aString
    "Create a kennel window with aString
    as its initial script."
|
topPane replyPane |
inputString := aString.
(topPane := TopPane new)
    label: 'KENNEL';
    model: self.
topPane addSubpane:
    (replyPane := TextPane new
        model: self;
        name: #reply;
        framingRatio: (0@0 extent: 2/3 @ (1/4))).
topPane addSubpane:
    (GraphPane new
        model: self;
        name: #graph;
        framingRatio: (0 @ (1/4) extent: 2/3 @ (3/4))).
topPane addSubpane:
    (inputPane := TextPane new
        menu: #inputMenu;
        model: self;
        name: #input;
        framingRatio: (2/3 @ 0 extent: 1/3 @ 1)).
replyStream := replyPane dispatcher.
topPane dispatcher open scheduleWindow
```

We call this window "KENNEL" because it will contain only dogs. (Your Smalltalk/V diskettes include the *FreeDrawing* utility, with which you can draw your own pictures of different animals and try them out.) The window consists of three panes: a text pane called *reply*, a graph pane called *graph*, and another text pane called *input*.

We set the instance variable, *inputPane*, to point to the input pane, so that later we can use it to reference the pane's contents. We also set another instance variable, *replyStream*, to point to the dispatcher of the reply pane, so that we can later use it like a stream and output things to it.

Since the size of each subpane depends on the size of the whole window, the message *framingRatio*: defines the position and size of each pane relative to its window. The coordinates of the rectangle argument to *framingRatio*: are a fraction of the width or height of the window. For example, if the window rectangle is:

`100 @ 100 extent: 300 @ 200`

then a framing ratio of `(2/3 @ 0 extent: 1/3 @ 1)` yields the rectangle:
(100 @ 100) + ((300 @ 200) * (2/3 @ 0)) extent:
(300 @ 200) * (1/3 @ 1)

which is equivalent to:

300 @ 100 extent: 100 @ 200

Since we want the window to open with a particular size, we provide the method `initWindowSize` which is used when the window is first opened:

initWindowSize

"Answer the initial window extent"

^ (Display boundingBox insetBy: 16 @ 16) extent

If you do not supply an `initWindowSize` method, the system uses a standard default size.

Several other new methods need to be added and existing methods need to be modified before we can try out the new window. The following code files in the changes and additions:

(File pathname: 'window5.st') fileIn

The following two methods initialize the two additional panes:

reply

"Initialize reply pane with an empty String."

^ String new

graph: aRect

"Initialize graph pane area to aRect and the animation associated with it."

| aForm |

aForm := Form

width: aRect width

height: aRect height.

aForm displayAt: aRect origin. "background"

animator := Animation new initialize: aRect.

animals do: [:anAnimal |

animator

add: anAnimal picture

name: anAnimal name

color: anAnimal color].

^aForm
Now, we'll slightly modify the previous example's `playSelection` and `playAll` methods by adding the message `changed:`, which asks the pane identified by its `Symbol` argument to reinitialize the pane contents:

**playSelection**

"Accept selected string as the script
and play it to animals."
self changed: #reply.
CursorManager execute change.
self script: inputPane selectedString.
self play.
CursorManager normal change

**playAll**

"Accept the entire content of the pane
as the script and play it to animals."
self changed: #reply.
CursorManager execute change.
self script: inputPane contents.
self play.
CursorManager normal change

Next, we'll change the `answer:` method to write to the reply pane instead of the System Transcript window:

**answer: aString**

"Output aString to the reply Pane."
replyStream
nextPutAll: aString;
cr

Next, we add a new method to clean up objects that are no longer useful after the window is closed. The message **super release** releases the objects needed by the `changed:` message in methods `playSelection` and `playAll`:

**release**

"Window is closed, release all the
objects created by this habitat."
inputPane := replyStream := nil.
super release

## Changes to Animal Classes

At the beginning of this tutorial we filed changes and additions to the **Animal** methods. We present them in this section.
Since we are going to animate the animal objects, we need to provide methods for initializing and accessing an animal's color and picture attributes. We are storing the color as a symbol representing the desired color and the picture as a series of images of the animal in motion that can be used by the **Animation** class described in Chapter 9. Here are these methods:

**color**

"Answer the receivers color."

^color

name: aString picture: images color: aColor

"Initialize the receivers name, pictures, and color."

name := aString.

picture := images.

color := aColor

**picture**

"Answer the receiver's array of pictures."

^picture

We need to provide behavior that lets the animals move about the graph pane. Since we are only using dogs for our example, we present the new methods for class **Dog**:

run: distance

"Run for distance."

self answer:

'I am running',
distance printString,
' feet'.

habitat animator

speed: topSpeed;

tell: name go: distance

turn: anAngle

"Turn direction with anAngle."

self answer:

'I am turning', anAngle printString, ' degrees'.

habitat animator

tell: name turn: anAngle
walk: distance

"Walk for distance."

self answer:

'I am walking ',
distance printString, 'feet'.

habitat animator

speed: self topSpeed // 2;

tell: name go: distance

In addition there are methods implementing the following new messages: bounce:, topSpeed:, direction:, and home. The implementations are very similar to the ones given above. You can use the Class Hierarchy Browser to view them.

## Running the Animation

Now that the methods are defined, we can initialize the animals:

| dogImages |
dogImages :=
    Array
        with: (GraphDictionary at: 'dog1')
        with: (GraphDictionary at: 'dog2')
        with: (GraphDictionary at: 'dog3')
        with: (GraphDictionary at: 'dog2').

Kennel := AnimalHabitat new.

Kennel add:

(Snoopy := Dog new
    name: 'Snoopy'
    picture: dogImages
    color: #gray).

Kennel add:

(Lassie := Dog new
    name: 'Lassie'
    picture: dogImages
    color: #black).

Kennel add:

(Wow := Dog new
    name: 'Wow'
    picture: dogImages
    color: #darkGray).
The above expressions add three dogs to the Kennel. Each dog has the same array of pictures but different colors. (Chapter 9 explains animation in detail).

Next we teach each dog the commands that define their behavior. Since all three dogs learn the same set of commands, we show only one dog's learning here:

Snoopy learn: 'bark a little'
action: [Snoopy beQuiet].

Snoopy learn: 'bark a lot'
action: [Snoopy beNoisy].

Snoopy learn: 'talk' action: [Snoopy talk].

Snoopy learn: 'home' action: [Snoopy home].

Snoopy learn: 'top speed'
action: [Snoopy topSpeed: Script peek asInteger].

Snoopy learn: 'run'
action: [Snoopy run: Script peek asInteger].

Snoopy learn: 'run inside kennel'
action: [Snoopy bounce: Script peek asInteger].

Snoopy learn: 'walk'
action: [Snoopy walk: Script peek asInteger].

Snoopy learn: 'direction'
action: [Snoopy direction: Script peek asInteger].

Snoopy learn: 'turn'
action: [Snoopy turn: Script peek asInteger].
Finally, evaluate the following expression to open the new window with a starting script:

Kennel openOn:

'Snoopy home

Lassie home

Wow home

Snoopy top speed 40

Lassie top speed 30

Wow top speed 20

Snoopy bark a little

Lassie bark a little

Wow bark a lot

Snoopy direction 45

Snoopy walk 200

Snoopy talk

Lassie direction 90

Lassie walk 150

Lassie talk

Lassie turn 225

Lassie run 150

Lassie talk

Wow direction 0

Wow walk 300

Wow talk

Wow turn 135

Wow run 250

Wow talk

Snoopy run inside kennel 4000 feet'

Pop up the pane menu of the input pane and select the **play all** choice to play the entire script. You see the dogs' dialogue in the reply pane, and their performance in the graph pane. The final window is shown in the following picture.
<figure><img src="image_46.png" /><figcaption>Figure 10.2
Kennel</figcaption></figure>

Try composing your own scripts in the input pane, and then select either all or a portion of it to play.

## What You've Now Learned

By the end of this tutorial, you should be familiar with prompters, single pane windows, and multi-pane windows. You've seen a complex interactive application, which provides many of Smalltalk's most important building blocks.

As always, if you want to review any of the topics covered in this tutorial, you can either repeat the corresponding section of the tutorial, or refer to the detailed description in Part 3.

If you are going to exit Smalltalk/V before proceeding on to the next tutorial, be sure to save the image.
# 11 OBJECT-ORIENTED DEVELOPMENT

The tutorials so far have shown you many strengths of the Smalltalk/V language and programming environment. Chapters 11 and 12 are about Smalltalk/V application development—the "how" rather than the "what" of OOPS. In Chapter 11, you will learn the OOPS application development cycle, a methodology which will guide you in designing and implementing your own Smalltalk/V applications. The process is demonstrated through a simple but complete example.

After working through the phone book example in this chapter, you will be ready for the more complex application in Chapter 12. There, a multi-window, multi-pane application involving state-transition data management is developed using Smalltalk/V in a case study of the development of an office information system.

## The Smalltalk/V Application Development Cycle

Smalltalk/V application development involves defining objects, their interrelationships and their behavior. Together with the Smalltalk/V environment, these problem solving techniques support an incremental and evolutionary approach to software development.

The Smalltalk/V application development process can be generally divided into six phases:

* State the Problem

* Draw the Window

* Identify the Classes

* Describe Object States

* List the Object Interfaces

* Implement the Methods

### State the Problem

Begin your Smalltalk/V application development project by making as explicit and cogent a problem statement as possible, describing what you are trying to get your software to do. For example:

**Problem Statement:** There are many people I call regularly on the phone, so I will create a window application which maintains a list of people and their phone numbers. The phone book list will be sorted in alphabetical order. Selecting a name will retrieve the associated phone number. Names and phone numbers can be added to and deleted from the list.
As you think through and learn more about your problem, you can return to the problem statement to qualify or extend it, refining application goals as you go along. The purpose here is to have a short, understandable statement of the problem which points toward the kind of software solution that will address your need.

Your problem statement, like your Smalltalk/V application, will evolve. The act of bringing your initial design ideas to life will generate insights that enhance your appreciation for the original problem.

## Draw the Window

Armed with your problem statement, ask yourself what a new window that helps solve your problem looks like, then draw it. Don's worry about the internal workings of the application and data structures involved. Concentrate on the appearance and interaction. What does the application look like? How does it behave? What existing Smalltalk/V windows have you seen that suggest an approach?

<figure><img src="image_47.png" /><figcaption>Figure 11.1
PhoneBook
Window Model</figcaption></figure>

Figure 11.1 shows a proposed phone book window. Notice that it includes menu selections. Having an idea of the number, size and contents of the panes of the window in your application is obviously helpful, but it is through the menu selections that things come to life. Menu items are the means for you as user-object to send messages to your application which is, after all, a Smalltalk/V object.

You have now transformed your problem statement into a concrete window to be built. The drawing is a specification of the application user interface and is the starting point for specifying the rest of the application.
The next three phases consist of identifying classes, object state and object interfaces, and are by nature interrelated. Although they are described sequentially, it may be more productive to perform them in parallel or iteratively.

## Identify the Classes

Knowing as much as you can about your problem and having a sketch of the application interface, you are ready to identify the classes of objects that implement the application. List both the new classes defined for the application and existing classes from the *Smalltalk/V* class hierarchy to be used. New classes will be either self-standing (a subclass of class *Object*), or a subclass of an existing class that implements some of the behavior needed by the new class. Existing classes used will generally include user interface classes (*Menu* and subclasses of *Pane*) and subclasses of *Collection*, *Magnitude* and *Stream*.

In our example, we need a new class *PhoneBook* whose instances are the phone book window application. Menus and panes appear in your window sketch, so classes *Menu*, *ListPane*, *TextPane* and *TopPane* are needed. After some consideration, it seems that a *Dictionary* is most appropriate for associating names with telephone numbers, and names and telephone numbers will be represented as *Strings*.

The classes identified so far seem like a natural breakdown of the problem statement and the window picture. But what if the choices aren't so obvious? It's important to make some choice, even if it's not the best. You may be wrong, but with an environment that allows you to make changes easily, you can change to better ones later. Any kind of progress on the application will enhance your understanding of the appropriate classes to be used.

## Describe the Object States

Given the problem statement, the window drawing and the classes to be used, what instance variables are needed in the new classes you have defined? In this example, our only new class is *PhoneBook*, which is the window model. What state is appropriate to put in a phone book object? The standard dispatcher and pane classes already keep track of most of the state associated with the window. The model, an instance of class *PhoneBook*, needs the application specific data, which in this case are (1) the dictionary of phone numbers *phones* and (2) the name from the selected list pane entry *selectedName*. Look in file *chapter.11* to see the resulting class definition.

Are there general rules for deciding what instance variables to use? The best guide is to look ahead to the object interface. What kind of questions (messages) will the object be asked? What will be the object's behavior?
## List the Object Interfaces

Object interfaces are the messages that the object responds to. List the messages that the new classes must implement. A good starting point for this is to augment the window drawing with the messages that will be used for each pane.

<figure><img src="image_48.png" /><figcaption>Figure 11.2 PhoneBook Window with Messages</figcaption></figure>

In Figure 11.2, the message names are circled. In general, messages are used for (1) accessing non-standard menus defined by the application, (2) accessing the contents of panes for display purposes, (3) saving the contents of changed panes, (4) carrying out menu selections, and (5) opening the window. In our example we have the following:

<table><thead><tr><td>Message</td><td>Purpose</td></tr></thead><tbody><tr><td>add</td><td>Add a new name to phone book.</td></tr><tr><td>list</td><td>Answer the list of names of people in the phone book.</td></tr><tr><td>list: aString</td><td>Display the phone number for aString.</td></tr><tr><td>listMenu</td><td>Answer the name list menu.</td></tr><tr><td>openOn: aDictionary</td><td>Open a phone book window for aDictionary.</td></tr><tr><td>remove</td><td>Remove selected name from phone book.</td></tr><tr><td>text</td><td>Answer the phone number for the selected name.</td></tr><tr><td>text: aString</td><td>Enter aString in dictionary as phone number for selected name.</td></tr><tr><td>from: aDispatcher</td><td></td></tr></tbody></table>
## Implement the Methods

All that remains to do is to implement the methods. A good starting point for a window application is to do the window opening message (in our case, `openOn:`). This method is generally the largest in your application. Here it is for the phone book:

``` FIRS
openOn: aDictionary
    "Open a phone book window on aDictionary. Define
    the pane sizes and behavior and schedule the window."
```

``` FIRS
| topPane |
```

``` FIRS
phones := aDictionary.
```

``` FIRS
topPane := TopPane new.
```

``` FIRS
topPane label: 'Phone Book'.
```

``` FIRS
topPane addSubpane:
```

``` FIRS
(ListPane new
    menu: #listMenu;
    model: self;
    name: #list;
    change: #list;;
    framingRatio: (0 @ 0 extent: 1/3 @ 1)).
```

``` FIRS
topPane add Subpane:
```

``` FIRS
(TextPane new
    model: self;
    name: #text;
    change: #text:from;;
    framingRatio: (1/3 @ 0 extent: 2/3 @ 1)).
```

``` FIRS
topPane dispatcher open scheduleWindow
```

All open messages for a window are very similar. They generally initialize model instance variables, and create window panes by specifying the following for each pane:

* **menu:** The pane menu. Not used if the standard pane menu is used.

* **model:** The pane model, generally self.

* **name:** Specifies the selector of the message used to ask model for pane contents.

* **change:** Specifies the selector of the message used by the pane to tell the model that the pane contents are changed.

* **framingRatio:** A rectangle that specifies the relative position and size of the pane in a window 0@0 corner: 1@1.

The remaining methods for the phone book are small and are contained in the chapter.11 file. The following expression files in class PhoneBook.

``` FIRS
(File pathname: 'chapter.11') fileIn
```
You will find it as the first expression in file chapter.11.

To try it out, evaluate:

PhoneBook new openOn: Dictionary new

## Knowing When to Stop

During the first round of a new application development project, you generally progress through the above six steps sequentially. Once you have your "first cut" at a proposed application, your increased understanding will generate new insights. You will find yourself jumping around the six steps in a non-sequential process. For instance, while writing a method you realize the need for a class instance variable which brings you back to state considerations.

Smalltalk/V's incremental compiling capabilities promote dynamic movement among these development steps. In face, Smalltalk/V application development can be seductive. It is always easy to think about adding just a few more lines of code which will further polish the application's interface or performance. By design, Smalltalk/V is an open system, an application always has the potential for enhancement.

That is why it is important to refresh your memory as to the original design goals of your Smalltalk/V application development project. If the prototype meets your design criteria, then stop. Use the application for a while and let experience guide subsequent enhancement.

## What You've Now Learned

You have seen the Smalltalk/V application development cycle applied to a very simple example. You have seen that a Smalltalk/V application develops in a revolving process which addresses:

* State the Problem

* Draw the Window

* Identify the Classes

* Describe Object States

* List the Object Interfaces

* Implement the Methods

The next chapter will showcase the Smalltalk/V application development cycle in a more involved case study. As always, if you want to review any of the topics covered in this tutorial, you can either repeat the corresponding section of the tutorial, or refer to the relevant topics in Part 3.
# 12 APPLICATION DEVELOPMENT: CASE STUDY

This chapter gives you a snapshot of the Smalltalk/V big picture...

* How do you approach real world problems with Smalltalk solutions?

* What tips and techniques help your application prototyping?

* Where are the "hot spots" in the Smalltalk/V manual that help you write your own applications?

* Where do you start?...How do you proceed?

We'll take a case study approach. A case of real world people, not whiz Smalltalk programmers. This chapter is intended for non-programmers, people with something they want their computers to help them do.

## The Case Study: A State-Transition Perspective

Our case involves the prototype development of an office automation system, a sales communication application. In the general scheme of things, the case study is an analysis involving state-transition models where physical objects pass through a series of states according to a network of connections between the states.

The connections between states reflect the logical constraints and preconditions which order the transition from one state to another. The tokens which pass from node to node along the constraint network are objects experiencing the state changes.

Objects going through state changes is a subject of broad interest. The objects might be:

* A molecule in a chemical reaction

* A widget on the widget assembly line

* A source code instruction processed by a compiler

* A ticket in an airline reservation system

State-transition models relevant to these objects include, respectively:

* A theory on the molecular interaction of the substances involved in the reaction

* A specification of the factory's numerical control tools and assembly line conveyors, including capacities and set-up time

* A graph grammar specifying the step-wise decomposition of a high-level language instruction into a collection of machine instructions

* A diagram describing the paperwork flow involved in getting a ticket to a traveler
Smalltalk/V is ideal for developing applications based on state-transition and other highly graphical and data intensive models. Its screen graphics and rich data abstracting capabilities allow you to produce working prototypes of complex models in record time.

Our case study shows how some self-starters in an optical disk drive sales office develop a Smalltalk/V application to track and facilitate their sales communications. Their state-transition network—though they didn't know or care to call it that—is a flowchart of their company's approved sales strategy. The tokens coursing through this sales strategy network are their potential customers.

## The Case Study Problem as a Smalltalk/V Problem

Where to begin? The Whizzard WORMS reps knew they had a sales communication problem. They knew they wanted to develop a Smalltalk/V application to facilitate these communications consistent with corporate marketing strategy. They had a sense of what the application interface would be like—inspired by things they'd seen, code they already had on hand. A lunchtime, napkin-based brainstorming session captured the problem.

On the first napkin they drafted a preliminary objective, their problem statement:

To develop a Sales Communication application (SalesCom) that will allow a rep to sit down to its interactive display where he or she will quickly and easily see what communication events are required for the day and to see what the current load of their customers is for each of the steps in the sales strategy. Further, the prototype version will print letters on a case by case basis and will facilitate making telephone follow-up calls. The prototype application will be built as much as possible from reusable code taken from the Smalltalk/V tutorial examples and system source files.

Looking over the rep developers' shoulders with the eyes of a generalist, their problem statement can be restated:

To develop a Smalltalk/V application using state-transition models to manage the movement of objects through a process. This application will have a multi-pane main window where a database of tokens moving through the process can be singled out for state inspection and manipulation. The application keeps a token state change event schedule. When moving a token through the process, the application prompts the user for preconditions or the result of state transitions where the network exhibits multiple alternate branches.

The reps, as far as WORM disk sales are concerned, see a world of "us" and "them". Inside are the Whizzard reps and outside, a sea of current and potential customers. The rep's job consists of acting as an agent for Whizzard through a series of communication events with a customer which hopefully lead to a sale.
They sketched a diagram of their customer prospecting strategy as shown in Figure 12.1. It was a simple marketing strategy which involved a direct mail campaign, followed by a telephone contact with an attempt to get interested folks to attend a sales seminar.

<figure><img src="image_49.png" /><figcaption>Figure 12.1
**SalesCom** Sales Strategy</figcaption></figure>

## A Window Model for the SalesCom Application

The reps next sketched a rough picture of the ideal **SalesCom** application window as in Figure 12.2. Their objective was to take inspiration from the various tutorial examples and system features to formalize their vision of the proposed application.

<figure><img src="image_50.png" /><figcaption>Figure 12.2
Sketch of **SalesCom** Window</figcaption></figure>
There were two purposes for the display as far as they could see:

* They want a basic facility to scan and manage the database of customers. Once one is selected, they want to see the detailed record of that customer.

* They want to promote a visual connection between their real world activity and the formal corporate marketing strategy represented by the (state-transition) diagram in Figure 12.1.

The first requirement suggested the **ListPane** and **TextPane** views in the **SalesCom** window model, the Master Customer List pane and Customer Detail text pane. The inspiration was the **Smalltalk/V** browsers where a clicked selection in a scrolling **ListPane** initiates a look-up of some associated detail information, the source code for a method or the contents of a file for instance.

The second requirement begged for graphic representation based on an extension of the tutorial Network of Nodes example. The "hard" part of taking a network data structure and giving it a visual representation is done already. The reps needed only to give the nodes more representative names and new screen coordinates to transform the random network of Figure 9.2 to that of Figure 12.1.

The **GraphPane** of the **SalesCom** application (inspired by the animation pane of the Animal Habitat tutorial example) facilitates the salesperson's visualization of the communication process and the flow of customers through the sales cycle. Numbers appended to the label of each communication event node indicate the total number of customers currently pooled at that step in the strategy as well as the year-to-date total of customers having passed through the event node.

## Menus Enrich the Window Model

The reps next addressed what the application would have to do to move customers through the marketing strategy. What "messages" would the user rep want to send to the **SalesCom** application object to control preparing letters and prompting phone calls?

They gravitated toward brainstorming about the menus of each of the **SalesCom** application subpanes. They found a pad of removable self-stick notes was useful for laying hypothetical menu pop-ups over each pane. After much debate, the reps decided on the configuration in Figure 12.3.

The reps were excited. They finished lunch with a clear sense of direction on their development project. They were ready to start writing code.

*Survival Tip #1: Don't over-plan. Yes, you need a decent statement of your problem and some sense of what the application's primary interface will look like. But you don't have to have all the answers to get going. To determine when to start, ask yourself, "If I write as much code as
<figure><img src="image_51.png" /><figcaption>Figure 12.3
Menus Enrich SalesCom Window</figcaption></figure>

it takes to get a prototype to look and behave like the design so far, would it do anything interesting and useful?" When your answer is affirmative, go to it. *Smalltalk/V* is designed to work with you in an evolving, incremental process.

As they prepared to begin development, the reps took a couple of minutes to draft a list of the classes involved in the SalesCom application:

* SalesCom, the sales communication application and window model class

* ComEvent, the class of communication events

* Network, the network of ComEvents which make up a sales strategy

* Dictionary, to associate customer names and customer data

## Getting There in Half the Time: Recycling Code

The reps set to work on a kind of scavenger hunt for reusable code to get the SalesCom application up and running as soon as possible. You can recreate their exploratory process by opening a Class Hierarchy Browser and use your Disk Browser to open the chapter.12 tutorial file.

As you do more Smalltalk/V programming you will see that you re-use code in two basic ways:

* indirectly through inheritance when creating new subclasses, and

* through cut and paste of methods from an existing class to a new, non-hierarchically related class.
You will use both methods in creating the SalesCom application, starting with the inheritance approach by creating a new class of object, ComEvent, as a subclass of the NetworkNode class to borrow the basic window model for the three pane SalesCom application.

If you have not done the tutorial examples in Chapters 6, 7, 9 and 10 and/or have not saved the image along the way, you should add the following classes by evaluating:

```
Object subclass: #NetworkNode
instanceVariableNames:
    'name position'
classVariableNames: "
poolDictionaries: "
```

Then select and evaluate:

```
Object subclass: #Network
instanceVariableNames:
    'connections'
classVariableNames: "
poolDictionaries: "
```

and evaluate:

```
Object subclass: #AnimalHabitat
instanceVariableNames:
    'animals replyStream animator inputString inputPane'
classVariableNames: "
poolDictionaries: "
```

Then select and evaluate the following file in the relevant methods for these three classes. This is to give you a starting point equivalent to where the sales reps started.

(File pathname: 'network9.st') fileIn.

(File pathname: 'nodes9.st') fileIn.

(File pathname: 'class10.st') fileIn.

(File pathname: 'animal10.st') fileIn.

(File pathname: 'window4.st') fileIn.

(File pathname: 'window5.st') fileIn.

Update the class list pane of the Class Hierarchy Browser.

## Re-working the Network of Nodes

The starting point of the Network of Nodes example can be seen in Figure 9.2.
As a first step, we create a new pool dictionary, `SalesStrategy`, to hold objects that will be global to our new application. Initially we create two new pool variables: `StratNet` to hold the sales strategy network, and `StratNodes` to hold the nodes used in the sales strategy network. Evaluate the following:

`SalesStrategy := Dictionary new.`

`SalesStrategy`

at: 'StratNet' put: Network new initialize;

at: 'StratNodes' put: Dictionary new

The network node of the `SalesCom` marketing strategy diagram requires a richer data structure than the simple name and position instance variables defined for the path computation and graphic display examples from earlier tutorial chapters. So we will create a `ComEvent` subclass of `NetworkNode`. In addition to a couple of new methods, a `ComEvent` has an `info` instance variable which is initialized as a `Dictionary` object. `Info` will be used to store information about the body of the letter or phone call script, the delay before the next event and a pointer to the next most likely event, etc. Select and evaluate:

`(File pathname: 'comevent.cls') fileIn`

We will use the pool variables `StratNet` and `StratNodes` to build a sales strategy network. `StratNet` and its nodes are given new names, positions and connections which transform the prior random network graphic example into the structured diagram of the `SalesCom` marketing strategy. Evaluate the following to see the transformation:

```text
#((('Kick-off Ltr' 180 20)
('Fol-Up Ltr' 180 120)
('Phone Call' 180 180)
('Wake Up Ltr' 295 180)
('Exit Seminar' 57 240)
('Exit Dead Leads' 280 240))
```

do: [:nodeInfo |

ComEvent new initialize;

name: (nodeInfo at: 1) position:

(nodeInfo at: 2) @ (nodeInfo at: 3)].

(SalesStrategy at: 'StratNet') initialize.

ComEvent

connect: 'Kick-off Ltr' to: 'Fol-Up Ltr';

connect: 'Kick-off Ltr' to: 'Exit Seminar';

connect: 'Fol-Up Ltr' to: 'Phone Call';

connect: 'Fol-Up Ltr' to: 'Exit Seminar';

connect: 'Phone Call' to: 'Wake Up Ltr';

connect: 'Phone Call' to: 'Exit Seminar';

connect: 'Phone Call' to: 'Exit Dead Leads';
To see what the sales strategy net looks like, evaluate the following:

* Display white
* (SalesStrategy at: 'StratNet') draw.
* Menu message: 'continue'.
* Scheduler systemDispatcher redraw

Your screen should look like Figure 12.4.

<figure><img src="image_52.png" /><figcaption>Figure 12.4
**SalesCom**
Network
Screen</figcaption></figure>

The preceding code drew directly on the display. What we want is to draw it to a form
so we can display it in a window. We need to edit the methods **draw** in class **Network**
and **NetworkNode** to produce an additional method **drawOn**: for each class. You can
either directly edit these methods using the Class Hierarchy Browser as indicated below
(note the **\*\* changed \*\* lines) or copy the methods from the file **chapter.12** and paste
them over the new method template.

The method **drawOn**: for class **Network** is:
drawOn: aForm "** changed **"
"Draw the network. For each node, it draws all
the arcs and then the node. All the nodes visited
are remembered to avoid double drawing."

| visited pen |

(pen := Pen new: aForm)

defaultNit: 4 @ 3.

visited := Set new.

connections keys do: [ :nodeA |

visited add: nodeA

(connections at: nodeA) do: [ :nodeB |

(visited includes: nodeB)

ifFalse: [

pen place: nodeA position;

goto: nodeB position]].

nodeA drawOn: aForm] "** changed **"

The method drawOn: for class NetworkNode is:

drawOn: aForm "** changed **"
"Draw the receiver node with a circle

around its name."

| font r aspect pen |

font := Font eightLine.

(pen := Pen new: aForm)

defaultNit: 2;

mask: Form white.

pen place: position.

pen solidEllipse: (r := name size * font width + 15 // 2)

aspect: (aspect := font height + 16 / r / 2 / Aspect).

pen mask: Form black.

pen centerText: name font: font.

pen ellipse: r aspect: aspect.

Note that the draw methods could now by changed to use the new drawOn: methods,
but we leave this as an excercise for you. (Hint: it can be done in a single line.)

To test the new methods, evaluate the following:
| f |

f := Form width: 600 height: 300.

(SalesStrategy at: 'StratNet') drawOn: f.

f displayAt: 0 @ 0.

Menu message: 'continue'.

Scheduler systemDispatcher redraw

That's it. You have roughed out one of the two main components of the visual presentation of the SalesCom application.

Take a moment to inspect the ComEvent class. Although the Net drawing of ComEvent nodes is visually similar to the random network tutorial example, notice the impact of the initialize and name:position: methods. While a ComEvent can be drawn by the same code that draws the original NetworkNode of the network tutorial example, a ComEvent has a much richer data structure:

initialize

"Initialize the dictionary data record of information about the communication event to be empty."

info := Dictionary new

info

at: '1.Strategy' put: 'NYS'; "not yet specified"

at: '2.Type' put: 'L, P or F';

at: '3 Message' put: 'Type your message here.';

at: '4.Next Step' put: 'NYS';

at: '5.Days Till NS' put: 'NYS';

at: '6.MTD' put: 0;

at: '7.YTD' put: 0

name: aString position: aPoint

"Set the name and position while adjusting for screen aspect ratio"

super

name: aString

position: aPoint x @ (aPoint y * Aspect) truncated.

StratNodes at: aString put: self

The next step is to integrate this graphic network display into the multi-pane window you are about to create with code copied from the Animal Habitat tutorial example. Divide and conquer. That's a big part of Smalltalk/V programming strategy.
Raiding the Animal Habitat

Before you can "borrow" some code, you need some place to put it. While you can expect to call on many existing classes in the course of your Smalltalk/V development projects, you can pretty much count on adding a new class for the application model. This is the class which defines the user interface of your application.

To run your "program", you create a new instance of your application model class, then interact with it. In this case, evaluate the following to create the new SalesCom class as a subclass of Object:

Object subclass: #SalesCom
instanceVariableNames: 
'replyStream'
classVariableNames: "
poolDictionaries: 'SalesStrategy'

The first and most important method in the SalesCom application is the openOn: method which describes the window appearance. This method also associates a collection of methods with each of the panes. These methods are triggered as needed when the window or any of its subpanes are created or changed.

You will get much of the code detail and the overall method structure from the openOn: method of the AnimalHabitat class. Use a Class Hierarchy Browser to locate the method, select all of it and copy it.

Return to the currently empty SalesCom class and add your first method by choosing new method from the method list pane menu. Select the entire default method template and chose paste from the text pane menu. Then change the method selector from openOn: aString to open. The method source text pane should look like this at the conclusion of this operation:
open
"Create a kennel window with aString as its initial script."
| topPane replyPane |
inputString := aString.
topPane := TopPane new label: 'KENNEL';
model self.
topPane addSubpane:
(replyPane := TextPane new
model: self;
name: #reply;
framingRatio: (0 @ 0 extent: 2/3 @ (1/4)).
topPane addSubpane:
(GraphPane new
model: self;
name: #graph;
framingRatio: (0 @ (1/4) extent: 2/3 @ (3/4)).
topPane addSubpane:
(inputPane := TextPane new
menu: #inputMenu;
model: self;
name: #input;
framingRatio: (2/3 @ 0 extent: 1/3 @ 1)).
replyStream := replyPane dispatcher.
topPane dispatcher open scheduleWindow

Choose save from the method source text pane and you will get an "undefined" message
explaining that the SalesCom class does not have the instance variable, inputString,
defined.

What a dilemma. You can't save the method without discarding the changes which, in
this case, means losing the entire new method.

Survival Tip #2: When you are on cut and paste "raids" of existing methods, put the source method's class instance variable name(s) into the temporary variable declaration of the new method in your new class. You can then save the method and decide later whether your new class definition needs to be changed to incorporate the source's instance variable(s) or whether you leave the temporary declarations.

Insert inputPane, inputString, and aString into the temporary variable declaration as
follows:

| topPane replyPane inputPane inputString aString |
And save again. This time it works. But you want a SalesCom window to open on a marketing network, not on an inputString. Edit the comment. You've determined that you won't be needing a temporary or instance variable called inputString, so delete it from the temporary declaration. Also delete the temporary aString and remove the first statement setting inputString. Edit the label: argument now appearing in the first statement of the method. The SalesCom window model now looks like:

```java
open
    "Create a SalesCom window with aNet
    as its marketing strategy."
  | topPane replyPane inputPane |
topPane := TopPane new label:
    ('SalesCom: ', Date today printString);
    model self.
topPane addSubpane:
    (replyPane := TextPane new
        model: self;
        name: #reply;
        framingRatio: (0 @ 0 extent: 2/3 @ (1/4))).
topPane addSubpane:
    (GraphPane new
        model: self;
        name: #graph;
        framingRatio: (0 @ (1/4) extent: 2/3 @ (3/4))).
topPane addSubpane:
    (inputPane := TextPane new
        menu: inputMenu;
        model: self;
        name: #input;
        framingRatio: (2/3 @ 0 extent: 1/3 @ 1)).
replyStream := replyPane dispatcher.
topPane dispatcher open scheduleWindow
```

Follow the same procedure, copying method initWindowSize from AnimalHabitat to class SalesCom and save it.

SalesCom is shaping up. Evaluate:

Test := SalesCom new.

Test open

A Walkback window informs you that the SalesCom object, Test, does not know how to reply. Close the Walkback and go back to AnimalHabitat to copy the reply method and paste it in as a new method in the SalesCom class. Save it. Next evaluate:

Test open
No big deal, another Walkback. A little more careful inspection of `open` reveals the need to copy `graph:` and `input` and `inputMenu` methods from `AnimalHabitat` into `SalesCom`. Start with `graph:`. After pasting it into `SalesCom`, `graph:` will look like this:

```pascal
graph: aRect
    "Initialize graph pane area to aRect and the
    animation associated with it."
| aForm |
aForm := Form
    width: aRect width
    height: aRect height.
aForm displayAt: aRect origin. "background"
animator := Animation new initialize: aRect.
animals do: [:anAnimal |
    animator
        add: anAnimal picture
        name: anAnimal name
        color: anAnimal color].
^aForm
```

Before saving, notice that most of the code relates to animals and not to our problem. Let's first edit the comment to say what we really want this method to accomplish. Then edit the code to do what is necessary. We can always leave the code incomplete and the comment will serve as a guide to finish it later. In this case, the actual implementation is very simple:

```pascal
graph: aRect
    "Initialize graph pane area to aRect and the
    network strategy graphic associated with it."
| aForm |
aForm := Form width: 600 height: 400.
StratNet drawOn: aForm.
aForm displayAt: aRect origin clipRect: aRect.
^aForm
```

**Survival Tip #3:** When re-working a copied method, edit the "comment" first to make it reflect the changes you will be making. Let the edits you make in the comment direct you to lines in the code which implement that aspect of the method responsible for the behavior referred to in the comment. This technique can be so useful that you will develop an appreciation for and will, hopefully, write clear and complete comments. They aren't "training wheels". Comments are an integral part of the communication among the Smalltalk/V community of programmers.
Continue by copying the `inputMenu` method into `SalesCom`. It can be saved without modification, though a `SalesCom` object will not understand if you ask it to perform any of its menu selections related to the Animal Habitat. The reps brought in `inputMenu` to get the `SalesCom` prototype window running as soon as possible. They later used this method as a template to create a menu for each of the `SalesCom` window subpanes as required by their prototype design.

Next copy the `input` method from `AnimalHabitat` to `SalesCom`. Before saving it will look like this:

### input

```python
"Initialize inputPane with inputString."
^inputString
```

You can see that this method simply returns the content of `inputString`, an `AnimalHabitat` instance variable, and since you just slipped those instance variable names into the temporary variable declaration of the `open` method, you know this won't work. So to "hardwire" a consistent response from this method until you decide what this method will need to do, change the method to read:

### input

```python
"Initialize inputPane with a temporary input string."
^'This is a test input string.'
```

and save the method.

**Survival Tip #4:** Simplified, interim versions of methods are an excellent way to "divide and conquer" a Smalltalk/V programming problem. Most often this involves writing a return expression which is a specific instance of the kind of object which will result from the more complex computation that a method is planned to perform. This is especially useful for separating development progress on the interface from progress on the internal manipulations of the involved data structures.

Now select and evaluate:

### Test open

You've done it. The first incarnation of a recognizable `SalesCom` object, as seen in Figure 12.5. The pizzaz of the strategy network `GraphPane` hints at the developing interface. The window resembles but certainly isn't an animal habitat anymore. The two `TextPanes` are the targets of the next phase of your `SalesCom` prototype development project.
<figure><img src="image_53.png" /><figcaption>Figure 12.5
**SalesCom** Window
Phase One</figcaption></figure>

Customers and Events: A Matter of State

The reps realized they were on track now. They saw two areas of **SalesCom** which needed attention:

* They needed to create an initial state, a test case "world" onto which a **SalesCom** object could open.

* They also needed to add new methods which would make the **SalesCom** window manage the interaction and updating of the Customers and Communication Events databases.

You incorporate many traditional database management operations into the **SalesCom** application simply by making the global variable **Customers** a new **Dictionary** object. The dictionary *keys* will be the customer company names. The value associated with each company is an array, its elements the 'fields' in the customer 'database'. A comment identifies each element in the first case entry. Select and evaluate:
Customers := Dictionary new.

Customers

at: 'ABC Inc.' put: #( "Key is company name"
'Fred Smith' "Field 1 is Contact Name."
'123 Maple St.' "Field 2 is Address."
'Boston MA 02055' "Field 3 is City State and Zip"
'555-4321' "Field 4 is Phone."
'Kick-off Ltr' "Field 5 is Current Event scheduled."
'Feb 14, 1988' "Field 6 is Prep Date for Current Event."
()); "Field 7 is an array for the customer history."

at: 'DEF Co.' put: #('Jane Doe' '321 Poplar Terr.'
'Hicksville MD 21202' '555-3476' 'Fol-Up Ltr' 'Feb 15, 1988'
('Kick-off Ltr: Feb 7, 1988'));

at: 'GHI Ltd.' put: #('Clive Davies' '999 Oak Ave.'
'Tustin CA 92680' '555-7890' 'Kick-off Ltr' 'Feb 14, 1988'
());

at: 'JKL Inc.' put: #('Bert Jenks' '666 Sycamore St.'
'Irvine CA 92680' '555-4734' 'Kick-off Ltr' 'Feb 14, 1988'
());

at: 'MNO Co.' put: #('Bill Rasp' '345 Apple Ave.'
'New Vists CA 93232' '555-5678' 'Fol-Up Ltr' 'Feb 20, 1988'
('Kick-off Ltr: Feb 6, 1988'));

at: 'PQR Corp.' put: #('Ellie Small' '423 Sassafras Ave.'
'Tustin CA 92680' '555-2064' 'Phone Call' 'Feb 20, 1988'
('Kick-off Ltr: Feb 1, 1988' 'Fol-up Ltr: Feb 8, 1988'));

To see the mini-database which you just created, evaluate:

Customers inspect

To inspect a customer—to review and modify the fields in a customer's "record"—select a customer name in the left hand list pane of the Customers Dictionary Inspector. Bring up the pane menu and click on inspect to pop up an Inspector on the selected customer. Each element, or field, of the customer record is a selectable line in the new Inspector list pane. Remember to select save in the TextPane whenever you make field changes that you want to keep.

To explore the many things you can do with Customers, turn to the Dictionary class entry in the Smalltalk/V Encyclopedia of Classes. Try "talking" to the Customers dictionary to explore its database-like behaviors.

The reps used the Encyclopedia of Classes like a foreign language phrase book throughout the development of SalesCom. The reps got the customers to jump through hoops with keysDo:, includes:, occurrencesOf: and at:put: messages. Check it out. Flip to the Encyclopedia and explore the things you can do to the Customers.
**Survival tip #5:** Study and understand the format of a class entry in the *Encyclopedia of Classes* in *Part 4* of the *Smalltalk/V* manual. And learn to actively use the *Methods Index* in the *Appendices* to direct your cross-referencing exploration of the behavior of the many classes which make up the *Smalltalk/V* environment.

With customer objects in hand, the reps turned their attention to the communication events.

The reps used the same basic object inspection technique to fill in the required information in each of the ComEvent network strategy nodes. This included typing in the body of the letters and phone call scripts that will eventually be merged with customer information. To save you time, evaluate:

```
(File pathname: 'comevents.in') fileIn
```

To view the new data contained in the ComEvent nodes, select and evaluate:

```
(SalesStrategy at: 'StratNodes') inspect
```

Open an Inspector on one of the nodes. Use the **Zoom** button to get a good view of the letter bodies and phone script. All these data management and multi-window editing features of Smalltalk/V dictionaries are already a part of the capabilities of the **SalesCom** application. The reps realized this was as sophisticated a data management facility as they needed for the prototype.

**SalesCom** consists now of two databases, **Customers** and **StratNodes**, a network of **ComEvents**. It will be up to new and original methods to manage these objects' interation and updating. You will do that by enhancing the behavior of the multi-pane **SalesCom** window.

## Methods and Messages: *Bringing the Prototype to Life*

The reps next objective was to get each **TextPane** working with the **Customers** dictionary. The inspiration was the browser, so they poked around relevant methods, especially **open**, in the browser classes and determined that they needed one to function as a **ListPane**. Selections in the **ListPane** would then trigger a method for the other **TextPane** to display the data record for the selected customer.

The reps edited **open** to reflect the following changes:

* **replyPane** was renamed **custsPane** and made a **ListPane**.

* **custsPane** was assigned a **name**: method, **customers**, to return the Sorted Collection of customer company name keys from the **Customers** dictionary.

* A **change**: method, **viewCust**; was added to **custsPane** so list selections would trigger display of the customer record in the customer detail **TextPane**.
* `inputPane` was renamed `custDetPane` to show that it would display customer details.

* `custDetPane` was added to the `SalesCom` instance variable list and recompiled after "killing" the `SalesCom` object, `Test`.

* Each pane was assigned its own menu method.

To see the many changes that the reps made to `SalesCom` during this next phase of prototype development, evaluate:

Test := nil.
(Files pathname: 'class12.2in') fileIn.

Test := SalesCom new.

Test open

You will probably get a walkback saying **has instances**.

**Survival Tip #6: Finding lost instances.** Windows cause dependencies to be stored between panes and models in the *Dependents* dictionary. If you have instances of a class that you cannot find, they are probably in the *Dependents* dictionary. This usually happens when you are debugging a new window. You remove these instances by executing the expression:

`Scheduler reinitialize`

All open windows close and the System Transcript reappears.

`SalesCom`'s basic behavior is evolving. Note the reps are using a "divide and conquer" approach to bring the prototype up in stages with the interface taking the lead. The `customers` method returns a typical list of customer names and `viewCust`: simply clears the Customer Detail pane and displays a few lines of sample text.

Once they got the synchronization between panes and the stream to the Customer Detail pane working, they "attached" the *Customers* dictionary to the `SalesCom` application by a quick rework to the `customers` method:

`customers`

"Return the keys of a Customers dictionary as a Sorted Collection."

`^Customers keys asSortedCollection`

And an enhancement of `viewCust`: was all that was needed to bring the Customers Dictionary "on-line":
```pascal
viewCust: aCust
    "Change: method response for the Customer List pane.
    It updates the Customer Detail pane to show the
currently selected customer."
| custData custHist prepDate |
curCust := aCust.
custData := Customers at: curCust.
custDetPane cancel.
    1 to: custData size - 1 do: [:index |
        replyStream
            nextPutAll: (custData at: index);
        cr].
custHist := custData at: 7. "Now print the history"
    replyStream nextPutAll: '----------HISTORY----------'; cr.
    1 to: custHist size do: [:index |
        replyStream
            nextPutAll: (custHist at: index);
        cr]
```

You can edit the methods to reflect these changes or paste them in from the tutorial chapter.12 file. Once these two methods are changed, you have reached another plateau in the SalesCom prototype development project. Both the Customers and Communication Events databases are up and accessible interactively in the SalesCom window as in Figure 12.6. Evaluate the expression Test open to see it.

<figure><img src="image_54.png" /><figcaption>Figure 12.6 SalesCom Window Phase Two</figcaption></figure>
Menu selections foreshadow but don't yet deliver the full **SalesCom** environment. Menu selections result in **message not understood** walkbacks. The rest of the prototype development consisted of writing small methods to perform the actions requested by each of the menu items.

## It's Getting Better All the Time: Evolutionary Development

The reps were on a roll. Every method they wrote gave them new insights and ideas for the next. They found that **Smalltalk/V** lent itself to "team development" as they took turns, one on the keyboard, the other scouring the manual. And by experience, they came up with another tip.

**Survival Tip #7:** Keep moving. Write the methods that "jump out at you" and practically write themselves. If you get stuck on a method, put its body in comment quotes and save it. Then go on to the next method you need to write. Something you pick up solving another method's implementation or sometimes just some "creative gestation" will bring you back to unfinished methods. Eventually the toughest methods seem to give way when they are the only thing standing between you are your application running.

To see what the reps were able to accomplish with **SalesCom** on their maiden **Smalltalk/V** development project, evaluate:

(File pathname: 'class12.3in') fileIn.
Test open

While they have a long wish list, the reps now had a prototype that met their original **SalesCom** development objective, see Figure 12.7. With the pop-up mail merge and phone call script windows, **SalesCom** was a lot more sophisticated a program than the reps ever throught they could have written.

Take a few minutes to take **SalesCom** on a shake-down cruise. Add a customer and update somebody's contact information. Print a few letters. Make a phone call or two. Push that initial bunch of prospects through a hypothetical sales campaign. Sell a couple of WORMS. Lose a couple to the Dead Leads bin.

Notice how the node highlighting pinpoints the position of the selected customer in the marketing campaign. Track how printing a letter or making a call updates both the customer and communication event records.

To see what makes **SalesCom** tick, poke around the methods which you have just filed in. Note that while a method like `viewCust:` now looks complex with all the data handling that has found its way in to support the additional interface features, `viewCust:` became complex in stages. The reps wrote simple methods that became more sophisticated a line or two at a time.
<figure><img src="image_55.png" /><figcaption>Figure 12.7
Full SalesCom
Prototype</figcaption></figure>

For the record, over the course of the prototype development project, the reps were able to get all the behavior you see exhibited in SalesCom by:

* creating two new classes, and

* writing about thirty new methods.

The reps began using SalesCom the day after they finished the prototype. They had a long list of anticipated changes but they found that use helped focus their subsequent development. Rather than add features that they thought would be useful, they wrote those that their daily use convinced them they needed.

## Where to Go from Here

Like many Smalltalk/V applications, SalesCom begins an extension. While the prototype was fully functional and positively impacted the reps sales performance, over the next few months they intend to do the following:

* Add batch printing of daily correspondence, including envelope printing, to the mail merge capabilities of SalesCom.

* Make the network of strategy nodes dynamic so exits of one strategy network lead to the top of other strategic clusters of communication event nodes.

* Add a management report window to show month and year to date totals, sales total, and "body counts" of customers in each node of the network as well as compute the average days delay between one event and another, etc.

* Use Smalltalk/V Goodies #2 to use the FieldPane class to create good looking, easy to use Customer and Communication Event data entry and editing interfaces. This will allow users to "tab" from field to field rather than using the SalesCom prototype Inspectors and Prompters.
To take SalesCom to an even higher level of sophistication, the reps (or you) might consider extensions into the realm of discrete event simulation and expert systems:

* Use the multi-tasking capabilities provided by the **Process** and **Semaphore** classes of **Smalltalk/V** to add an Event Driven Simulation component to **SalesCom**. Such an extension would take the **SalesCom** databases' current state and use what it knows about response tendencies to generate simulation data to evaluate future impacts of current marketing decisions.

* Use the backward chaining inference capabilities available from the logic programming extensions of the **Prolog** class supplied as an example program to create a **SalesCom** Expert System. Based on sales management "rules", the **SalesCom** Expert System should be able to analyze data from **SalesCom** Event Driven Simulation runs. The result of this analysis would be a series of recommendations as to how to adjust sales activity now to avoid bottlenecks and "feast or famine" cycles in the future.

As you can see, the potential of **SalesCom** is great and will be reached by a process of evolutionary development. From prototype to bigger and better. With **Smalltalk/V** the limits are those of your imagination and the time and energy you have for development.

## What You've Now Learned

In the last chapter, we introduced the **Smalltalk/V** application development cycle. In this chapter, we've shared a number of tips to help speed your application development:

* Don't over-plan. Know enough to get going, then do. **Smalltalk/V** is designed to work with you in an evolving, incremental process.

* When on cut and paste "raids" of existing methods, turn source class instance variable references into temporary variable assignments until you determine the destination class' data structure requirements.

* When re-working a copied method, edit the "comment" first. Then let the changes made to the comment point the way to lines in the method source code which implement those aspects of the method whose description in the comment has changed. And remember, comments are a critical means of communication among **Smalltalk/V** programmers.

* Simplified, interim versions of methods are an excellent way to "divide and conquer" a **Smalltalk/V** programming problem. Often this means writing a return expression which is a "hardwired" instance of the type of message a method will return once its more complex computation is written.

* Use the **Encyclopedia of Classes in Part 4 of the Smalltalk/V manual**. And use the **Methods Index in the Appendices** to direct your cross-referencing exploration of **Smalltalk/V** classes and their methods.
* Keep moving. Keep your productivity high by writing the "easy ones" first. Very often the act of writing other methods or incubating on a tough one will result in insights to conquer almost any Smalltalk/V programming problem.

* Know how to bounce back from errors, like using Scheduler reinitialize to kill hard-to-find instances.

Keep these tips in mind as you tackle your first Smalltalk/V programming project. The remaining three sections of this manual are provided as reference tools. Part 3, The Smalltalk/V 286 Reference, covers in greater detail all the material explored in these tutorials. Part 4, The Encyclopedia of Classes, exhaustively describes the classes in the Smalltalk/V environment, including each classes' instance and class variables and methods. The Appendices contain a variety of sections on advanced and special topics as well as that ever-so-important Methods Index.

If you want to review any of the topics covered in this tutorial, you can either repeat the corresponding section of the tutorial, or refer to the detailed description in Part 3.
<figure><img src="image_56.png" /></figure>
Part 3

Smalltalk/V 286
Reference
# 13 THE SMALLTALK LANGUAGE

Smalltalk is easy to learn and use because it has simple syntaxes and semantics, and few concepts. The concepts *object*, *class*, *message*, and *method* form the basis of programming in Smalltalk. The methodology for using Smalltalk consists of:

* Identifying the objects appearing in the problem and its solution.

* Classifying the objects according to their similarities and differences.

* Designing messages which make up the language of interaction among the objects.

* Implementing methods which are the algorithms that carry out the interaction among objects.

## Objects

Objects are self-describing data structures. Every object is an instance of a class. The class of an object is determined by sending it the message *class*. This message is understood by all objects.

Objects are protected data structures. The data stored inside of an object is accessible only through messages. Objects can also be shared.

The word *self* in a method refers to the receiver object of the message that invokes the method.

## Variables

All Smalltalk *variables* are containers for objects. A variable contains a single object pointer.

The variable name can be used in an expression to refer to the object whose pointer it contains. A variable may contain different object pointers at different times. The object pointer contained in a variable changes when an assignment expression is evaluated. An assignment makes a copy of a pointer to an object, not a copy of the object itself.

Variables are either *private* or *shared*. Private variables are accessible only to a single object. Shared variables are accessible to multiple objects. A private variable has a lower-case first letter, while a shared variable has an upper-case first letter.

There are three kinds of variables:

1. *Instance variables* are the component parts of an object. They exist for the lifetime of the object.
2. *Temporary variables* are created during the activation of a method. They exist for the lifetime of the method activation.

3. *Shared variables* are shared by many objects. They exist until explicitly deleted.

## Instance Variables

Each object maintains its own internal state. The private memory of an object consists of its individually accessible components called *instance variables*. Instance variables are similar to fields of a record structure in other languages. Instance variables either have a name or are referred to with an integer index. *Named instance variables* are accessed by using their name. *Indexed instance variables* are accessed only through messages (usually using `at:put` with integer indices). Each member of a class has its own separate instance variables.

Instance variables have a type—they contain either *pointers* or *bytes*. All instance variables for objects belonging to the same class are the same type. Most objects' instance variables contain pointers. The pointers refer to objects. If an object contains bytes, then its instance variables contain eight-bit values representing elementary data values.

Classes may specify both *named* and *indexed* instance variables for their member objects. The number and names of named instance variables are fixed for all members of the class. The number of instance variables may differ among members of the same class. For example, `#(1 2 3)` and `#('up' 'down')` are both objects of class *Array*, but they have different numbers of indexed instance variables, 3 and 2 respectively. A class with indexed instance variables creates new members with a message that specifies the number of indexed instance variables to create (usually the message `new:` with an integer argument). Many objects return their number of indexed instance variables in response to the message `size`.

Only the instance variables of the receiver of the message that invoked the method can be referred to by name.

## Temporary Variables

*Temporary variables* include *method arguments* and *method temporaries*, and contained *block arguments*.

Method arguments are assigned the associated message arguments for the message which caused method invocation. Method temporaries are initialized to nil upon method invocation. Block arguments are assigned the associated message arguments for the
**value:** message at the time the block is activated. When a block is invoked while its containing method is still active, the block and the containing method share the same temporary variables.

## Shared Variables

*Shared variables* are defined in dictionaries called *pools*. Different kinds of shared variables are defined in different kinds of pools. All shared variable names start with an upper-case letter. The variable name and the variable value are bound together into an object that is an instance of class *Association*. This association object is placed in the pool.

The System Dictionary *Smalltalk* is a pool which contains all the global variables.

Global variables are accessible from every object.

Class variables for each class are implicitly collected into a pool for the class. Class variables are defined as part of the class specification. Class variables are accessible only to the class, subclasses, instances of the class, and instances of the subclasses.

Pool variables are contained in named pool dictionaries that you explicitly construct. Pool dictionaries are global variables. To make pool variables accessible to a class and its instances, you must modify the class specification.

## Classes

*Classes* are the program modules of Smalltalk because they describe data structures (objects), algorithms (methods), and external interfaces (message protocol). Classes provide complete capabilities to solve a particular problem.

Every object is an instance of some class. All objects which are instances of a class are similar because they have the same structure (i.e., the same instance variables), the same messages to which they respond, and the same available methods.

Classes are also objects contained in global variables which are maintained in the System Dictionary *Smalltalk*. As such, class names begin with a capital letter. This allows classes to be referred to in an expression.
## The Class Hierarchy

Classes form a hierarchy, consisting of a root class, called **Object**, and many subclasses. Each class inherits the functionality of all its superclasses in the hierarchy. **Class Object** provides the common behavior for all objects. It includes methods for printing an object symbolically, for testing the class of an object, and for making a copy of an object. Each subclass builds on its superclasses by adding its own methods and instance variables to complete the implementation of the subclass's behavior.

The complete Smalltalk/V class hierarchy is shown on the next page. Indentation is used to show subclass relationships.
### Object

#### Behavior

Class

MetaClass

#### BitBlt

CharacterScanner

Pen

Animation

Commander

#### Boolean

False

True

#### ClassBrowser

#### ClassHierarchyBrowser

#### ClassReader

#### Collection

Bag

IndexedCollection

FixedSizeCollection

Array

CompiledMethod

Bitmap

ByteArray

FileHandle

Interval

String

Symbol

OrderedCollection

Process

SortedCollection

Set

Dictionary

IdentityDictionary

MethodDictionary

SystemDictionary

SymbolSet

#### Compiler

LCompiler

#### Context

HomeContext

#### CursorManager

NoMouseCursor

#### DeletedClass

#### DemoClass

#### Directory

#### DiskBrowser

#### Dispatcher

GraphDispatcher

PointDispatcher

ScreenDispatcher

ScrollDispatcher

ListSelector

TextEditor

PromptEditor

TopDispatcher

DispatchManager

DisplayObject

DisplayMedium

Form

BiColorForm

ColorForm

DisplayScreen

ColorScreen

DOS

File

Font

Icon

InputEvent

Inspector

Debugger

DictionaryInspector

Magnitude

Association

Character

Date

Number

Float

Fraction

Integer

LargeNegativeInteger

LargePositiveInteger

SmallInteger

Time

Menu

Message

Pane

SubPane

GraphPane

ListPane

TextPane

TopPane

Pattern

WildPattern

Point

ProcessScheduler

Prompter

Rectangle

Semaphore

Stream

ReadStream

WriteStream

ReadWriteStream

FileStream

TerminalStream

StringModel

TextSelection

UndefinedObject
## Inheritance

A class inherits all of its superclasses' instance variables, class variables, and methods. Inheritance of class variables allows the methods of a class to refer to the class variables defined in its superclasses.

Inheritance of instance variables allows the methods of a class to refer to the instance variables defined in its superclasses, but it also means that superclass instance variables are included in objects which are instances of the class.

Determining what method to perform starts with two pieces of information: the message selector and the class of the receiver of the message.

First, the available methods for the class of the receiver are examined to see if there is a method which matches the message selector. If so, that method is performed. If not, the superclass of the class of the receiver is used, and the check for a method matching the selector is performed again. This checking for a matching method and advancing to the superclass is repeated until the method is found or until the end of the superclass chain is reached. In the latter case, a programming error occurs, and a message which describes the error is sent to the receiver of the original message.

There is a special syntax form for a receiver, **super**, which changes the initial class used for message lookup. The word **super** has two implications.

1. It represents the same object as **self** does, the receiver of the message which caused the method containing **super** to be performed.

2. It causes message lookup to start in the superclass of the class containing the method in which **super** appears, rather than starting in the class of the receiver.

The major purpose of a message to **super** is to be able to use a method in a superclass which is redefined in a subclass.

## Class Messages

Messages to class objects are used for creating instances of the class and for initializing class variables. the most common messages for creating new instances are **new** and **new**:.
Some classes define their own messages for creating instances.

Like all objects, classes know to which messages they can respond. For other objects, the methods available are determined by the object's class. Class objects, too, belong to a "class," called a *metaclass*, which determines the messages to which the class can respond.
There are three important classes relating to metaclasses:

1. **Metaclass**—the class of all metaclasses.

2. **Class**—the superclass of all instances of **Metaclass**.

3. Every metaclass has exactly one instance: the class of which it is the metaclass.

## Specifying a New Class

In order for you to add a new class, you first choose a superclass on which you will build. Make the new class a subclass of the chosen superclass, then add the instance variables and methods necessary to complete the new class's functionality. Classes are normally specified using a Class Hierarchy Browser. The following describes the information which defines a class.

Classes are defined by sending a message to the new or modified class's superclass with class specification information as arguments. The class information that can be specified is the following:

* The class name

* Whether objects of the class contain pointers or bytes

* Whether objects of the class can contain indexed instance variables

* The names of the named instance variables for objects of the class

* The names of the class variables available to all objects of the class

* The names of the pool dictionaries which define shared variables available to objects of the class and possibly other classes

The message which specifies a class is sent to its superclass. There are three class definition messages. They are as follows:

1) **subclass**: subclassSymbol

instanceVariableNames:

instanceVariableNameString

classVariableNames: classVariableNameString

poolDictionaries: poolDictionaryNameString

2) **variableSubclass**: subclassSymbol

instanceVariableNames:

instanceVariableNameString

classVariableNames: classVariableNameString

poolDictionaries: poolDictionaryNameString

3) **variableByteSubclass**: subclassSymbol

classVariableNames: classVariableNameString

poolDictionaries: poolDictionaryNameString
The first two messages define classes whose member objects contain pointers. The first message specifies objects with named instance variables (zero or more of them). The second message specifies objects with both named and indexed instance variables.

The third message defines classes whose member objects contain bytes. Objects with bytes contain only indexed instance variables, so there is no instance variable name string argument. Objects with bytes define elementary data values such as strings of characters.

## Messages and Methods

All processing in a Smalltalk system involves sending messages to objects. Messages are the language of interaction which you use in order to express your computing requirements to objects. Messages request services from an object in terms of its external interface.

Methods are the algorithms which are performed by an object in response to receiving a message. Methods represent the internal details of the implementation of an object.

Protocol definitions for a class always have two parts—*class methods* and *instance methods*.

*Class* methods implement the messages sent to the class. The receiver of a class message is always the class object, not an instance of the class. All classes are global variables and can be referred to by their names.

*Instance* methods implement messages sent to instances of the class. The receiver of an instance message is always an object that is an instance of the class.

A method contains a sequence of Smalltalk expressions. There are four types of expressions:

1. Literals:

`#aSymbol # (1 2 4 16) 'magic'`

2. Variable names:

`Smalltalk x replacementCollection`

3. Message expressions:

`bag add: stream next`

100 factorial

array at: index + 10 put: Bag new
### 4. Blocks of code:

`[ :x :y | x name < y name ]`

The beginning of a method defines its name, arguments, and any temporary variables that it uses.

Sending a message involves:

1. Identifying the object to which the message is sent (the receiver of the message).

2. Identifying the additional objects that are included in the message (the arguments).

3. Specifying the desired operation to be performed (the message selector).

4. Accepting the single object that is returned as the message answer.

The following sections present the syntax of methods and messages both informally with examples and more precisely using a syntax metalanguage. The metalanguage definition appears in Appendix 1. If you find that the informal presentation is sufficient, you can skip over the syntax rules. A complete syntax summary and cross reference are also presented in Appendix 1.

## The Syntax of Variable Names and Literals

Variable names and literals are the elemental building blocks used in higher-level syntax forms in Smalltalk.

### Variable Names

A variable name identifies a variable in an object. A variable is a container for an object pointer. A variable name is a sequence of letters and digits, beginning with a letter. Example variable names are:

`OrderedCollection aString elements x2`

Variable names beginning with an upper case letter represent shared variables, while those beginning with a lower-case letter represent private variables. The rules for variable names are:

`<rule> variableName = identifier.`

`<rule> identifier = letter {letter | digit}.`
## Literals

A literal defines an object of class Number, String, Character, Symbol, or Array. Examples are given below where each of the possible literal forms is defined. The syntax rule for literals is:

`<rule> literal = number | string | characterConstant | symbolConstant | arrayConstant.`

## Numbers

Numbers are objects of class Float, Fraction, or Integer. If a number contains a decimal point, it is an object of class Float. If it contains a negative exponent and no decimal point, it belongs to class Fraction. All other numbers belong to class Integer. If the number includes r, the digits preceding r define the number radix. In this case, capital letters are used to represent digit values greater than 9, with A = 10, B = 11, etc. Example numbers are:

15 16rFF 3.1416 1e-3 -100

The rules for numbers are:

`<rule> number = [digits "r"] ["-"] bigDigits ["."bigDigits] ["e" ["-"] digits].`

`<rule> digits = digit {digit}.`

`<rule> digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".`

`<rule> bigDigits = bigDigit {bigDigit}.`

`<rule> bigDigit = digit | capitalLetter.`

## Strings

A string is a sequence of characters enclosed in apostrophes. It is an object of class String which is a sequence of objects of class Character that can be indexed. Strings are not necessarily constant; their characters may be changed by sending a message to the string. Paired apostrophes within a string reduce to a single apostrophe in the resultant string object. Example strings are:

`'hello' " 'isn"t' ' "comment in string" '`

The rules for strings are:
`<rule> string = "" {character | "" | "" } "".`

`<rule> character = letter | digit | selectorCharacter | "[" | "]" | "(" | ")" | "^" | "" | "$" | "#" | ":" | ".." | "{" | "}" | "".`

`<rule> selectorCharacter = "," | "+" | "/" | "\" | "*" | "~" | ">" | "<" | "=" | "@" | "%" | "|" | "&" | "?" | "".`

## Comments

A comment is a sequence of characters enclosed in double quotes. A comment is ignored anywhere within a method, except when occurring within a string. Example comments are:

"Answer the size of the receiver" "goodBye"

The rule for comments is:

`<rule> comment = "" {character | "" } "".`

## Character Constants

A character constant is an object of class Character. A character constant appears as a dollar sign followed by any character. Example character constants are:

$$ \$ \$a \$' \$ \$. $$

The rule for character constants is:

`<rule> characterConstant = "$" character | "$" "'" | "$" "'".`

## Symbols

A symbol is an object of class Symbol, a sequence of objects of class Character which can be indexed. Symbols differ from strings in that their characters may not be changed. A symbol constant identifies the associated symbol object. The form of a symbol constant is a number sign, #, followed by the characters of the symbol. Example symbol constants are:

\# + \#asOrderedCollection \#at:put: \#==

The rules for symbols and symbol constants are:
```
<rule> symbolConstant = "#" symbol.
<rule> symbol = unarySelector | binarySelector | keyword {keyword}.
<rule> unarySelector = identifier.
<rule> binarySelector = selectorCharacter [selectorCharacter] | "-".
<rule> keyword = identifier ":".
```

## Arrays

An array is an object of class Array which may be indexed by an integer from one through the size of the array. An array is a series of literals enclosed in parentheses. An array constant identifies the associated array object. It consists of an array preceded by a number sign. Example array constants and arrays are:

```
#('red' 'blue' 'green')
#(yes no)
#(1 'two' three $4 (5))
```

The rules for arrays and array constants are:

```
<rule> arrayConstant = "#" array.
<rule> array = "(" {number | string | symbol | array | characterConstant} ")".
```

## Expression Syntax

The actions in a method are specified by a series of expressions separated by periods. A period is optional after the last expression of the series. Each expression computes a single object as its result. The expression may also include assignment of its result to one or more variables.

The final expression in an expression series may be preceded by a caret, `^`. The caret means that method execution terminates and answers the object computed by the expression.

The rules for expressions and expression series are:

```
<rule> expressionSeries = {expression "."} [["^"] expression].
<rule> expression = {variableName ":="} (primary | messageExpression {";" cascadeMessage}).
<rule> primary = variableName | literal | block | "(" expression ")".
```

A message expression is a request to an object (the receiver of the message) to perform a computation and return an object as the answer. There are three kinds of message expressions: `unary`, `binary`, and `keyword` (n-ary). Each has a different precedence and a different syntax for its selector, the name of the message.
A unary expression sends a series of unary messages which are evaluated from left to right. A unary message has no arguments.

A binary expression sends a series of binary messages which are evaluated from left to right. A binary message has a single argument following the binary selector. The traditional arithmetic operators are implemented in Smalltalk using binary expressions. This gives all arithmetic operators the same precedence. Parentheses may be used to specify other than left-to-right evaluation.

A keyword expression sends a single keyword message with one or more arguments. The arguments to a keyword message are evaluated from left to right.

The selector of a keyword message is the concatenation of all the keywords in the message.

Unary expressions have highest precedence, followed by binary and then keyword. Parentheses may be used to specify a different evaluation order.

Cascaded messages are a series of messages to the same receiver. Each message after the first is preceded by a semicolon.

The rules for message expressions are:

<rule> messageExpression = unaryExpression | binaryExpression | keywordExpression.

<rule> cascadeMessage = unaryMessage | binaryMessage | keywordMessage.

<rule> unaryExpression = primary unaryMessage {unaryMessage}.

<rule> binaryExpression = (unaryExpression | primary) binaryMessage {binaryMessage}.

<rule> keywordExpression = (binaryExpression | primary) keywordMessage.

<rule> unaryMessage = unarySelector.

<rule> binaryMessage = binarySelector (unaryExpression | primary).

<rule> keywordMessage = keyword (binaryExpression | primary) | {keyword (binaryExpression | primary)}.

## Blocks

A *block* is a part of a method enclosed in square brackets. It is an object describing executable code. Blocks may be nested.

A block may have arguments. These are specified between the left bracket and vertical bar by preceding each block argument variable name with a colon.
The result of block execution is the final expression in the block. A block with no arguments is executed by sending it the message **value**.

A block with one argument is executed by sending it the message **value**: The argument to the **value**: message is assigned to the block argument upon block execution.

A two-argument block is executed by sending it the message **value**: **value**: arguments are assigned to the block arguments.

A block may contain an expression preceded by a caret, `^`. Evaluation of such an expression causes termination of execution for both the block and the method in which the block appears.

Blocks are the basis for control structures in Smalltalk. Since control structures conform to keyword message syntax, control structures have no special syntax.

The rule for blocks is:

`<rule> block = "[" [{" : variableName} "|] expressionSeries "]".`

## Method Syntax

A complete *method specification* includes a message pattern, optional primitive number, optional temporaries, and an expression series. The message pattern specifies how to send a message to request method execution. It includes the method selector and the variable names used to refer to arguments within the method.

The rules for method syntax are:

`<rule> method = messagePattern [primitiveNumber] [temporaries] expressionSeries.`

`<rule> messagePattern = unarySelector | binarySelector variableName | keyword variableName {keyword variableName}.`

`<rule> primitiveNumber = "<" "primitive:" number ">".`

`<rule> temporaries = "|" {variableName} "|".`

## Control Structures

Control structures are invoked by sending messages with blocks as arguments. Three forms, with several variations, are predefined in the Smalltalk language. You may define additional forms in Smalltalk using these predefined ones.
## Conditional Execution

The following predefined conditional execution messages are available:

ifTrue: aBlock

ifFalse: aBlock

ifTrue: trueBlock ifFalse: falseBlock

ifFalse: falseBlock ifTrue: trueBlock

In all cases, the receiver expression must be of class **Boolean** and the arguments must be blocks with no arguments. The **ifTrue:** argument block (if present) is sent the message **value**, if and only if, the receiver has the value **true**. The **ifFalse:** argument block (if present) is sent the message **value**, if and only if, the receiver has the value **false**. The answer of the conditional messages is the last expression in the executed block or nil if no block is executed.

## Iterative Execution

The following predefined iterative execution messages are available:

whileTrue: aBlock

whileFalse: aBlock

Both the receiver and argument of these messages must be no-argument blocks. For **whileTrue:**, the receiver block is sent the message **value**. If it answers true, the argument block is sent the message **value**. The iteration continues until the answer of the first block evaluation is false. For **whileFalse:**, the sequence is the same but the iteration continues until the answer of the first block evaluation is true. The answer of both **whileTrue:** and **whileFalse:** is always nil.

## Short Circuit Boolean Evaluation

The following predefined boolean operators are available:

and: aBlock

or: aBlock

The receiver of each of these methods must be of class **Boolean** and the argument must be a block. For **and:**, if the receiver is true, the block is sent the message **value**, and the answer of the message is the last block expression. If, however, the receiver of the **and:** message is false, the answer is false, and the block is not evaluated. For **or:**, if the receiver is false, the block is sent the message **value**, and the answer of the message is the last block expression. If, however, the receiver of the **or:** message is true, the answer is true, and the block is not evaluated.
This image does not contain any text. It appears to be a blank page or a drawing.
# 14 SMALLTALK/V 286 CLASSES

This section describes the major Smalltalk/V classes. These classes serve as the basic building blocks for your applications.

## Magnitudes

The magnitude classes are the easiest to understand and the most frequently used. They define objects that can be compared, measured, ordered, and counted. These include characters, numbers, dates, and times. Many useful messages for comparing, testing, and ordering these objects are defined. The arithmetic operators and many useful numerical functions are also defined as messages understood by the numerical magnitude objects.

This chapter presents a quick overview of each of the magnitude classes provided in Smalltalk/V. Examples are used to demonstrate some of the functionality provided. Part 4: The Encyclopedia of Classes gives a detailed specification of each of the magnitude classes.

The Magnitude class hierarchy shown below lists all of the magnitude classes.

Magnitude

Association

Character

Date

Number

Float

Fraction

Integer

LargeNegativeInteger

LargePositiveInteger

SmallInteger

Time
## Magnitude

All of the magnitude classes are subclasses of the abstract class **Magnitude**. Class **Magnitude** provides the comparing and ordering protocol inherited by its subclasses. All magnitudes support comparing, ordering, and interval testing. **Magnitude** assumes its subclasses implement the ordering relation and comparison methods: `=`, `<=`, `>=`, `<`, `>`, `~=`. Based on these methods, **Magnitude** provides generic methods for interval testing and max/min computation inherited by all **magnitude** classes. Some numerical examples are:

<table><thead><tr><td>Expression</td><td>Answer</td></tr></thead><tbody><tr><td>46 &gt; 33</td><td>true</td></tr><tr><td>46 min: 33</td><td>33</td></tr><tr><td>46 max: 33</td><td>46</td></tr><tr><td>5/4 between: 0.5 and: 1</td><td>false</td></tr></tbody></table>

## Character

The instances of class **Character** are the extended ASCII character set from ASCII value 0 to ASCII value 255. Characters are pre-existing objects in Smalltalk, hence they do not have to be created. References to characters are made in two ways: as literals or by converting integers into the corresponding ASCII character. There are two conversion messages. The message **asCharacter** can be sent to an integer, or the message **value**: with an integer argument can be sent to class **Character**. For example:

<table><thead><tr><td>Character</td><td>Literal</td><td>Equivalent Expression</td></tr></thead><tbody><tr><td>A</td><td>$A</td><td>65 asCharacter</td></tr><tr><td>B</td><td>$B</td><td>66 asCharacter</td></tr><tr><td>C</td><td>$C</td><td>Character value: 67</td></tr><tr><td>space</td><td>$</td><td>32 asCharacter</td></tr><tr><td>line feed</td><td></td><td>10 asCharacter</td></tr><tr><td>tab</td><td></td><td>Character value: 9</td></tr></tbody></table>

Like all subclasses of **Magnitude**, the class **Character** must define how characters are compared and ordered. The methods `<`, `<=`, `=`, `>=`, `>`, and `~=` compare characters by comparing their ASCII values. For example:

<table><thead><tr><td>Expression</td><td>Answer</td></tr></thead><tbody><tr><td>$a = $A</td><td>false</td></tr><tr><td>$A &lt; $B</td><td>true</td></tr></tbody></table>
The interval testing and min/max methods are inherited from class Magnitude automatically:

<table><thead><tr><td>Expression</td><td>Answer</td></tr></thead><tbody><tr><td>69 asCharacter max: $A</td><td>$E</td></tr><tr><td>$x between: $a and: $t</td><td>false</td></tr></tbody></table>

Class Character has many testing and conversion methods. Some examples follow:

<table><thead><tr><td>Expression</td><td>Answer</td></tr></thead><tbody><tr><td>$a isUppercase</td><td>false</td></tr><tr><td>$a isLowercase</td><td>true</td></tr><tr><td>$a asUppercase</td><td>$A</td></tr><tr><td>$? asLowercase</td><td>$?</td></tr><tr><td>$e isVowel</td><td>false</td></tr><tr><td>$+ isLetter</td><td>false</td></tr><tr><td>$9 isDigit</td><td>true</td></tr><tr><td>$A asciiValue</td><td>65</td></tr></tbody></table>

## Date and Time

Instances of class Date represent specific dates such as January 1, 1980 or September 15, 1876. Instances of class Time represent specific times of the day such as 10 am or 12:15 pm.

Dates and Times are created by evaluating expressions. The detailed descriptions of classes Date and Time in Part 4 give a complete list of the messages supported by Date and Time. Some examples are:

Time now

Date today

'20 January 1950' asDate

Date newDay: 20 month: #Jan year: 1950.

The following code makes an instance of class Date and puts it in the global variable Birthday:

Smalltalk at: #Birthday put: '4 August 1976' asDate
Ordering and comparing of dates and times are supported. Some examples of messages supported by dates are:

<table><thead><tr><th>Expression</th><th>Answer</th></tr></thead><tbody><tr><td>Birthday year</td><td>1976</td></tr><tr><td>Birthday dayName</td><td>Wednesday</td></tr><tr><td>Birthday &gt; Date today</td><td>false</td></tr><tr><td>Birthday min: Date today</td><td>'August 4, 1976'</td></tr><tr><td>Birthday previous Weekday: #Saturday</td><td>'July 31, 1976'</td></tr><tr><td>Birthday daysLeftInYear</td><td>149</td></tr><tr><td>Birthday daysInYear</td><td>366</td></tr></tbody></table>

You can add new ways of creating objects by defining new methods. If you add the following method as a class method in class Time:

hour: hours minute: minutes seconds: seconds

"Answer an instance of class Time as specified"

self fromSeconds:

$$((hours * 60) + minutes) * 60) + seconds$$

then you can create instances of class Time using expressions like the following:

Smalltalk at: #LunchTime
put: (Time hour: 12 minute: 0 second: 0).

Smalltalk at: #DinnerTime
put: (Time hour: 18 minute: 45 second: 0).

Smalltalk at: #BreakfastTime
put: (Time hour: 7 minute: 30 second: 0).
Some examples using these new global variables are:

<table><thead><tr><th>Expression</th><th>Answer</th></tr></thead><tbody><tr><td>LunchTime<br>between: BreakfastTime<br>and: DinnerTime</td><td>true</td></tr><tr><td>LunchTime min:<br>DinnerTime</td><td>12:00:00</td></tr><tr><td>DinnerTime hour</td><td>s18</td></tr><tr><td>LunchTime &lt;<br>BreakfastTime</td><td>false</td></tr></tbody></table>

## Number

Smalltalk supports three kinds of numbers: floating point (class Float), rational (class Fraction), and integer (class Integer and its subclasses). The methods of class Number define the general behavior of its subclasses, support mixed mode arithmetic, and provide many useful numeric, testing, and iteration functions.

Number defines the arithmetic protocol that its subclasses must implement. These are the usual binary arithmetic operators: `+`, `-`, `*`, `/`. There is equal precedence between all binary operators, so evaluation is left to right. Some examples:

<table><thead><tr><th>Expression</th><th>Answer</th></tr></thead><tbody><tr><td>3 + 4</td><td>7</td></tr><tr><td>3 + 4 * 2</td><td>14</td></tr><tr><td>2 + 4 / 12</td><td>1/2</td></tr></tbody></table>

Number implements many numerical methods that its subclasses can inherit such as: exp, cos, arcSin, tan, ln, sqrt, floor, reciprocal. Some examples:

<table><thead><tr><th>Expression</th><th>Answer</th></tr></thead><tbody><tr><td>7.5 floor</td><td>7</td></tr><tr><td>4 reciprocal</td><td>1/4</td></tr><tr><td>-2.3 abs</td><td>2.3</td></tr></tbody></table>
Number implements many testing methods inherited by its subclasses such as: even, positive, strictlyPositive. Some examples:

<table><thead><tr><td>Expression</td><td>Answer</td></tr></thead><tbody><tr><td>4 even</td><td>true</td></tr><tr><td>0.1 positive</td><td>true</td></tr><tr><td>0 strictlyPositive</td><td>false</td></tr></tbody></table>

Number implements methods for creating other kinds of objects, such as:

<table><thead><tr><td>Expression</td><td>Answer</td></tr></thead><tbody><tr><td>2 @ 7</td><td>A Point with x coordinate of 2 and y coordinate of 7</td></tr><tr><td>1/4 to: 3/4 by: 1/8</td><td>An Interval containing the fractions 1/4, 3/8, 1/2, 5/8, 3/4</td></tr></tbody></table>

Number also implements iteration methods, such as:

1/4 to: 1.5 by: 1 do: [:i| Transcript space; nextPutAll: i printString; cr]

which prints the numbers 1/4 and 5/4 in the Transcript window.

Smalltalk/V supports mixed mode arithmetic so that arithmetic expressions can be composed of different kinds of numbers. Executing sample expressions is the best way to understand the conversion rules.

<table><thead><tr><td>Expression</td><td>Answer</td><td>Comment</td></tr></thead><tbody><tr><td>1 + 2</td><td>3</td><td></td></tr><tr><td>5.1 - 3</td><td>2.1</td><td>Note space between - and 3</td></tr><tr><td>2 * -4.0</td><td>-8.0</td><td>Mixed mode gives a Float</td></tr><tr><td>2/4</td><td>1/2</td><td>A fraction</td></tr><tr><td>1/2 + 1</td><td>3/2</td><td>Mixed mode gives a Fraction</td></tr><tr><td>1/2 + 1.0</td><td>1.5</td><td>Mixed mode gives a Float</td></tr><tr><td>4/2</td><td>2</td><td>Fraction reduces to an integer</td></tr></tbody></table>
The following examples explain many of the messages that can be used with numbers.

<table><thead><tr><th>Expression</th><th>Answer</th><th>Comment</th></tr></thead><tbody><tr><td>4 // 3</td><td>1</td><td>Integer quotient</td></tr><tr><td>-4 // 3</td><td>-2</td><td>Truncate toward minus infinity</td></tr><tr><td>4 \ 3</td><td>1</td><td>Integer remainder</td></tr><tr><td>-4 \ 3</td><td>2</td><td>Integer remainder, truncates as //</td></tr><tr><td>-4 quo: 3</td><td>-1</td><td>Integer quotient, truncate toward zero</td></tr><tr><td>-4 rem: 3</td><td>-1</td><td>Integer remainder, truncate toward zero</td></tr><tr><td>-2.3 abs</td><td>2.3</td><td>Absolute value</td></tr><tr><td>10 negated</td><td>-10</td><td></td></tr><tr><td>11 reciprocal</td><td>1/11</td><td>A fraction</td></tr><tr><td>2 + 3 * 4</td><td>20</td><td>Evaluation is left to right</td></tr><tr><td>3 - (2 * 2)</td><td>-1</td><td>Parentheses change evaluation order</td></tr><tr><td>2 + 3 negated</td><td>-1</td><td>Unary operator (negated) done first</td></tr><tr><td>6 quo: 2 + 1</td><td>2</td><td>Keyword operator (quo:) done last</td></tr><tr><td>2 sqrt</td><td>1.414236</td><td>Square root</td></tr><tr><td>4 sqrt</td><td>2.0</td><td>Answer always float</td></tr><tr><td>2.1 squared</td><td>4.41</td><td>Receiver times itself</td></tr><tr><td>2.3 even</td><td>true</td><td></td></tr><tr><td>2 odd</td><td>false</td><td></td></tr><tr><td>10 negative</td><td>false</td><td></td></tr><tr><td>0 positive</td><td>true</td><td>True if &gt;= 0</td></tr><tr><td>0 strictlyPositive</td><td>false</td><td>True if &gt; 0</td></tr><tr><td>-0.1 sign</td><td>-1</td><td></td></tr><tr><td>0 sign</td><td>0</td><td></td></tr><tr><td>100 sign</td><td>1</td><td></td></tr><tr><td>5.1 ceiling</td><td>6</td><td>Nearest integer greater than or equal</td></tr><tr><td>-5.1 ceiling</td><td>-5</td><td></td></tr><tr><td>5.1 floor</td><td>5</td><td>Nearest integer less than or equal</td></tr><tr><td>-5.1 floor</td><td>-6</td><td></td></tr><tr><td>5.1 truncated</td><td>5</td><td>Nearest integer toward zero</td></tr><tr><td>-5.1 truncated</td><td>-5</td><td></td></tr><tr><td>5.1 rounded</td><td>5</td><td>Nearest integer</td></tr><tr><td>5.1 truncateTo: 2</td><td>4</td><td>Nearest argument multiple toward zero</td></tr><tr><td>5.1 truncateTo: 2.3</td><td>4.6</td><td></td></tr><tr><td>5.1 roundTo: 2</td><td>6</td><td>Nearest argument multiple</td></tr><tr><td>5.1 roundTo: 2.3</td><td>4.6</td><td></td></tr><tr><td>5 exp</td><td>148.41316</td><td>Exponential</td></tr><tr><td>2.7182819 ln</td><td>1.0</td><td>Natural logarithm</td></tr><tr><td>4 log: 2</td><td>2.0</td><td>The logarithm in the base of the argument</td></tr><tr><td>3 raisedTo: 1.1</td><td>3.3483695</td><td>The receiver to the power of the argument</td></tr></tbody></table>
<table><thead><tr><td>Expression</td><td>Answer</td><td>Comment</td></tr></thead><tbody><tr><td>4 raisedToInteger: 3</td><td>64</td><td>The receiver to the power of the integer argument</td></tr><tr><td>30 degreesToRadians</td><td>0.52359878</td><td>Convert degrees to radians</td></tr><tr><td>2 radiansToDegrees</td><td>114.59156</td><td>Convert radians to degrees</td></tr><tr><td>0.52359878 sin</td><td>0.5</td><td>Angle in radians</td></tr><tr><td>0.72273425 cos</td><td>0.75</td><td></td></tr><tr><td>0.24497866 tan</td><td>0.25</td><td></td></tr><tr><td>0.5 arcSin</td><td>0.52359878</td><td>Angle in radians</td></tr><tr><td>0.75 arcCos</td><td>0.72273425</td><td></td></tr><tr><td>0.25 arcTan</td><td>0.24497866</td><td></td></tr></tbody></table>

## Float

An 8-byte IEEE format is used for instances of class **Float** to approximate real numbers. This gives approximately 18 digits of precision and represents values in the range (+/-) 4.19e-307 to (+/-)1.67e308. The 8087 or 80287 arithmetic co-processor, depending upon your computer, must be present to perform arithmetic operations on floating operands.

## Fraction

Instances of class **Fraction** are exact representations of rational numbers. A pair of integers (instance variables numerator and denominator) describes the fraction. Fractions are created by sending the slash (/) message to an integer with an integer argument (provided that the answer does not reduce to an integer).

## Integer

Integers are frequently used in counting and indexing. Three subclasses of class **Integer** are defined: **LargeNegativeInteger**, **LargePositiveInteger** and **SmallInteger**. Instances of class **SmallInteger** are in the range -32767 to 32767. These are highly efficient in both computing speed and memory occupation. Small integers are encoded in the reference to the object (the object pointer); they are not represented as objects in memory. The large integer classes can represent numbers with up to 64K bytes of precision. Conversion between integer classes is automatic.
# Streams

The `stream` classes are used for accessing files, devices, and internal objects as sequences of characters or other objects. Streams have an internal record of their current position. Streams also have access messages which get or put the next object at the current position and advance the stream's position by one. Messages are defined for changing the stream position so that random access is possible.

This chapter presents the purposes of and the protocol shared among the stream hierarchy classes. For a complete specification of each class, refer to Part 4: Encyclopedia of Classes.

The `Stream` class hierarchy is as follows:

<table><tr><td>Stream</td></tr><tr><td>ReadStream</td></tr><tr><td>WriteStream</td></tr><tr><td>ReadWriteStream</td></tr><tr><td>FileStream</td></tr><tr><td>TerminalStream</td></tr></table>

Streams are frequently used for scanning input and writing edited output. The example which follows sends the message `printString` to an instance of class `String`. The answer to this message is a new string composed of the initial string (the receiver of `printString`) surrounded by quotes with any internal quotes doubled. For example:

<table><thead><tr><td>Expression</td><td>Answer</td></tr></thead><tbody><tr><td>'hello' printString</td><td>'hello'</td></tr><tr><td>'hello' printString printString</td><td>'"hello"'</td></tr></tbody></table>

The key to the following implementation of `printString` in class `String` is that an instance of class `WriteStream` automatically grows to contain all the characters written to it and responds to the message `contents` by returning a string containing all of its characters.
```perl
printString
    | inputStream outputStream |
    inputStream := ReadStream on: self.
    outputStream := WriteStream on:
        (String new: self size + 2).
    outputStream nextPut: $'.
    [inputStream atEnd]
    whileFalse: [
        character := inputStream next.
        outputStream nextPut: character.
        character == $
            ifTrue: [outputStream nextPut: $']].
    outputStream nextPut: $'.
    ^outputStream contents
```

This example illustrates several stream messages.

Instances of classes `ReadStream` and `WriteStream` are created with the `on:` message with a string as the argument. Both streams are positioned at the first character. Note that in creating the `WriteStream` instance, space is provided for the containing quotes but not for interior paired quotes. If interior quotes exist, the string object affected by the `WriteStream` will automatically be enlarged.

Characters are written to the `WriteStream` with the message `nextPut:`. The character to write is the argument.

The end of a `ReadStream` is detected with the `atEnd` message. If there is a character at the current position, `atEnd` answers false; otherwise it answers true.

A character is read from the `ReadStream` with the message `next`. Note that you cannot send the message `next` to a `ReadStream` that is positioned at the end.

All of the characters in a `WriteStream` are returned as a string in answer to the `contents` message.
## Accessing Protocol

We summarize the above information in the following protocol:

<table><thead><tr><td>Protocol</td><td>Explanation</td></tr></thead><tbody><tr><td>atEnd</td><td>Answer true if stream is at the end else answer false.</td></tr><tr><td>contents</td><td>Answer the collection of objects that is being streamed over.</td></tr><tr><td>next</td><td>Answer the next object in the receiver stream and advance the position by one.</td></tr><tr><td>nextPut: anObject</td><td>Write anObject at the current position. Answer anObject.</td></tr></tbody></table>

## Positioning and Reading Protocol

Some of the stream positioning protocol is as follows:

<table><thead><tr><td>Protocol</td><td>Explanation</td></tr></thead><tbody><tr><td>position</td><td>Answer an integer representing the stream's position. The position at the beginning of the stream is zero.</td></tr><tr><td>position: anInteger</td><td>Set the stream position to anInteger. Report an error if anInteger is beyond the end of the stream.</td></tr><tr><td>reset</td><td>Set the stream's position to zero.</td></tr><tr><td>skip: anInteger</td><td>Add anInteger (which may be negative) to the stream's position.</td></tr></tbody></table>

Some stream reading protocol follows:

<table><thead><tr><td>Protocol</td><td>Explanation</td></tr></thead><tbody><tr><td>do: aBlock</td><td>Proceed through the stream from the current position to the end evaluating aBlock with each element of the stream as the block argument.</td></tr></tbody></table>
<table><thead><tr><td>Protocol</td><td>Explanation</td></tr></thead><tbody><tr><td>empty</td><td>Answer true if the stream contains no elements; otherwise answer false.</td></tr><tr><td>next: anInteger</td><td>Answer a collection of the next anInteger elements of the stream. Advance the stream position by anInteger.</td></tr><tr><td>peek</td><td>Answer the next element in the stream without advancing the stream position. Answer nil if at end of stream.</td></tr><tr><td>peekFor: anObject</td><td>Answer true and advance the stream position if the next object in the stream equals anObject. Otherwise, answer false and leave the stream position unchanged.</td></tr><tr><td>skipTo: anObject</td><td>Set the stream position beyond the next occurrence of anObject in the stream or, if none, at the end of the stream. Answer true if there was an occurrence; otherwise answer false.</td></tr><tr><td>upTo: anObject</td><td>Answer a collection of objects starting at the current stream position and up to but not including the next object that equals anObject and advance the stream position beyond the object that equals anObject. If anObject is not in the stream, answer up to the end of the stream and set the stream position to the end.</td></tr></tbody></table>

The following example illustrates positioning and reading protocol using a stream on an array of symbols. First the stream is created and assigned to the variable Colors. Then a series of messages are sent to the stream Colors. The result of each message is shown below.

```pascal
Colors := ReadStream on:
          # (red blue green yellow pink cyan magenta brown).
```
<table><thead><tr><td>Expression</td><td>Answer</td></tr></thead><tbody><tr><td>Colors empty</td><td>false</td></tr><tr><td>Colors next</td><td>red</td></tr><tr><td>Colors next: 3</td><td>(blue green yellow)</td></tr><tr><td>Colors peek</td><td>pink</td></tr><tr><td>Colors peekFor: #blue</td><td>false</td></tr><tr><td>Colors upTo: #magenta</td><td>(pink cyan)</td></tr><tr><td>Colors skip: -4</td><td></td></tr><tr><td>Colors position</td><td>3</td></tr><tr><td>Colors skipTo: #pink</td><td>true</td></tr><tr><td>Colors upTo: #red</td><td>(cyan magenta brown)</td></tr></tbody></table>

## Writing Protocol

Some additional stream writing protocol follows.

<table><thead><tr><td>Protocol</td><td>Explanation</td></tr></thead><tbody><tr><td>nextPutAll:<br>aCollection</td><td>Write the elements of aCollection to the stream.<br>Answer aCollection.</td></tr><tr><td>next: anInteger<br>put: anObject</td><td>Write anObject to the stream anInteger times.<br>Answer anObject.</td></tr><tr><td>cr</td><td>Write a line-terminating character to the stream.</td></tr><tr><td>tab</td><td>Write a tab character to the stream.</td></tr><tr><td>space</td><td>Write a space character to the stream.</td></tr></tbody></table>
All objects understand the message `printOn:` with a stream as the argument. This message produces a character description of the receiver object on the argument stream. For example, the implementation of `printOn:` for class `Rectangle` is:

```
printOn: aStream
    "Print the origin and corner points"
origin printOn: aStream.
aStream nextPutAll: ' corner: '.
corner printOn: aStream
```

where the `printOn:` message is sent to the origin and corner points and the message `nextPutAll:` to its stream argument. The implementation for class `Point` is:

```
printOn: aStream
    "Print the x and y coordinates"
x printOn: aStream.
aStream nextPutAll: ' @ '.
y printOn: aStream
```

An example of printing a `Rectangle` is:

`Display boundingBox printOn: Transcript`

which writes the following in the System Transcript window if you are running in EGA color mode:

`0 @ 0 corner: 640 @ 350`

## Interface to DOS File System

Class `FinancialStatement`, a subclass of `WriteStream`, provides the primary interface to the DOS file system. File streams respond to all of the stream protocol presented earlier. File streams use an instance of the class `File` to provide random page access to DOS files. Files use an instance of class `FileHandle` to read and write DOS file pages. The class `Directory` provides access to DOS disk directories.

In this section we present an overview of these file system classes. For detailed information on all of the messages that they provide, please see the descriptions in Part 4.
## File Streams

File streams are usually created with either a message to class File specifying a partial or complete path name or a message to an instance of class Directory specifying a particular file to access in that directory. Here are some examples of messages to class File.

File pathname: `c:\smaltalk\chapter.1`

File pathname: `chapter.1`

File pathname: `\smaltalk\chapter.1`

The first expression above has a complete path name. The second example above is a partial path name. The directory object Disk, a global variable, is used to complete the path name. In this case the file `chapter.1` in the directory Disk is accessed. The final example is a complete path name without a disk drive specifier. The drive specifier used is the same as that used by the directory Disk.

The other way to create a file stream is by sending one of the following messages to a directory object.

Disk file: `chapter.1`

Disk newFile: `junk.fil`

The above two messages cannot have path names as arguments, only a file name. The difference between the two messages is that the second message will erase an existing file of the same name if one exists. They both will create the file if it does not already exist.

A word of caution about DOS files. DOS does not automatically update the directory entry on disk as you write to a file. There are two messages that you can send to file streams to cause the directory entry to be updated on the disk. These are:

stream close

stream flush

The difference between the two messages is that the first closes the file stream making further access to the DOS file using this object impossible. The second message also causes the directory entry to be updated but keeps the file stream object open for further access to the file. For consistency, all other streams support these two messages as well, but they have no effect.

File streams are buffered for efficiency. In addition, file streams recognize two different formats for end of line, the DOS cr-lf pair, and the UNIX single lf. When a file stream is opened, the beginning of the file is scanned to determine which format applies. New files are created using the DOS format. The following three messages let you test and change the line ending format for a file.
stream lineDelimiter "Answers Lf or Cr"

stream lineDelimiter: Lf "Change to Unix format"

stream lineDelimiter: Cr "Change to DOS format"

The fastest way to read a file stream is with the `upTo:` or the `nextLine` message. The fast way to write a file stream is with the `nextPutAll:` message.

Putting all of the above together, here is a faster version of the program given in Chapter 7 of the tutorials which converts text files from DOS format to UNIX format.

"Convert a file from DOS format to UNIX format"

| input output |

input := Disk file: 'chapter.7'.

output := Disk newFile: 'stripped.7'.

output lineDelimiter: Lf.

[input atEnd]

whileFalse: [output nextPutAll: input nextLine; cr].

output close

## Directories

The class `Directory` provides access to DOS file system directories. `Smalltalk/V` as delivered has the following global variables which contain directories.

### Disk

### DiskA

### DiskB

The variable `Disk` contains the directory in which you started `Smalltalk/V`. The variables `DiskA` and `DiskB` contain the root directories for the disk devices A: and B: respectively. You can create new directory objects using the following messages:

SampleDir := Directory pathname: 'a:\dirname'

DiskC := Directory new drive: $c; pathname: '\>'

Note that creating a directory object is not the same as creating a directory on the disk drive itself. To create a new directory on the disk, send the message `create` to a directory object with the proper drive and path name, as in:

SampleDir create
Directories understand messages for listing their subdirectories and files, for creating new files and subdirectories, and much more. See Part 4 for more details.

## Files and FileHandles

Class **File** provides the logical support to file streams necessary for random page access to DOS files. Class **FileHandle** provides the low level access to DOS files. Part 4 provides a detailed list of the messages implemented by these classes. Unless you are building some sort of new file access protocol separate from file streams, you will rarely have to deal with these classes. A few of the class messages for **File** are important: the ones for copying, renaming, and removing files.

**FileHandle** is a subclass of **ByteArray**. A **FileHandle** instance has a size of exactly two bytes long which contains the DOS file handle number when the file is opened. DOS allows only a limited number of file handles. When you try to open a file and no more file handles are available, **Smalltalk/V** automatically checks that all file handles are indeed used by some objects. For example, you might have opened a file and forgot to close it. As long as this handle is not pointed to by any object, **Smalltalk/V** will automatically reuse it to open a new file.

## Terminal Input and Output

Terminal input and output is accomplished through the cooperation of two classes: **InputEvent** and **TerminalStream**. **InputEvent** is a lower level interface whose method **nextEvent** uses a primitive to read a keyboard or mouse event and return the type of the event read. **InputEvent** is interrupt driven. It usually waits on the **KeyboardSemaphore** (a global variable containing an instance of class **Semaphore**) until the semaphore is signaled by a key stroke or mouse operation.

**CurrentEvent** is the global variable used to read events. It contains an instance of **InputEvent**. The read primitive modifies the instance variables of **CurrentEvent**: **type**, **value**, **x**, and **y**. The location of the mouse at the time of the event is placed in **x** and **y**. The following table describes the events returned from the read primitive (left column), the types returned by the **InputEvent** (middle column), and the values associated with the types (right column).
<table><thead><tr><th>Primitive Type</th><th>nextEvent Type</th><th>Value</th></tr></thead><tbody><tr><td>#characterInput</td><td>#characterInput</td><td>ASCII code</td></tr><tr><td>#functionInput</td><td>#functionInput</td><td>scan code</td></tr><tr><td>#mouseMove</td><td>#mouseMove</td><td></td></tr><tr><td rowspan="7">#mouseButton</td><td>#mouseStillDown</td><td>mouse button value</td></tr><tr><td rowspan="7">#mouseButtonUp</td><td>1 = left button down</td></tr><tr><td>2 = right button down</td></tr><tr><td>3 = middle button down</td></tr><tr><td>5 = left button down shifted</td></tr><tr><td>6 = right button down shifted</td></tr><tr><td>7 = middle button down shifted</td></tr><tr><td>1 = left button up</td></tr><tr><td rowspan="7">#nullEvent</td><td rowspan="7">#nullEvent</td><td>2 = right button up</td></tr><tr><td>3 = middle button up</td></tr><tr><td>5 = left button up shifted</td></tr><tr><td>6 = right button up shifted</td></tr><tr><td>7 = middle button up shifted</td></tr><tr><td>(none)</td></tr></tbody></table>

The scan code associated with `#functionInput` is defined by DOS. When a `#mouseMove` event is returned from the primitive, an `InputEvent` can generate either `#mouseMove` or `#mouseStillDown` depending on whether a mouse button is being held down (if yes, the latter one is generated). When a `#mouseButton` event is returned, an `InputEvent` further differentiates it into two types, `#mouseButtonDown` or `#mouseButtonUp` depending on whether it is a down or up action that has caused the event.

TerminalStream is defined as a subclass of `ReadWriteStream`. It uses `InputEvent` to read from the keyboard or mouse and sends messages to `CharacterScanner` to write characters to the terminal screen. The global variable `Terminal`, which contains an instance of class `TerminalStream`, is used throughout Smalltalk/V to handle terminal I/O.

TerminalStream reimplements the method `next` defined in `Stream` with the method `read`. Method `read` is the major user interface for reading a single character (or function key) from the keyboard or an event from the mouse. Two global variables, `FunctionKey` and `MouseEvent`, are set to true or false to indicate the source of the input before the input is returned. If a mouse event is read, the variable `MouseEvent` is set to true. If a function key is pressed, the variable `FunctionKey` is set to true. And non-function keys set both variables to false.
Method `nextPut:` outputs a character to the terminal screen at the current cursor position. It is seldom used in the system since writing characters to the screen is usually done through instances of `CharacterScanner` which has more sophisticated masking and clipping capabilities.

## Programming Function Keys

When a function key is processed outside of class `Terminal-Stream`, the `read` method in class `TerminalStream` is usually invoked by method `processInput` in class `Dispatcher`. As soon as a function key is pressed, the `read` method returns the function code and sets the global variable `FunctionKey` to true and the global variable `MouseEvent` to false. Method `processInput` then calls method `processKey`: in the receiver ` dispatcher` class (any of the ` dispatcher` classes). The latter method checks the state of the two global Boolean variables, `MouseEvent` and `FunctionKey`. If either of the variables is true, then a function code must be processed. To do this, method `processFunctionKey`: in the receiver ` dispatcher` is invoked to compare the function code to the values defined by the pool dictionary `FunctionKeys`. When a match is found, the appropriate action is taken.

Suppose you want to assign the `move` and `frame window` operations to `F1` and `F2`. First, define names for them, for example `MoveWindowFunction` and `FrameWindowFunction` respectively. Then, enter these names into the pool dictionary `FunctionKeys` and associate them with the desired function key codes. You can do this by executing the following expression:

FunctionKeys at: 'MoveWindowFunction'
put: 59 asCharacter.

FunctionKeys at: 'FrameWindowFunction'
put: 60 asCharacter

This creates two new variables in pool dictionary `FunctionKeys`. Note that 59 and 60 are the scan codes generated by `F1` and `F2` respectively.

The next step is to add the following code to method `processFunctionKey`: in class `Dispatcher` so that the desired functions occur when `F1` and `F2` are pressed.
MoveWindowFunction == aCharacter

ifTrue: [
    pane topPane hasCursor
    ifTrue: [^pane topPane dispatcher move]
    ifFalse: [^Terminal bell]].

FrameWindowFunction == aCharacter

ifTrue: [
    pane topPane hasCursor
    ifTrue: [^pane topPane dispatcher frame]
    ifFalse: [^Terminal bell]].

## Collections

A collection is a group of related objects. The Smalltalk collection classes define several different data structures which serve as containers for arbitrary objects. For example, a *String* is a sequence of characters while a *Set* is an unordered collection of non-duplicated objects of any kind. The collection classes are useful because they provide similar protocol for:

1. Iterating over the elements of a collection.

2. Searching a collection for a particular element.

3. Adding and removing elements.

4. Accessing and changing elements.

The following is the Collection class hierarchy:

Collection

Bag

IndexedCollection

FixedSizeCollection

Array

byteArray

Interval

String

Symbol

OrderedCollection

SortedCollection

Set

Dictionary

IdentityDictionary
The attributes, conversions, and common protocol among various collections are discussed next with a description of each kind of collection following.

## Attributes of the Collection Class

In general, each kind of collection can be characterized by four attributes:

1. Whether the collection has a well defined order associated with its elements. This order can be defined either externally by a key or internally by the contents of elements.

2. Whether the collection's size is fixed or expandable.

3. Whether or not duplicates of the collection's elements are allowed.

4. Whether the collection is accessible by a set of keys. Keys can be either integer indices or lookup keys.

The following table shows the attributes of each class:

<table><thead><tr><th></th><th>Ordered</th><th>Fixed size</th><th>Dup's</th><th>Keys</th><th>Element Class</th></tr></thead><tbody><tr><td>Bag</td><td>No</td><td>No</td><td>Yes</td><td>None</td><td>any</td></tr><tr><td>IndexedCollection*</td><td>Yes</td><td>N.A.</td><td>N.A.</td><td>Integer</td><td>N.A.</td></tr><tr><td>FixedSizeCollection*</td><td>Yes</td><td>Yes</td><td>N.A.</td><td>Integer</td><td>N.A.</td></tr><tr><td>Array</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Integer</td><td>any</td></tr><tr><td>Vector</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Integer</td><td>SmallInteger</td></tr><tr><td>Interval</td><td>Internal</td><td>Yes</td><td>No</td><td>Integer</td><td>Number</td></tr><tr><td>String</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Integer</td><td>Character</td></tr><tr><td>Symbol</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Integer</td><td>Character</td></tr><tr><td>OrderedCollection</td><td>Yes</td><td>No</td><td>Yes</td><td>Integer</td><td>any</td></tr><tr><td>SortedCollection</td><td>Internal</td><td>No</td><td>Yes</td><td>Integer</td><td>any</td></tr><tr><td>Set</td><td>No</td><td>No</td><td>No</td><td>None</td><td>any</td></tr><tr><td>Dictionary</td><td>No</td><td>No</td><td>No</td><td>Lookup</td><td>any</td></tr><tr><td>IdentityDictionary</td><td>No</td><td>No</td><td>No</td><td>Lookup</td><td>any</td></tr></tbody></table>

**Notes:** * — abstract classes, there are no instances

Internal — ordered by the internal contents of the collection

N.A. — not applicable (determined by subclasses)

In the table, the only collections that have the same attribute values are the *String* and *Dictionary* pair and *Dictionary IdentityDictionary* pair. The difference between a *String* and a *Symbol* is that a *Symbol* is guaranteed to be unique while a *String* can have many copies. The difference between a *Dictionary* and an *IdentityDictionary* is that during the key lookup comparison, the former uses the `==` message while the latter uses `==`.
## Conversions

Because the various collection classes have different attributes, being able to convert from one kind of collection to another is useful. Smalltalk/V provides the following conversion protocol in class **Collection**.

<table><thead><tr><td>Methods</td><td>Comments</td></tr></thead><tbody><tr><td>asArray</td><td>Ordering is possibly arbitrary.</td></tr><tr><td>asBag</td><td>Duplicates are kept.</td></tr><tr><td>asSet</td><td>Duplicates are eliminated.</td></tr><tr><td>asOrderedCollection</td><td>Ordering is possibly arbitrary.</td></tr><tr><td>asSortedCollection</td><td>Each element is &lt;= its successor.</td></tr><tr><td>asSortedCollection: sortBlock</td><td>Ordering is specified by sortBlock.</td></tr></tbody></table>

Thus any collection can be converted into an **Array**, a **Bag**, a **Set**, an **OrderedCollection**, or a **SortedCollection**.

## Instance Creation

Like other classes, message **new** can be used to create an instance of any collection. Message **new**: can be used to create a fixed-size collection with a specified size and a variable size collection with a specified initial allocation size.

Some collections may be expressed in literal form:

<table><thead><tr><td>Class</td><td>Instance in literal form</td></tr></thead><tbody><tr><td>String</td><td>'John Mary'</td></tr><tr><td>Symbol</td><td>#John</td></tr><tr><td>Array</td><td>#$J 'John' John (John 3))</td></tr></tbody></table>

A literal string is enclosed in a pair of quotes, a literal symbol is preceded by a number sign (`#`), and a literal array is enclosed in paired parentheses and preceded by a number sign. The **Array** example contains four elements: a character, a string, a symbol, and another array which has two elements — a symbol and a small integer. Notice that within a literal array, a symbol or another array element must not be prefixed with a number sign.

In addition, there is protocol in every collection class to create instances with one, two, three, or four elements which are not necessarily constants. For example,
Array with: 'Daughters of John'
with: #('Ann' 'Mary')

creates an array with two elements, a string and another array of two elements.

## Common Protocol

Smalltalk/V provides common protocol to manipulate collections in a uniform way. These can be categorized as adding new elements, removing elements, testing the occurrences of elements, and enumerating elements. These are all described in Part 4: Encyclopedia of Classes under class Collection.

Suppose you have two global variables, Customer and Supplier, initialized as:

Customer := Bag with: #John.

Supplier := # (John Peter).

Then you send adding, removing, and testing messages to Customer:

<table><thead><tr><th>Expression</th><th>Answer</th><th>Customer value if changed</th></tr></thead><tbody><tr><td>Customer add: #Bob</td><td>Bob</td><td>Bag(John Bob)</td></tr><tr><td>Customer addAll: Supplier</td><td>(John Peter)</td><td>Bag(John John Peter Bob)</td></tr><tr><td>Customer removeAll: Supplier</td><td>(John Peter)</td><td>Bag(John Bob)</td></tr><tr><td>Customer removeAll: Supplier</td><td>error</td><td>Bag(Bob)</td></tr><tr><td>Customer remove: #Bob</td><td>Bob</td><td>Bag()</td></tr><tr><td>Customer isEmpty</td><td>true</td><td></td></tr><tr><td>Customer occurrencesOf: #John</td><td>0</td><td></td></tr><tr><td>Customer includes: #John</td><td>false</td><td></td></tr><tr><td>Customer addAll: # (John John)</td><td>(John John)</td><td>Bag(John John)</td></tr><tr><td>Customer addAll: Supplier</td><td>(John Peter)</td><td>Bag(John John Peter)</td></tr><tr><td>Customer occurrencesOf: #John</td><td>3</td><td></td></tr></tbody></table>
Enumerating messages allow you to process all the elements of a collection. Enumerating messages usually take a one-argument block as an argument and evaluate it with each element in the receiver collection. Assume *Customer* and *Supplier* have the same values as at the end of the last example.

count := 0.
Customer do: [ :aName | count := count + aName size].
^count

produces 17.

Customer select: [ :aName | aName == #John]
produces Bag(John John John).

Customer reject: [ :aName | aName == #John]
produces Bag(Peter).

Customer collect: [ :aName | aName asArray]
produces Bag(($J $o $h $n ) ($J $o $h $n ) ($J $o $h $n ) ($P $e $t $e $r ) ).

Customer detect:
[ :aName | aName includes: $P]
produces Peter.

Customer detect:
[ :aName | aName == #Mary] ifNone: ['Not found']
produces 'Not found'.

Customer inject: 0 into:
[ :count :aName | count + aName size]
produces 17.

## Class Bag

A *Bag* contains a collection of arbitrary objects. Duplicates are allowed and ordering is arbitrary. A *Bag* does not have external keys; therefore it cannot respond to the messages *at:* and *at:put*. In addition to the common protocol, it has a message, *add:withOccurrences*: to add an element a specified number of times. *Bags* are hashed for efficient lookup.
As an example, here is an expression that computes the frequency of occurrence of words in a file.

| input frequency output word |
input:=File pathname: 'in.fil'.
output:=File pathname: 'out.fil'.
frequency:=Bag new.
[(word:=input nextWord) isNil]
    whileFalse: [frequency add: word asLowerCase].
frequency asSet asSortedCollection do:[:word|
    output
        nextPutAll: word;
        tab;
        nextPutAll: (frequency occurrencesOf: word) printString;
        cr].
output close.

## Class Set

A Set is like a Bag except that it cannot have duplicate elements. Sets are hashed for efficient lookup.

As an example, here is an expression that computes a sorted list of words in a file.

| input words word |
input:= File pathname: 'in.fil'.
words:= Set new.
[(word:= input nextWord) isNil]
    whileFalse: [words add: word asLowerCase].
^ words asSortedCollection.

## Class Dictionary

Class Dictionary represents a set of objects with external lookup keys. Dictionaries are hashed for efficient lookup. A dictionary's elements are instances of class Association which contain a lookup key and its corresponding value. Because the key is only for lookup purposes, the messages includes:, do:, and other inherited enumeration messages are applied to the values rather than to the keys or to the associations themselves. Class Dictionary provides other messages to deal with keys and associations. Refer to Part 4 for all the messages implemented by class Dictionary.
## Class IdentityDictionary

Class **IdentityDictionary** is similar to **Dictionary** except that it uses equivalence (`==`) instead of equality (`=`) during a key lookup. Its implementation also makes better storage utilization than a **Dictionary**. Because its key lookup matches object pointers instead of object contents, the only sensible classes for its keys (except for special situations) are **Symbol** and **SmallInteger**.

## Class IndexedCollection

Class **IndexedCollection** represents collections with elements ordered externally by integer indices. It is an abstract class to contain common protocol for its subclasses and therefore should not have any instance of its own created.

Because of its well-defined ordering, all of its subclasses implement the equality (`==`) message in such a way that the answer is true if two **IndexedCollections** have the same class and size, and their corresponding elements answer true for the equality message.

## Class FixedSizeCollection

Class **FixedSizeCollection** is a subclass of class **IndexedCollection**. It is an abstract class to provide common protocol for its subclasses: **Array**, **byteArray**, **Interval**, **String**, and **Symbol**. These subclasses represent collections with a fixed range of integer indices as external keys. Because these subclasses have fixed sizes, they cannot respond to the **add**: message.

The instance creation message **new**: is subtly different when applied to a fixed size collection than to a variable one. The following message:

### (Array new: 5) size

evaluates to 5, while

### (OrderedCollection new: 5) size

evaluates to 0. When message **new**: is sent to class **Array**, the new instance is created with elements initialized to nil. When the message is sent to a variable size collection like **OrderedCollection**, the new instance is created with space allocated, but is logically empty.

The elements of an **Array** can be any objects. An element of a **byteArray** must be a **SmallInteger** in the range of 0 to 255. The elements of a **String** or **Symbol** are characters. **Symbols** are guaranteed to be unique.
An **Interval** represents a finite arithmetic progression. Its elements can be any kind of number: integer, float, or fraction. Although **Interval** contains all the numbers within a specified range and with a specified increment between each number, it is represented concisely with only three instance variables: beginning, end, and increment. Its elements are regenerated upon access rather than stored in the instance. To create an instance, the two **Interval** class messages, `from/to:` and `from/to:by:`, are used. Class **Number** also provides some shorthand messages, `to:` and `to:by:`, to create new **Intervals**.

## Class OrderedCollection

**OrderedCollections** are ordered by the sequence in which objects are added to and removed from them. They are like dynamic arrays, except that they can be expanded on both ends. To facilitate this feature, messages are provided to add, remove, and access both the beginning and end.

The `add:` message defined in class **Collection** is implemented to be like `addLast:`. Other messages enable you to access, add, or remove an object in the middle by specifying its preceding or succeeding object.

**OrderedCollections** can act as stacks or queues. Operations to a stack are typically "last-in, first-out." Following is a comparison of terminology:

<table><thead><tr><th>Typical Stack<br>Vocabulary</th><th>OrderedCollection<br>Message</th></tr></thead><tbody><tr><td>push newElement</td><td>addLast: newObject</td></tr><tr><td>pop</td><td>removeLast</td></tr><tr><td>top</td><td>last</td></tr><tr><td>empty</td><td>empty</td></tr></tbody></table>

Operations to a queue are typically "first-in, first-out":

<table><thead><tr><th>Typical Queue<br>Vocabulary</th><th>OrderedCollection<br>Message</th></tr></thead><tbody><tr><td>add newElement</td><td>addLast: newObject</td></tr><tr><td>delete</td><td>removeFirst</td></tr><tr><td>front</td><td>first</td></tr><tr><td>empty</td><td>empty</td></tr></tbody></table>

Queues grow on one end and shrink on the other. When space is exhausted on the growing end, an **OrderedCollection** always checks the shrinking end. If there is enough space, it shifts the entire collection towards the shrinking end to make room for growing at the other end. If there is not enough space, it will allocate a larger space and copy the original collection to the new space.
## Class SortedCollection

SortedCollections are ordered according to a two-argument block called the *sort block*. The sort block is used to determine whether two elements are correctly sorted relative to each other. Because the position of each element is dictated by the sort block, messages such as `addLast:` are disallowed. Message `add:` `newObject`, however, will insert the `newObject` into the sorted position according to the sort block.

There are five ways to create a new instance:

SortedCollection new
SortedCollection new: 10
SortedCollection sortBlock: [ :a :b | a > b ]
anyCollection asSortedCollection
anyCollection asSortedCollection: [ :a :b | a > b ]

A sort block can be as complex as desired, but the last expression in the block must evaluate to either true or false. For example, the following sort block assumes that strings are being compared. It sorts the strings based on the number of unique vowels.

[ :a :b |
(a aslowerCase select: [ :c | c isVowel]) asSet size
<=
(b aslowerCase select: [ :c | c isVowel]) asSet size]

When the sort block is not specified at creation time, the following default sort block is used:

[ :a :b | a <= b ]

The sort block can also be changed any time by sending message `sortBlock: newBlock` to a `SortedCollection` which automatically resorts the whole collection according to the `newBlock`.

## Window Classes

To write an interactive application in Smalltalk/V, you need to understand Smalltalk/V window technology. In Smalltalk/V, a window typically involves three major kinds of classes (and their subclasses): the application classes (such as `ClassBrowser`) which synchronize panes, the `Pane` classes which display on the screen, and the `Dispatcher` classes which process keyboard and mouse inputs.
The application class is also referred to as the **model** class. It has to be written for each new application, although you can use an existing model as a template. The **Pane** and **Dispatcher** classes and their subclasses are complete building blocks in the system, and you rarely need to modify them.

The relationship among these classes is depicted in Figure 14.1 using the **ClassBrowser** window as an example.

You can look at the **model** class as the skeleton of a window which organizes all the window panes and is responsible for the communication and synchronization among the panes.

The **Pane** class has two immediate subclasses: **TopPane** and **SubPane**. The main function of an instance of class **TopPane** is to coordinate all of the subpanes in the *model* window. Thus there is one and only one instance of class **TopPane** in each window. It holds the window label located at the top of the window.

The **SubPane** class has three subclasses: **GraphPane**, **ListPane** and **TextPane**. An instance of class **ListPane** provides a view of a list of strings. You can browse through the list (scrolling if necessary), and select the string that you wish. When you make a selection, the **model** instance will be notified and act accordingly.

An instance of class **TextPane** lets you view and edit the text that it contains. The text is usually represented as an instance of class **StringModel**. When you save a piece of modified text, the **model** instance is again notified to act according to your request.

Every pane has a unique dispatcher associated with it. The type of the pane determines the type of the associated dispatcher. An instance of a **Dispatcher** subclass serves as a messenger that collects input from the keyboard and mouse. It sends messages to its pane to take actions according to the input events.

Keep in mind that for an application with window panes that involve only classes **GraphPane**, **ListPane** and **TextPane**, you only need to know about the application model. If you want to define new kinds of panes, then you also need to define corresponding new **Dispatcher** subclasses, which means you need to learn about the entire trilogy. Or if you want to re-assign the meaning of a function key, then you need to know how to modify the existing **Dispatchers**. For these more advanced problems refer to the Smalltalk source code and **Part 4: Encyclopedia of Classes** for the relevant classes.
<figure><img src="image_57.png" /><figcaption>Figure 14.1
Dispatcher, Pane, Model Relationships</figcaption></figure>
# Application Model

An application model has five major functions:

* Remember the Current State

* Create Panes

* Initialize Contents of Panes

* Carry Out Communication and Synchronization

* Define Menus for Panes

## Remember the Current State

This is normally accomplished by assigning application states to instance variables in the model class. For example, class *ClassBrowser* has the following instance variables:

### browsedClass

The class object you are currently browsing.

### selectedDictionary

The current message dictionary (either class or instance) of the class you are browsing.

### selectedMethod

The currently selected method within the currently selected message dictionary.

The contents of these variables are normally initialized during pane creation and changed from time to time by the *change* methods mentioned below.

## Create Panes

The creation of panes is usually accomplished by sending the message *open* or *openOn*; depending on whether an argument is needed, to a new instance of the model class. It initializes the following items:

* The label and minimum size of the window

* The name of each pane

* A menu creation message associated with each pane (optional)

* A framing block for calculating the area of each pane relative to the window

* A change message for each pane to send when the change in a pane has global effects

* The model of each pane (usually the application model itself but can be another model. This can also be changed dynamically.)
Following is the `openOn:` method defined in `ClassBrowser`:

```perl
openOn: aClass
    "Create a class browser window on aClass.
    Define the type, behavior and relative size
    of each pane and schedule the window."
| topPane twoLineHeight |
(aClass isKindOf: Class)
    ifFalse: [^ nil].
browsedClass := aClass.
topPane := TopPane new
    label: aClass name, ' | ClassBrowser';
    minimumSize: SysFontWidth * 20
    @ (SysFontHeight * 8);
yourself.
twoLineHeight := ListFont height * 2 + 4.
topPane addSubpane:
    (ListPane new
        model: self;
        name: #dictionaries;
        change: #dictionary:;
        selection: 2;
        framingBlock: [box]
        box origin extent:
            box width // 5 @ twoLineHeight]).
selectedDictionary := browsedClass.
topPane addSubpane:
    (ListPane new
        model: self;
        name: # selectors;
        menu: #selectorMenu;
        change: #selector:;
        framingBlock: [:box]
        box origin + (0 @ twoLineHeight)
        extent: box width // 5 @
            (box height - twoLineHeight)).
topPane addSubpane:
    (TextPane new
        model: self;
        name: #text;
        change: #accept:from:;
        framingBlock: [:box]
        box origin + ((box width // 5) @ 0)
        corner: box corner]).
topPane dispatcher open scheduleWindow
```
Invoking this method will create a window with three subpanes:

* dictionaries (a ListPane)

* selectors (a ListPane)

* text (a TextPane)

In order for the window to work properly, the messages *dictionaries*, *dictionary*;, * selectors*, *selector*;, *selectorMenu*, *text*, and *accept:from*: must be defined as instance methods in the model.

## Initialize Contents of Panes

The application model must provide for each subpane a method with the same name as the pane name which, when invoked, answers the data of the pane. For example, the *ClassBrowser* has three methods to initialize its three subpanes:

* dictionaries

"Answer the array of dictionaries"
  ^#( class instance )

* selectors

"Answer a sorted list of selectors for the selected dictionary"
  ^selectedDictionary selectors asSortedCollection

* text

"Answer the source text for the selected method"
  ^selectedDictionary sourceCodeAt: selectedMethod

Note that the first two methods answer an instance of a subclass of class *IndexedCollection* whose elements must be printable like *Strings* or *Symbols*. The third one answers a string (lines within the string are separated by line feeds). In case there is more data than a string can hold, the method *fileInFrom*: in *TextPane* can be used to initialize its data from an external file (refer to source code of the *file* method in the *DiskBrowser* class).

## Carry Out Communication and Synchronization

When you make a selection or change the contents of pane data, the effect can be either *local* or *global*. Global effects the model or other panes. Anything else is local. For example, in the *Class Browser*, when you make a selection in the *dictionaries* pane, both the * selectors* pane and *text* pane need to be synchronized. Thus the effect is global. If you make editing changes in the *Class Browser's* text pane, the change is local because it does not effect other panes or the model. When you save these changes, however, the text needs to be compiled into the selected class and logged to the *change log* file. This can only be done by the model, so the effect of saving the text is global.
Specifying change messages in the `open` or `openOn:` method provides each pane with a message to send when these global effects occur. The argument of a change message is usually a piece of data passed from the pane to the model. For example, when you select a method in the selectors pane, the following method in `ClassBrowser` is invoked by the pane:

```
selector: aSymbol
  "Display the selected method in the text pane"
selectedMethod := aSymbol.
self changed: #text
```

where the first statement changes the application state by assigning `aSymbol` to the instance variable `selectedMethod`, and the second statement informs the text pane that the state has changed and it needs to update its contents.

When the global effect calls for one or more updates in another pane, the `changed:` or `changed:with:` method defined in class `Object` can be used to broadcast the effect to all subpanes of the model. In the previous example, selecting a method in the selectors pane displays the source of the method in the text pane. The `changed:` message is used to notify the text pane that the `selectedMethod` had been changed. The other message, `changed:with:`, in addition to notifying subpanes, also passes an object as the argument of the `with:` keyword to provide communication from the model to the panes.

When the subpane receives the update message (sent by method `changed:`) and its name matches the argument of the `changed:` keyword, the pane name is sent as a message to the model retrieving the new pane contents. To continue the previous example, after the text pane receives the update message, it updates its own data by sending the text message to the `ClassBrowser` to perform the text method which in turn answers a string of the new text pane contents. This concludes the update, and the `Dispatcher` regains control and waits for the next keyboard or mouse event.

## Define Menus for Panes

If the `menu:` message is sent during the creation of a pane, a method with the same name as the message argument must be defined in the model. This method answers an instance of class `Menu` which contains the desired menu items for the pane. In the `openOn:` method of the `ClassBrowser`, the message `menu: #selectorMenu` is sent to the selectors pane. Thus a corresponding method is defined in the `ClassBrowser`:

```
selectorMenu
  "Answer the selector pane menu"
^Menu
  labels: 'remove\senders\implementors' withCrs
  lines: #()
  selectors: #(removeSelector senders implementors)
```
The string argument to **labels**: contains the items to be shown in the menu. Message **withCrs** replaces backslashes (\) with line feeds in its receiver string. The argument to ** selectors**: is an array of messages to send when you select the corresponding item in the menu. The methods carrying out these messages can be optionally defined in the model. If you do not define them, the ones in the **Dispatcher** class (associated with the pane) are used as defaults. If they are not defined in either class, an error results. The **ClassBrowser** defines all three methods needed in the menu:

**removeSelector**
"Remove the selected method"
**selectedMethod** isNil
ifTrue: [^ nil].
**selectedDictionary**
removeSelector: selectedMethod.
**Smalltalk logEvaluate**
selectedDictionary name,
'removeSelector: #',
selectedMethod.
**selectedMethod** := nil.
self
changed: # selectors with: # restore;
changed: # text

**senders**
"Popup a window with the senders of the selectedMethod"
**selectedMethod** == nil
ifFalse: [Smalltalk sendersOf: selectedMethod]

**implementors**
"Popup a window with the implementors of the selectedMethod"
**selectedMethod** == nil
ifFalse: [
Smalltalk implementorsOf: selectedMethod]

The **removeSelector** method provides another example of changing the current state and using message **changed**: to inform the selectors pane to update.
## Pane

An instance of class **TopPane** is responsible for all the functions pertaining to the whole window:

* Display the window frame and invoke each **SubPane** to display its pane contents.

* Save, display, and highlight the window label.

* Activate the window and all subpanes.

* Answer whether the window contains a certain point.

* Close the **TopPane** and invoke each **SubPane** to close itself.

Class **SubPane** and its three subclasses, **GraphPane**, **ListPane** and **TextPane** are responsible for functions that are specific to subpanes:

* Display the pane frame.

* Activate itself.

* Answer whether the pane contains a certain point.

* Display a portion of its data in the pane.

* Scroll data in four directions.

* Make a selection on a piece of its data.

* Close itself.

In addition, a **TextPane** provides the capabilities to cut, paste, copy, and execute portions of its data.

## Dispatcher

The main function of an instance of class **Dispatcher** and its subclasses is to interpret the input from the keyboard or mouse and send an appropriate message to the corresponding pane. It also has the following functions:

* Activate or de-activate the corresponding pane.

* Return the cursor to the top-left corner of its pane.

* Open or close the window.

* Cycle windows or panes in the window.

A **TopDispatcher** has the following additional functions:

* Provide methods to execute items in the window menu.

* Set or change the content of the window label bar.

Other **Dispatchers** have the following additional functions:

* Tell the pane to scroll its data by a specified amount.

* Tell the pane to handle selections.
* Provide methods to execute items in the pane menu.

## Prompter

Class Prompter gives an application writer a simple mechanism to pose a question and solicit an answer. A Prompter is a window with its label showing the intended question and a single text pane for editing the answer. It is a window application itself, but is often used by other window applications as a building block. For example, when you create a file using the Disk Browser, the first thing you see is a Prompter asking you to respond with the file name.

To open a Prompter, you can send one of the following two messages to class Prompter:

Prompter prompt: question default: answer

Prompter prompt: question defaultExpression: answer

where both question and answer are strings. After the Prompter window is opened, the answer string will be shown in its text pane as a default. The first message returns a string as answered by the application user, while the second message returns an object resulting from evaluating the answer. For instance,

Prompter prompt: 'Give me a string please'
default: '2 + 3'

returns '2 + 3', and

Prompter prompt: 'Give me an expression please'
defaultExpression: '2 + 3'

returns 5 after the default answer is accepted. If you cancel the Prompter, an answer of nil will be returned by both messages.

Notice that when a Prompter is accepted or canceled, the program flow control is given back to the caller of the Prompter. When most other kinds of windows are closed, control is given to the Scheduler (described below) to cause another window to become active.

## Dispatch Manager

A DispatchManager schedules all the windows under its control. Normally only one such instance exists in the system which is contained in the global variable Scheduler. The Scheduler maintains an ordered collection of TopDispatchers and schedules windows by sending messages to these TopDispatchers. It performs the following functions:

* Add and remove dispatchers (thus windows).
* Answer the TopDispatcher associated with the active window.

* Display all the windows.

* Cycle the ordering of windows.

* Search for the window containing the cursor and make it the active window.

* Re-initialize the system by removing all windows and then drawing the System Transcript window.

## Applications with Multiple Windows

One way to coordinate windows within a multi-windowed application is to have one application model for all the windows in the application. You make each subpane of these windows a dependent of the model. When a change is made to a subpane which will have global effects, one of the change methods in the model will be invoked. It will then decide what other subpanes will be affected and updated.

In Smalltalk, subpanes in one window can have non-unique names. Subpanes with the same name are updated simultaneously in one broadcasting of changes. Since this is also true for the one model multi-window approach, take care in giving the same name to subpanes in different windows.

Another way to coordinate windows is to leave the current scheme of one model per window as it is. In this case, one super model is created with all other models as its dependents. In the super model, one change message is defined for each dependent. When the lower-level model receives a change message, it updates its own panes as in the single window application. In addition, the lower-level model sends an appropriate change message to the super model which then broadcasts the change to other dependent models. Each lower-level model should implement an update method to accept the broadcasting from the super model.

A window can also be associated with the super model which can, for example, allow you to select different functions in the application. Each selection triggers a sequence of windows being activated. This approach provides more modularity. For instance, subpane names are related to a particular lower-level model, so you do not have to be concerned about name collisions among windows. This gives you an opportunity to use single-window applications as building blocks. The disadvantage is that more levels of message sends will be generated.

When an application wants to update a subpane which is outside of the current active window, it should make the subpane's window the active window first. If it does not, the portions of the subpane obscured by other windows will be overwritten.
# Graphic Classes

Smalltalk/V graphics are generated by bitmapped operations. In fact, Smalltalk/V uses bitmapped graphics to generate the entire user interface.

A *Bitmap* is simply a linear array of bits. Each bit has a value of 1 or 0, with 1 representing white and 0 black. Since a monochrome display screen is a two dimensional plane of pixels, instances of class *Form* provide a two dimensional view of the bitmap to represent the monochrome screen. A Form has a *bitmap*, a *width* and a *height*, which allows you to manipulate the bitmap as if it were a two dimensional array of bits. The *DisplayScreen* represents a monochrome screen which is a special kind of Form, and hence a subclass of class *Form*.

A *ColorScreen* or a *ColorForm* (both are subclasses of *Form*) consists of an array of bitmaps. This additional dimension allows colors other than black and white to be recorded. All bits at the same location of each bitmap collectively represent the color of the pixel at that location. For example, if the bit at location 0@0 in the first bitmap is 1, and the bits at the same location in the second, third, and fourth bitmap are 1, 0, and 0, then the color code for the top left pixel is the binary number 0011 (the bit in the first bitmap is least significant) or 3 in decimal. The EGA and VGA graphics controllers allow a maximum of four bitmaps, thus a maximum of 16 colors are available to be displayed at any one time.

Almost all bitmapped operations involve moving bits from one place to another. Naturally, you might think that you would implement this by simply sending messages to a *Form*. Due to the complexity of these operations, however, another class, called *BitBlt*, is created to handle all bitmapped operations.

Bitmapped operations address a bit or an area of bits in a *Form*. A *Point* addresses an individual bit, while a *Rectangle* refers to a block of bits.

Bitmapped graphics, then, is centered around four classes and their subclasses: *Point*, *Rectangle*, *Form*, and *BitBlt*. The rest of the chapter describes each of these classes in detail.

## Point

A *Point* addresses a bit within the bitmap of a two dimensional *Form*. It has two instance variables: *x*, representing the column (horizontal) coordinate, and *y*, representing the row (vertical) coordinate. The value of *x* increases to the right and *y* to the bottom.

The most efficient way to create a *Point* is by sending the `@` message to an *Integer*. For example, the top left corner of a *Form* can be addressed by the *Point*:
## 0 @ 0

where the first integer (receiver) is the x coordinate and the second integer (argument) the y coordinate. The x: and y: messages alter the coordinates of a Point, while the x and y messages retrieve these coordinates. A Point can also be compared with another Point. Following are some examples:

<table><thead><tr><th>Expression</th><th>Result</th></tr></thead><tbody><tr><td>(1 @ 100) x</td><td>1</td></tr><tr><td>(1 @ 100) y</td><td>100</td></tr><tr><td>(1 @ 100) x: 50</td><td>50 @ 100</td></tr><tr><td>(1 @ 100) y: 50</td><td>1 @ 50</td></tr><tr><td>(-2 @ 10) < (-1 @ 11)</td><td>true</td></tr><tr><td>(-2 @ 10) < (-1 @ 10)</td><td>false</td></tr><tr><td>(-2 @ 10) > (-3 @ 11)</td><td>false</td></tr><tr><td>(-2 @ 10) max: (-3 @ 11)</td><td>-2 @ 11</td></tr><tr><td>(-2 @ 10) min: (-3 @ 11)</td><td>-3 @ 10</td></tr><tr><td>1 @ 2 between: 0 @ 2 and: 2 @ 2 true</td><td></td></tr></tbody></table>

Arithmetic can be performed on a Point with either a Point or a Number (as a scalar) argument. The message `transpose` creates a new Point by swapping the two coordinates of the receiver Point, while `dotProduct` gives the sum of the x product and y product of two Points:

<table><thead><tr><th>Expression</th><th>Result</th></tr></thead><tbody><tr><td>(1 @ 10) + (2 @ 12)</td><td>3 @ 22</td></tr><tr><td>(3 @ 22) - 10</td><td>-7 @ 12</td></tr><tr><td>(1 @ 10) * (3 @ 2)</td><td>3 @ 20</td></tr><tr><td>(1 @ 10) // 2</td><td>0 @ 5</td></tr><tr><td>(-2 @ -3) abs</td><td>2 @ 3</td></tr><tr><td>(2 @ 3) negated</td><td>-2 @ -3</td></tr><tr><td>(2 @ 4) dotProduct: (5 @ 6)</td><td>34</td></tr><tr><td>(2 @ 4) transpose</td><td>4 @ 2</td></tr></tbody></table>

## Rectangle

A Rectangle references a rectangular block of bits contained in a Form. It is represented by two Points: the top left Point, called **origin**, and the bottom right Point, called **corner**. Its width and height can then be calculated by:

width := corner x - origin x

height := corner y - origin y
which represent the number of columns and the number of rows of bits contained in the Rectangle. The Point represented by width @ height is called the extent of the Rectangle. A simpler way to calculate the extent is:

extent := corner - origin.

A Rectangle is usually created by sending the corner: or extent: message to a Point. For example, the following two expressions create two Rectangles covering the same area:

1 @ 1 corner: 100 @ 100

1 @ 1 extent: 99 @ 99

To illustrate the Rectangle instance messages, consider these rectangles, Box1 and Box2:

Box1 := 20 @ 0 corner: 150 @ 100.

Box2 := 70 @ 80 corner: 170 @ 120.

<table><thead><tr><th>Expression</th><th>Result</th></tr></thead><tbody><tr><td>Box1 top</td><td>0</td></tr><tr><td>Box1 bottom</td><td>100</td></tr><tr><td>Box2 left</td><td>70</td></tr><tr><td>Box2 right</td><td>170</td></tr><tr><td>Box1 center</td><td>85 @ 50</td></tr><tr><td>Box1 width</td><td>130</td></tr><tr><td>Box1 height</td><td>100</td></tr><tr><td>Box1 origin</td><td>20 @ 0</td></tr><tr><td>Box1 corner</td><td>150 @ 100</td></tr><tr><td>Box1 containsPoint: 50 @ 50</td><td>true</td></tr><tr><td>Box1 expandBy: 10</td><td>10 @ -10 corner: 160 @ 110</td></tr><tr><td>Box2 insetBy: 10</td><td>80 @ 90 corner: 160 @ 110</td></tr><tr><td>Box1 intersects: Box2</td><td>true</td></tr><tr><td>Box1 intersect: Box2</td><td>70 @ 80 corner: 150 @ 100</td></tr><tr><td>Box1 merge: Box2</td><td>20 @ 0 corner: 170 @ 120</td></tr><tr><td>Box1 translateBy: 10 @ 10</td><td>30 @ 10 corner: 160 @ 110</td></tr><tr><td>Box1 moveBy: 10 @ 10</td><td>30 @ 10 corner: 160 @ 110</td></tr><tr><td>Box2 moveTo: Box1 origin</td><td>30 @ 10 corner: 130 @ 50</td></tr></tbody></table>

Notice that the last two expressions modify Box1 and Box2 themselves, while others create new rectangles.
# Form

Class *Form* is a subclass of *DisplayMedium*, which in turn is a subclass of *DisplayObject*. *DisplayObject* and *DisplayMedium* are abstract classes; they do not hold any data. Their purpose is to group related methods together to be inherited by their subclasses. (Part 4 lists their protocols.)

As we said earlier, the main purpose of a *Form* is to provide a two dimensional view for a bitmap. It therefore has three instance variables: *bits* (which contains the bitmap), *width* (which contains the number of pixels horizontally), and *height* (containing the number of pixels vertically).

A *Form* also has three additional instance variables:

* *offset* is the Point on the *DisplayScreen* from which this *Form* was originally copied by sending message *fromDisplay*: to class *Form*.

* *byteWidth* is the number of bytes (8 bits) horizontally. For efficiency reasons, each row of a *Form* is allocated in an integral number of words (16 bits). Therefore, if its width is not an integral of the word size, the remainder bits in the last word of each row (the right hand side of the *Form*) are not used. This, however, is transparent to the user.

* *deviceType* denotes the type of device to which this *Form* belongs. When an instance of class *Form* is created, its *deviceType* is automatically set to 0, indicating that the *Form* resides in regular memory. When an instance of class *DisplayScreen* (a subclass of *Form*) is created, it is set to 1, indicating that the *Form* serves as the buffer for the display screen, whose address and size are dictated by the graphics adapter and graphics mode being used. The values of *deviceType* for instances of class *ColorScreen*, *ColorForm*, and *BiColorForm* are 1, 3, and 2, respectively.

A global variable, called *Display*, contains an instance of either *ColorScreen* or *DisplayScreen*. Every time you change the graphics mode, this variable is reinitialized to the exact size of the screen. It can be used as any other *Form*; when pixels are moved to *Display*, however, they are automatically shown on the screen. In other words, the contents of *Display* reflect your monitor's display screen.

The following messages can be sent to class *Form*, *BiColorForm*, or *ColorForm* to create new instances:

### fromDisplay: aRectangle

Answer a new *Form* whose extent equals *aRectangle*'s extent and whose content is copied from *aRectangle* area of the display screen.

### width: wInteger height: hInteger

Answer a white *Form* whose width is *wInteger* and height is *hInteger*.