[comp.lang.smalltalk] Example Program

budd@mist.cs.orst.edu (Tim Budd) (03/04/88)

Some poor soul wanted to see an example Smalltalk program, and I can
sympathize.  It is easy enough to find out how to mess your display up by
drawing spirals and arcs, but progressing from that to a real application
is a significant step, and one for which (as of yet) there doesn't appear
to be any real help.  To do almost anything reasonable you must at least
mention the words ``model-view-controller'', and these are VERY
intimidating to the uninitiated.

Here at OSU I try to expose junior level undergraduate students to
Smalltalk as part of a programming languages course.  It is the third
new language they see in one term (the other two being Setl and Prolog),
and I only have time for three simple assignments, spread over a period of
three weeks.  Clearly then there is a lot of hand holding, and we can't go 
too deeply into anything.  In the first assignment they simply encounter
the browser, find out about pens, and learn how to *do it*.  In the second
assignment they add some new classes and some new code, to create the
commander pen example.  For the third assignment they create a new
application, a simple McPaint style program.

At this point I could just give the McPaint application (it is only 6
methods), but, so as to not take all the fun of discovery away, I will
simply give the assignment that I hand out to my class, and let the reader
discover the code for themselves.

(I appologize for those who don't speak troff, but this seemed easier than
posting the troff output).

enjoy
--tim budd	budd@cs.orst.edu
	department of computer science
	oregon state university
	corvallis, oregon 97330

p.s. and before anybody asks, yes, I am the author of Little Smalltalk and
yes, I make them use Smalltalk-80, not Little Smalltalk. 
-------------------------------------------
.SH
CS 318 Programming Assignment 10
.br
Due, Friday March 11th
.PP
In your first Smalltalk assignment you investigated the nature of the
objects of class Pen.  In your second assignment you learned how to add new
functionality to the system, by adding new classes and methods.  In this
assignment we will create an application using these techniques.
The application will be a simple painting type program, similar to those
made popular on the Macintosh.
.PP
Before we can begin to describe this program, we need to consider first
the way that users communicate with a running Smalltalk program.  Users
convey information to a program by means of input devices, typically a
keyboard and/or a mouse.
Part of any program must therefore be devoted to listening to these
devices, and responding in an appropriate fashion.  This portion of the
program is called the \fIController\fP.
Another part of the program is concerned with presenting information in a
form meaningful to the user, by displaying a representation of the information 
on an output device, the bitmapped display screen.  This part of the
program is called the \fIView\fP.
Finally a third part of the program is the actual information being
manipulated by the user.  This is called the \fIModel\fP.
.PP
This way of dividing interactive programs up into three separate parts, the
Model-View-Controller triad, allows a great deal of flexibility and
modularity in the design of interfaces.  For example, if there are
different ways to view data (a graph and a table, for example) an
application can have several different views and one controller and one
model.
In our assignment we will create our own model and controller, and use a
standard view which is provided as part of the system.
.PP
Start in the browser in the first pane, and select ``add category''.  Add a
new category called \fIpainting\fP.  Then modify the class template in the
bottom pane to add a new class called \fBPaintController\fP, making it a 
subclass of \fBMouseMenuController\fP.  Instances of PaintController should
each have two instance variables, one called pen and one called canvas.
There are no class variables.
.PP
Once you have created and \fIaccepted\fP the class description, click the
\fIinstance\fP box in the second pane, then move to the
third pane and select ``add protocol''.  Add the name ``initialization''.
Then modify the method description in the bottom pane so that it looks as
follows:
.DS I
\fBinitialize\fP
	" painting program initialization "

	super initialize.
	pen \(<- Pen new.
	canvas \(<- Quadrangle fromUser.
	canvas borderWidth: 2.
	canvas display.
	view \(<- StandardSystemView new.
	view controller: self.

	" PaintController new startUp "
.DE
.PP
This method tells how to initialize your painting program.  The message
\fIsuper initialize\fP causes any initialization associated with the
superclass (MouseMenuController) to be performed.  To initialize our own
specific variables we make \fIpen\fP an instance of \fBPen\fP, and canvas
a quadrangle (the dimensions of which are obtained interactively from the user).
Our application will use the pen to write on the canvas.
We put a border on the canvas quadrangle and display it.  We then create an
instance of a standard system view to serve as our view, and indicate that
the controller for the view will be ourselves.
.PP
Now move to the third pane and add a protocol group for ``control
activity''.  In order to make our program respond to control events (that
is, mouse and keyboard events) we need to have some way of telling the
system when we want our program to take over control, and when we want to
give up control.  By default our program will be given control when it is
started, and the easiest thing is to say we will give up control when the
rightmost button is pressed.  This button is known, for historical reasons,
as the blue button.  By defining the following method we indicate that our
program should run as long as the blue button has not been pressed.  Enter
this method.
.DS I
\fBisControlActive\fP
	" run as long as the blue button is not pressed "

	\(ua sensor blueButtonPressed not
.DE
.PP
But what is it we want our program to do?  We want to create the ability to
draw on the canvas.  Let us set it up so that when the user presses the
leftmost button (called the red button) we start drawing, and when the user
presses the button again we stop drawing.  We can associate an action that
is to take place when a button is pressed by defining a method called
\fBredButtonActivity\fP.  We could start out, therefore, with something
like the following:
.DS I
\fBredButtonActivity\fP
	" draw on the canvas until the red button is pressed again "

	[ sensor redButtonPressed not ]
		whileTrue: [ pen goto: sensor cursorPoint ]
.DE
.PP
Enter this method, then go back to the initialize method and select the
example text given in the final comment and \fIdo it\fP.  A little square
will appear under the cursor.  Move down to the bottom of the display (so
that you aren't on top of the browser) and click the leftmost button - the
cursor will then move down to the lower left part of the rectangle, which
you can adjust to any size you want.  When you have a size you like, click
the leftmost button again.  Move into region you have outlined (the canvas
region) and press the left button (the red button) again.  The cursor
should disappear, and the pen should track the cursor, drawing on the
canvas.  Click the button to make it stop.  (I have found the mice in our
lab to be somewhat unresponsive - probably they need a good cleaning.  You
may have to click several times or hold down the mouse to get the action
you want.  When then cursor disappears you are drawing, and when the cursor
reappears you are not).
You stop the program by clicking the blue button (the rightmost button).
Note you can do this only when you are not drawing (when you have a
cursor).
.SH
Question 1
.PP
There are several problems with this.  Try drawing outside of the canvas.
Does it work?  When you stop drawing, move the cursor, and start drawing
again, what happens?
.sp 2i
.PP
Lets address the second problem first.  The problem is that when we start
we want to without drawing first move the pen to the location where the user 
pressed the mouse down. You may have discovered already that if you give a
pen the command \fBup\fP it will quit drawing, and if you give it the
command \fBdown\fP it will start again.  Thus, to move the pen to the
location where the mouse event took place we need the three statement
sequence:
.DS I
	pen up.
	pen goto: sensor cursorPoint.
	pen down.
.DE
.PP
Next, to tell if the current location is inside of the canvas, we can use
the fact that rectangles (such as our canvas) respond to the message
\fBcontainsPoint:\fP.  Then we want to wrap our goto message in a
conditional test, as follows:
.DS I
	(canvas containsPoint: sensor cursorPoint)
		ifTrue: [ pen goto: sensor cursorPoint ]
.DE
.SH
Question 2
.PP
Making both of these changes, write the revised method for
\fBredButtonActivity\fP.  Accept it in the system, and write it below.
.sp 2i
.PP
Well, pens are nice, but to be really interesting it would be useful if we
had other writing instruments, such as a brush or an eraser.  Both of these
can be described as modifications of attributes of our pen.  A brush can be
described as a pen with a nib size of 4 and a black mask, an eraser as a
pen with a nib size of 4 and a white mask, and of course a pen has a nib
size of 1 and a black mask.  If you don't remember how to modify nib sizes
and masks, go back to class \fBPen\fP and browse around.
.PP
Well, how do we give the user the ability to make changes?  The obvious
solution is a menu.  Let us use the middle button (the so called ``yellow''
button) for our menu selector.
Our superclass, \fBMouseMenuController\fP, allows us to create a menu very
easily with a single message.  Go back to our initialization message, and
add the following line to the very end.
.DS I
	self yellowButtonMenu: (PopUpMenu labels: 'pen\ebrush\eerase' withCRs)
		yellowButtonMessages: #(doPen doBrush doErase).
.DE
.PP
This message creates a new menu (an instance of PopUpMenu) with labels pen,
brush, and erase.  The message withCRs takes a string and repaces
backslashes with carrage returns, which separate the different menu items
from each other.  When the user selects one of these, one of the messages
doPen, doBrush or doErase will be activated.  Each of these should change
the characteristics of our pen into the desired format.
After adding this line you may want to try your application.
Start it up.  When you are not drawing (that is, when you have a cursor),
try pressing the middle (yellow) button.  A menu should appear.  Move out
of the menu before releasing the cursor, otherwise you will receive an
error box, since the controller will try to call a method that does not
exist yet.
.PP
Now move to the third pane, select ``add protocol'', and create the
category ``pen modification''.  Then in this group, create the three
methods doPen, doBrush, and doErase.  Each of these should consist of just
two statements, which set the pen nib and mask appropriately.
.SH
Question 3
.PP
Write below the three methods doPen, doBrush and doErase.
.sp 2i
.PP
Go back to the initialization method and start your application up again.
Note how the pen should stay in the borders of the canvas.  Try using the
middle button to change the pen characteristics.  Note that you can only
change the pen characteristics when you are not drawing (that is, when you
have a cursor).
.PP
There are of course a lot of additions we might want to make to our
program.
For example, you might want to allow gray tones, rather than simply black
and white.  You might also want to draw other forms, such as circles and
rectangles.  You may wish to investigate these other graphics classes and
think about how this might be done.
.PP
When you are all finished, file your solution out (see handout on filing
out).  Attach to your answers to the questions posed here a copy of the
filed out form of your solution.