The problem with examples is that to be interesting, they have to do something
interesting (to you, the reader) and anything that does something interesting needs some
programming. The Allegro CL IDE is a tool for designing and implementing user interfaces
to applications, along with a collection of tools which make the task of programming
easier, but it does not write the underlying program (the part that actually does the
work, often called the engine).

We will discuss two examples in this chapter. First is a simple example where we make a
form with two controls. We modify the event handler of one control so clicking on it
prompts the user for a string and then displays the string in the other control. We show
all steps and illustrate various windows and dialogs as we go.

Second, we discuss the application created by the Interface Builder Tutorial. We do not
discuss the steps of the Tutorial in detail. Rather we discuss aspects of the finished
application. If you see a feature of interest to your needs, you know that information on
implementing it can be found in the Tutorial.

3.1 A simple example with two communicating controls

In this example, we create a dialog with a button
and single-item-list. We will arrange
it so that clicking on the button displays a dialog asking the user to enter some text
that is then added to the items in the single-item-list.

Here is approximately what the system looks like when we have a project with one form
(form1):

We got this configuration by opening a project and then choosing New Form
from the File menu, specifying
Dialog for the type of form. We were asked where we wanted the project files to be saved,
and accepted the default c:\allegro-projects\project1\. (If you have used projects
before, you may be using project2 or project3 rather than project1.) You should have a
directory associated with the project. If we save files, we will save them here, as that
makes cleanup easier. It is actually not necessary to save files for the first example,
but if you do, it is best to place them is the project directory.

Second, we need to define a function that will be used in the application. This
function will display the dialog asking for a string. Go to the editor workbook window
(click View | Editor if the
editor workbook is not visible -- that command brings it to the front). This window uses a
tab control to display editor buffers, which may or may not be associated with files. It
starts with a single unsaved buffer named Untitled (there may be a form1 buffer as well):

Bring up the Apropos dialog by clicking Search | Apropos. Enter ask-user-for
in the String box and click Search. All symbols whose names start with ask-user-for
are displayed; ask-user-for-string is at the bottom.

Select it by clicking on it (it is selected in the illustration). Edit | Copy copies it to the
clipboard. Back in the Untitled buffer, Edit | Paste pastes it into the
buffer. Pressing F1 causes the help page for ask-user-for-string to be
displayed.

Now press `a'. The `a' choice, multiple-value-bind, is printed in the editor.

Notice too that the Lisp code indents as you go to new lines. When you are done, the
buffer should look like this:

Place the cursor just after the final parenthesis (or anywhere within the definition)
and click Tools
| Incremental Evaluation. You should see ask-for-string printed in the Debug
window. That indicates that the function definition has been evaluated in the Lisp
environment, as we want.

Note that there are shortcut keys for most of the menu choices we have mentioned
(typically Control-C for Edit | Copy, Control-V for Paste, Control-.
for Symbol Completion, and Control-E for Incremental Evaluation). However,
your editor mode may use those key combinations for editing operations making them
unavailable as menu shortcuts. The shortcuts are shown in the menu. Use them in preference
to the menu if desired and if they are not used by the editor.

Now we can design our application window. We will work with the
current project and form. Display the Project Manager window by
clicking View
| Project Manager.

Now select the blank form. If it is not visible, click on (and so select) form1 (not
saved) in the Project Manager and click on the View Selected Form button (shown
in the illustration).

The form should appear. The Inspector should be inspecting Form1. If it is not (or is
not visible), double-click on the interior of the blank form. The Inspector, inspecting
form1, will appear.

Also now display the code associated with form1 in an editor buffer. Do this by
selecting form1 in the Project Manager dialog and clicking on the View Selected Code
button.

This will display an unsaved form1 buffer in the Editor Workbook.

We will be adding code for event handlers for controls on the form in this buffer
presently. (Note: if we do not display this buffer before we start editing event
handling code, the initial event handling function code is stored in a hidden buffer. Then
when this form1 buffer is used, redefinition warnings will be displayed when the code is
compiled. Such warnings, if they appear, may be safely ignored.)

Now back to the inspection of Form1. (Double-click on the form if the inspector is
hidden). The inspector looks like this:

Change the title of the form to Simple Example by selecting the original value (Form1)
as follows. Select the text (Form1) which is the value of the title property and type Simple
Example. Alternatively, click on the extended editor button (with the three dots, on
the right, when the title property is selected, and replace Form1 with Simple Example in
the dialog that appears and click OK. The title of form1 will change to Simple Example.
(This change is already made in the illustration.)

Add a button to the form. Do this by clicking on the Button button on the Widget Palette (click in the background of the form if the widget palette is not visible). Make sure not to select the Default Button or Cancel Button. (If you leave the
mouse over a button on the Widget Palette, a tooltip appears saying which button it
is.) Once you have clicked on the Button button, click on the form where you want the
button to go. Here is the Widget Palette with the Button button and the
Single-item-list button (used later) identified:

Add a single-item-list to the form. Click on the single-item-list icon on the Widget Palette and click on the form where you want it to go.

The form will now look like this:

We want to change the name and the range of the single-item-list. Select the
single-item-list by clicking on it. Sizing handles (the solid squares) appear around the
selected component, as in the illustration. The Inspector window should now be inspecting
the single-item-list component. Click in the name
property, and type :list-box. Then click in the range field and type nil.
Here is the Inspector after both changes have been made. Notice the single-item-list on
the form is now blank (not illustrated). That is because we have set the range (the things
displayed) to nil. In the application, when a user clicks the button, a
dialog asking for a string will appear and whatever the user enters will become an item in
the single-item-list.

Now we want to change the name and the title of the button. Select the button, and go
to the Inspector, change the name field to :add-button and the title field to Add .
Here is the Inspector when those changes are made. Also notice the title of the button on
the form is changed to Add .

We want to specify the behavior when a user clicks on the button. When the user clicks,
the on-change event handler for the
button (if there is one) is called. We need a handler function that asks the user for a
string and makes the result an item on the single-item-list.

To modify the on-change event handler for the button, select the button and look
at the Inspector. Click on the Events button and you will see the event handlers for the
button. Actually, there aren't any  all the values are nil meaning all
events are ignored (the on-change-test,
the only item whose value is not nil, is an exception we do not discuss
here).

Click on the extended editor button  to the right, with the three dots -- for the on-change event. Since the button is on
form1, your code is added to the source file associated with form1. For this case, you are
placed in the form1 tab of the Editor Workbook (which we displayed earlier). A skeleton
function named form1-add-button-on-change is displayed. The name of the event
handler combines the name of the form, the control, and the event.

The cursor should be in the blank line in the middle of the function definition. Enter
the following line. The modified editor should look like the illustration.

(ask-for-string "Enter string to add to list" "Greetings")

Have the system evaluate the definition by placing the cursor in the definition or after
the last parenthesis -- i.e. after t) -- and clicking Tools |
Incremental Evaluation.

Now test the form by running the project, which means creating the window designed by
the form and running it so the controls are active. Click Run | Run Project. You
will be prompted to save the project files. Do so if you want but saving it is not
necessary yet. If you do save files, you are asked if you want to add the Untitled buffer
to the project and what its name should be. Call it excode.cl -- do not worry
about the file names now; we discuss them below in 3.2 Building an
application. After you have saved the files or not, an active dialog appears. Note:
if you did not display the form1 buffer in the editor workbook before clicking on the
on-change extended editor button for :add-button, you may see redefinition warnings at
this point. They result from the form1-add-button-on-change definition
being stored and evaluated in a hidden buffer and they may be safely ignored.

After you have saved the files or not, an active dialog appears. When you click on the
Add button, a dialog asking for a string, with default Greetings, should appear. We show
part of the form and the two dialogs.

Modify the string or not and click on Okay. You won't see a visual change on the main
dialog because we haven't done anything with the string just entered. We will now make use
of it. Do not stop running the project. We are going to change its behavior while it is
running by redefining the form1-add-button-on-change event handler.

Go to the form1 tab on the Editor Workbook. In the definition of the form1-add-button-on-change
function, select all lines below the line starting (declare, like this:

Add the following code, replacing all the current code below the declare line. The
illustration shows the appearance after these lines are typed in. Note that the text is
indented a couple of spaces and is indented after pasting in the editor buffer. It is
important that no left parenthesis except the very first in a Lisp form (the one before defun
in our window) be in the first column, or the editor will be confused about where the Lisp
form actually begins.

Note that list-box was found using the function find-sibling and the name, :list-box.
There are a number of find- functions. find-sibling is best in this case because
only the other controls on the form have to be examined. This function now changes the
range of list-box to include the entered string (adjoin adds an item to a list if
it is not already there  if the new string is equal to a string already in the
range, it is not added).

Go back to the running form and click on the Add button. Again you are prompted for a
string but now, when you enter one and click OK, the string is added to the
single-item-list. We have added Greetings, Hello, and The quick brown fox. All appear in
the illustration (The quick brown fox is truncated because we made no provision for lines
that do not fit).

We now have a dialog with the controls communicating with each other. And we were able
to modify the behavior while the form was running.

3.2 Building an application

We can convert this simple example into a standalone application. Early in the example,
we mentioned the Project manager window and used it (if needed) to display the blank form.
Since then we haven't said much about projects.

A project is a collection of modules associated with an application. With the modules
and Allegro CL itself, you can build your application. Modules are parts of the
application and have one or more associated files. Form1 is a module and has associated
files form1.cl and form1.bil (we discuss these types of files in more detail
in chapter 4).

We have to be sure that the project contains all the modules necessary, form1 and the
untitled (or excode.cl if you saved it) files. Lots of files are included in an
application so we strongly recommend that you start with an empty folder (like the C:\allegro-projects\project1
subfolder suggested by the system early in this discussion).

If you have saved excode.cl already, you were asked whether you wanted the
file added to the current project. If you answered `yes', the file will be included in the
project. If you answered `no', the file would be saved without adding it to the project.
Suppose that is what you did. You can add that file (or any other) to the project at any
time as follows:

With just form1 in the project, the general tab of the project manager should look like
this:

Add C:\allegro-projects\project1\excode.cl (save the Untitled buffer to that file
if you haven't already done so -- when you save, you are asked if you want to include the
file in the current project; if you answer `yes', the rest of this step is not necessary).
Add the file by clicking on the + button on the Project Manager toolbar and specifying the
file to the dialog that appears (you first have to identify the file type: CODE:
an existing source code file, in this case). Alternatively, select this buffer in
the Editor Workbook, right click on the editor pane, and select Add File to Project
from the shortcut menu that appears. (This avoids having to browse for the file.) After
the addition, the Project manager looks like this:

Save the files. (A project will not build unless all files are saved.) Click Files | Save All. This will
save all unsaved files associated with the project. If asked, save form1.cl to C:\allegro-projects\project1\form1.cl
and project1.lpr (the project file) to C:\allegro-projects\project1\project1.lpr.

A Creating Image window is created (it is not displayed by default but it
appears on the run bar). Information about what is happening while the application is
being built is printed in that window. Note that you cannot do anything while the image is
being created other than waiting. When the build completes (assuming it is successful), a
dialog like this appears:

If you click Run the EXE File, the application will run. If you click OK,
you return to the running Lisp and the IDE.

Look in the C:\allegro-projects\project1 folder. You should see project1.exe
and a bunch of other files.

You can start project1.exe by double-clicking on it to run the application. End
the application by clicking on the Close button of the dialog.

3.3 The Doodler Tutorial

The Doodler tutorial is another application whose complete source is included with the
IDE. The tutorial illustrates features of the Allegro CL IDE. We recommend that you run
the tutorial separately. Here we discuss features of the final product rather than taking
you through it step by step.

The code, examples, and so on can be found in the gui-builder-tutorial folder
and its subfolders. We strongly recommend that you use the tutorial to familiarize
yourself with the product. It exercises many of the features of the system, often showing
more than one way to achieve a particular end. It provides many programming examples
(already written). Our hope is that parts of the tutorial will give you examples that will
be useful to you in your own program design. And the result is a program that is at least
visually interesting even if it is not directly relevant to whatever application you want
to write.

3.3.1 What the doodler application does

The tutorial application is called the doodler (the name of the project is the
:interface-builder-tutorial project). It has four windows and dialogs: one for choosing a
background color,

one for defining a cycloidal curve (by specifying the three relevant coefficients),

one for listing and managing the curves defined,

and the main window upon which the specified curve or curves are drawn.

The files in the final subfolder of the tutorial folder show all the files associated
with the interface-builder-tutorial application after it is completed. Except for the
files util.cl, cycloid.cl, colorx.cl, all of which implement the
application  drawing the curve and choosing a color -- and the several bmp files,
providing the illustrations for the buttons, all the files needed for the application are
generated using the Allegro CL IDE. (Some files, such as the Help file doodler.hlp,
provide assistance in using the tutorial but are not needed by the doodler application.)

Here are the elements of the project listed by the doodler project manager.

We shall return to the doodler application as this chapter progresses. Now we discuss
developing applications in general.

3.3.2 Developing an application

Broadly, the steps to developing an application are these:

Decide what the application should do

. All applications start with initial data and
initial input, accept additional input from the user while running, and display results
based on the initial and runtime data. The results may be output continuously, or from
time to time, or only at the end. We use `output' in a wide sense, including visual
display on the screen, sound from speakers, control signals or messages to external
devices run by the program, and printing of data to some sort of file. You must decide,
broadly, what data your application will work on and what results will be displayed and
how these results will be calculated.

Write or procure the application engine

. In the last line of step one, we say you
must determine `how these results will be calculated'. We call the part of the program
which calculates the results the `engine'. It is possible the engine is already available
-- your task is to provide the user interface. Or maybe you have to write it yourself. The
engine in interface-builder-tutorial draws the curve in the doodler window. A compiler is
an engine, as is a searching program, a drawing program, an editor, and so on. Engines are
usually rather simple, in that they take inputs and produce outputs. It is the job of the
user interface to procure these inputs and make the outputs available in a useful form.

Decide on what to do with the results

. This step and the next (Decide on how to
collect the input) can be done in either order, but providing inputs programmatically (for
testing) is often easier and an application that cannot communicate results is useless. In
this step, you decide on the format in which the output of the engine will be provided --
displayed as a picture or text, as sound from a speaker, as a data file or data output
appropriate as input to another application, or as commands sent directly to a device. We
will mostly focus on display of results. Writing to a file is usually fairly
straightforward once you know what to write. The output for the interface-builder-tutorial
application is the pretty picture, for example. It could also (or in addition) be a copy
of the picture sent to your printer, a graphics file written to disk, or, if
interface-builder-tutorial was a component of a larger application, a pixmap object passed
to the larger application.

Decide on how to collect the input

. There are many ways to collect input: from a
file, from another program, from the user directly. Reading from a file is usually not
complicated. You just have to find the file and understand its format. Reading from
another program is often harder (because interprocess communication is often complicated)
but conceptually, at least, it is straightforward. Interactive input from the user will be
the main focus of our discussion, both in this chapter and in this manual as a whole.

Just as there are many ways to say something, there are many ways to collect input from
a user. The doodler mostly uses buttons, but some numeric input uses an editable text
control with an associated up-down control:

It also uses a color choice control that is a standard Windows dialog for choosing
colors. (Note: that dialog is not associated with one of the forms of the
interface-builder-tutorial project. It is displayed by ask-user-for-color.) Many
are designed for and can be used for collecting user input. User input from the mouse is
still input.

Implement 3 and 4

. Much of the work of implementing collection of input (4) and
display of output (3) can be done using the various tools supplied with the Allegro CL
IDE, as we describe in this manual.

Fiddle

. One of the most powerful features of Allegro CL is that modifications are
relatively easy and often can be done with a change in only one module while all others
can be left alone.

3.3.3 The steps in developing the doodler application

Decide what interface-builder-tutorial should do. Now, of course the real purpose of
interface-builder-tutorial is to show off the features of the application-building tools
available in Allegro CL, but that real purpose confuses our purpose. The apparent purpose
(which we discuss here) is to draw pictures (of cycloid curves) in a window, allowing
aesthetic enjoyment and obtaining information about how cycloid curves are affected by
changes in the defining equation. Here are three curves differing only in the value of the
A coefficient, 100, 200, and 300 for the smallest, middle, and largest curves.

Write or procure the application engine

. The engine in this application draws the
specified curves. The file cycloid.cl contains the code which calculates the
information necessary to draw the curves. (Curves like these are drawn by calculating the
x and y coordinates of many points of the curve. The curve is drawn by connecting those
dots.) Fortunately, this engine is provided and does not have to be written at this time.

Decide on what to do with the results

. The output is a drawing of one or more
curves. This drawing could be printed or written to a bitmap file, or drawn on the screen.
Doodler draws it on the screen.

Decide on how to collect the input

. What input might be necessary? Here is all the
information used to draw the curves:

The background color of the drawing pane.

How many curves to draw.

The A, B, and C coefficients of each curve.

The line color of each curve.

Additionally, the user can erase the drawing pane, center the drawing pane, and scroll
the drawing pane (all information about user desires that must be input somehow).

It is reasonable to have a window that lists all curves and allows the user to add and
delete curves from that list. Another window can be used for specifying the details of
each curve, and another for specifying the background color. Finally, the user must
somehow give the command to draw the specified curves. This arrangement (a description of
the actual interface-builder-tutorial windows) is obviously one choice among many. The
draw command could be placed on the doodler window rather than on the curves window, or
the erase button could be placed below the Draw All button on the curves window. The
background color could be implemented by a Background Color button on the doodler window
(that displayed a color choice dialog immediately) rather than having a background color
window with a few choices and an option to get more choices. There are no end of possible
arrangements, some being obviously unsatisfactory (dialogs for each coefficient rather
than one dialog for all three) but many being equally satisfactory.

Notice that this step is longer to describe that the others, because there are so many
possibilities and so few reasons to strongly prefer one choice over another.

Implement 3 and 4

. The tutorial goes into great detail about implementing 3 and 4.
We strongly recommend that you use the tutorial to learn the basics. In this chapter, we
will discuss some details of implementing 3 and 4 as a way of describing how to do things
that you will want to do in most all applications. However, we will not describe the
process of creating the interface-builder-tutorial application step by step.

Fiddle

. In 4 above, we mention some choices which are not obviously better than
other possibilities. Maybe the Draw All button should be on the doodler window itself,
like the erase button, rather than on the curves window. One reason for this decision is
the buttons on the doodler window have picture labels and there is no obvious icon for
draw like there is for erase. (The button on the doodler window that might be draw, the
one illustrating a drawn curve, actually displays the curve-dialog button.) Maybe there
should not be a Background Color window at all, just a color button on the doodler window
that directly displays the common color choice dialog. You can doubtless think of other
possible changes, some of which would, from your point of view, improve the
interface-builder-tutorial application. Note that the process of fiddling with an
application typically adds features, capabilities, and choices, thereby increasing the
application size and complexity and making it harder to maintain.

3.3.4 The doodler application windows

There are four windows associated with the doodler application. Each window is designed
by a form during the design process, so there are four forms in the
interface-builder-tutorial project.

These forms are listed on the Options tab of the interface-builder-tutorial Project
Manager. The project started (as all new projects do) with one form. Three others were
added after. Each form was added by clicking on New Form in the File menu. When you click
on File | New Form, this
dialog appears asking for the Window class of the form:

There are more choices shown in the illustration then will be present in a fresh
Allegro CL. The additional choices, doodler, curve-dialog, coefficient-dialog
and background-palette, were all added as the project was worked on. You are
choosing the type of window corresponding to the form. The default choices (everything but
the four listed as new) provide many capabilities, but creating new classes of devices is
common because you get to add features to the new device class without affecting any
existing example of that class. Consider the doodler choice (the device of the doodler
window). The doodler class is defined as follows (in final\doodler.cl):

That says doodler is a subclass of bitmap-window (a bitmap window is one with an
interior bitmap-pane upon which you can draw). It has two additional slots:
doodler-curve-dialog and doodler-background-palette. They are not initialized. Later, they
are given as values the two dialogs (corresponding to the curve-dialog and
background-palette forms). A show-curve-dialog method is defined as follows:

This method is specialized to instances of the doodler class (and thus cannot affect
any other window in the system). Without analyzing the code too closely, if the value of
the doodler-curve-dialog slot is non-nil, it is assumed to be a curve-dialog and
displayed; if it is nil, a curve-dialog is created and displayed. The argument to this
method is the current window (a doodler window).

Why are things done this way? The design is to have a button on the doodler window
which, when clicked, displays the associated curve-dialog. This is implemented by having
the system call show-curve-dialog with the parent (doodler) window as its argument when
the button is clicked. That action displays (creating if necessary) a curve-dialog
associated with the doodler window. The dialog knows which doodler window it is associated
with (because that doodler window is its parent) and the doodler window knows which
curve-dialog it has (because it is the value of the doodler-curve-dialog slot of the
doodler window).

3.3.5 Popping up a common dialog

Windows has provided many standard (called common) dialogs for common actions. One is
the file choice dialog (a dialog that lets you pick a file on the system) that everyone is
probably familiar with. Another is the color choice dialog. The background-color dialog
allows a user to choose a background color for the doodler window. Several choices are
provided along with a way to get many more choices and even make a custom color using a
common color-choice dialog (clicking the Other Color button).

Let us look at how this is implemented. The code is in background-palette.cl
(the code associated with the background-palette form) and colorx.cl, an auxiliary
source file not specifically associated with a form. In background-palette.cl, we
find the function called when the Add Colors button is clicked:

#| So far, a color-choose common dialog has been displayed and the user
has either specified a new color or canceled. If a new color is chosen,
it is added to the list of defined background colors and the multi-picture
button is expanded to include the new color.
|#
(when (not (setf color-name
(find-color-name dialog new-color)))
(setf color-name (new-color-name dialog))
(let* ((colors (range color-list)))
(setf (range color-list)
(append colors
(list (make-instance 'button-info
:name color-name
:image new-color
:string nil
:height nil
:tooltip nil
:help-string nil))))))
;; change which color is pressed
(setf (value color-list) (list color-name)))))

The call to ask-user-for-color
pops up a color choice dialog. The add-other-color function does what is
necessary when a new color is chosen. You can mimic the code here when you want to pop up
a common dialog and use the result of user action in your application.