# Tutorial

To use FormsVBT, you need a copy of SRC Modula-3 (Version 3.3 or later) and an X server for your system. If you have these, you may want to compile and run the example programs as you read this chapter.

## Getting Started

The first example program is in the file `Hello.m3`:

    MODULE Hello EXPORTS Main;
    IMPORT FormsVBT, Trestle;
    VAR fv := FormsVBT.NewFromFile("Hello.fv"); BEGIN
      Trestle.Install(fv);
      Trestle.AwaitDelete(fv)
    END Hello.

The program builds a form (sometimes called a ``dialog box'' or a ``user interface'') whose description is contained in a file named `Hello.fv`. It installs the form in a top-level window, and then waits until that window is deleted by the user. The window installed by the program is shown in the left half of Fig. [fig:hello] .

The ``Hello FormsVBT!'' example program. The initial version is on the left; the second version on the right.

The file Hello.fv contains the following S-expression:

    (VBox
      (Text "Hello FormsVBT!")
      (Bar)
      (HBox (Text "Left") (Bar) (Text "Right")))

The top-level component is a `VBox`. A `VBox` takes an arbitrary number of ``children'' (sub-components) and arranges them vertically from top to bottom. This `VBox` has 3 children: `Text`, `Bar`, and `HBox`. A `Text` displays a text string, a `Bar` draws a line orthogonal to the orientation of its parent, and an `HBox` arranges its children horizontally, from left to right. The `HBox` has 3 children, two `Text`s and one `Bar`.

The standard way that you compile and link your programs is to use `m3build`. The `m3makefile` for the ``Hello FormsVBT!'' application is as follows:

    import         (formsvbt)
    implementation (Hello)
    program        (Hello)

Then you can compile and link the `Hello` program by typing the shell-command `m3build -S` in the directory containing the source code.

Actually, most Modula-3 programmers follow the convention of storing all of the source files for an application in a directory called `src`. The `m3build` command, when run from `src`'s parent directory, stores all of its derived files (including the executable) in a subdirectory whose name depends on the platform on which you are running. For example, on DECstations, the derived directory is `DS`; on an Alpha running OSF, the directory is `AOSF`. When you follow this directory structure, you should invoke `m3build` without any arguments.

Here's a slightly fancier version of the interface (shown in the right half of Fig. [fig:hello] ):

    (Rim (Pen 20)
      (Border (Pen 1)
        (Rim (Pen 2)
          (Border (Pen 2)
            (VTile
              (Text "Hello FormsVBT!")
              (HBox
                (LabelFont (PointSize 240))
                (Color "White")
                (Text (BgColor "Pink") "Left")
                (Bar)
                (Text (BgColor "VividBlue") "Right")))))))

The top-level component is a `Rim` whose `Pen` property has a value of 20. A `Rim` must contain exactly one child (a `Border` in this case), and it surrounds its child with some background space. Here, the `Rim` provides 20 points of background space between each edge of the window manager's window frame and the rest of the interface. A `Border` is just like a `Rim`, but draws with the foreground color instead of the background color. We replaced the `VBox` with a `VTile`, and deleted its `Bar` child. A `VTile` is like a `VBox`, but it also automatically inserts a dividing bar between its children; by dragging the dividing bar, the user can control the division of space among the children. In this example, the `HBox` has been given two properties, `Color` and `LabelFont`. These control the foreground color and font used by the `HBox` and all of its descendants. Similarly, the `BgColor` property changes the background color used.

The fancy version of ``Hello FormsVBT!'' is in the file `HelloFancy.fv`. To run the application using this file, either modify the application to use `HelloFancy.fv` or rename the file `HelloFancy.fv` to be `Hello.fv`. Alternatively, you might find it enjoyable to run the FormsVBT interactive UI builder, `formsedit`. Just type the shell-command

        formsedit HelloFancy.fv

Exercise 1 Write the FormsVBT S-expression for T^4, a Trestle Tiling Monster of Order 4. (See the Trestle Tutorial, Fig. 2 on page 5.)

## Resources

A resource is constant data needed by an application program at runtime; often it is ``loaded'' at startup time. Almost all FormsVBT programs have resources, such as the `.fv` (pronounced ``dot ef vee'') files that specify the user interface. Other typical resources specific to an application include bitmaps, cursors, and help-texts.

When an application is built, its resources can be ``bundled'' with the executable image. The primary benefit of this feature is that applications are self-contained with respect to the resources they need. Thus, you can copy an executable to a remote site and you won't need to copy the resource files and install them in the same place as they were when the application was built. Also, your application will be insulated against changes in library resources.

The easiest way to do this is to name the resources and the bundle in the `m3makefile`, as in this example:

    import         (formsvbt)
    resource       (Hello.fv)
    bundle         (HelloBundle)
    implementation (Hello)
    program        (Hello)

The second line declares that there is a resource named `Hello.fv`. The third line has the effect of collecting all the named resources (only one in this case) and creating an interface called `HelloBundle` that provides access to them. The program would then be modified to look like this:

    MODULE Hello EXPORTS Main;
    IMPORT FormsVBT, HelloBundle, Rsrc, Trestle;
    VAR
      path := Rsrc.BuildPath(HelloBundle.Get());
      fv   := NEW (FormsVBT.T).initFromRsrc ("Hello.fv", path);
    BEGIN
      Trestle.Install(fv);
      Trestle.AwaitDelete(fv)
    END Hello.

The call to `HelloBundle.Get` returns a bundle that is used to create a resource-path, which is then searched by the `initFromRsrc` method.

But what if you want the application to use new resource files? For example, you might have changed some details of the `.fv` file that don't require any changes to the application code. Do you have to rebuild the entire application?

Fortunately, the answer is no. However, you do need to tell FormsVBT that you want it to look for those resources in the file system before it looks for them among the resources that were bundled into the application. You do this by changing the resource-path so that it includes one or more directories before the bundle.

The convention is to use environment variables whose names are spelled by combining the program's name with the string `"PATH"`. This variable should be set to a list of directory-names, each separated by a colon. So, if you want to run the `Hello` program using the `Hello.fv` file that's in Smith's home directory instead of the one that's bundled with the application, you would type something like this shell command:

        setenv HelloPATH /user/smith

In the program, you would construct a resource-path that included this directory by adding the name `HelloPATH`, prefixed with a dollar sign:

    MODULE Hello EXPORTS Main;
    IMPORT FormsVBT, HelloBundle, Rsrc, Trestle;
    VAR
      path := Rsrc.BuildPath("$HelloPATH", HelloBundle.Get());
      fv   := NEW (FormsVBT.T).initFromRsrc ("Hello.fv", path);
    BEGIN
      Trestle.Install(fv);
      Trestle.AwaitDelete(fv)
    END Hello.

## The FormsVBT Language

Syntactically, there are three types of components in FormsVBT: leaves, filters, and splits. A leaf has no children; a filter has exactly one child; and a split has any number of children.

The FormsVBT leaf components include passive objects like texts and pixmaps, as well as interactive objects like scrollbars and type-in fields.

A filter modifies its child's looks or behavior in some way. We've seen how a `Border` draws a border around its child. Another common filter is `Boolean`. It adds a check box to the left of its child and makes the box and the child sensitive to mouse clicks. It's important to realize that the child may be any arbitrarily complex arrangement of components, although a `Text` component is the most common.

The purpose of most splits is to divide the display area among its component-children (sub-components). In addition to the horizontal and vertical splits that we've seen, FormsVBT provides a temporal split (`TSplit`) to display exactly one child at any given time, and a z-axis split (`ZSplit`) to display children as overlapping subwindows.

Components are written as lists containing the component's type, followed by some number of properties, followed by some number of sub-components. Properties are written as lists containing a keyword and a value. For example, in the S-expression:

      (HBox
        (LabelFont (PointSize 240))
        (Color "White")
        (Text "Left")
        (Bar)
        (Text "Right")

the parent-component's type is `HBox`. This component has two properties; the first property has the keyword `LabelFont` and the value `(PointSize 240)`; the second has the keyword `Color` and the value `"White"`. It has three sub-components: `(Text "Left")`, `(Bar)`, and `(Text "Right")`.

The value of each property is type-checked when the description is parsed. The possible types include strings, integers, and real numbers, as well as more complicated types like color and font specifications.

So far, we have seen two kinds of properties. Class properties, like `Pen`, are defined in conjunction with specific components, and are allowed only on components of that class. Inherited properties, like `Color` and `LabelFont`, may be specified for any component, though they are not relevant to all component types. The inherited properties have the feature that a value specified for one component becomes the default value for all descendants of that component. Thus an inherited property applies not to one component, but to an entire subtree.

FormsVBT supports a third type of property, universal properties. A universal property can be specified on any component, and its value applies only to that component. <--! %Thus, universal %properties are like class properties in the sense that their values %apply to the particular component on which the property is specified. %Universal properties are also like inherited properties in the sense %that they can be specified for all components. However, the value of a %universal property does not propagate; it applies only to the %component on which it is specified. % To summarize, the FormsVBT language offers a rich set of composition mechanisms % and a variety of predefined objects, allowing easy specification and % implementation of complex user interfaces. This philosophy is quite similar to % that of InterViews~\cite{interviews}, a popular C++ toolkit running on X. % Nearly all of the primitive objects found in the VBTkit and Trestle window % system, upon which FormsVBT is based, have counterparts in InterViews. -->

Exercise 2: In `HelloFancy.fv`, wrap a `Scale` component around the top-level `Rim`. The `Scale` has two class properties: `HScale` and `VScale`. What happens when the values of both of these properties are set to 1.75? What happens when you nest `Scale` filters?

## The Three-Cell Calculator Application

A more interesting application is a three-cell calculator. Readers may wish to compare the FormsVBT implementation of this example with that of SUIT [suit] . The user can enter two numbers and an arithmetic operation to perform on the two numbers. The result is computed and displayed whenever the user selects a new arithmetic operation or types a new number. Fig. [fig:calc3cell] shows the application in action.

The user interface is described by the following FormsVBT expression:

    (Shape (Width 300 + 100 - 50) (Height + 25)
      (Rim (Pen 20)
        (VBox
          (HBox
            (VBox Fill (Numeric %num1 =5) Fill)
            (Radio %functions =add
              (VBox
                (Choice %div "divide")
                (Choice %mul "multiply")
                (Choice %sub "subtract")
                (Choice %add "add")))
            (VBox Fill (Numeric %num2 =2) Fill)
            (Text "=")
            (Text %result LeftAlign ""))
          (Glue 10)
          (HBox Fill (Guard (Button %exit "QUIT")) Fill))))

The tokens that start with percent signs are names assigned to components. For example, the `Text` component where the application stores the result of each computation is named `result`. An application can access only named components at runtime.

The three-cell calculator application.

This form contains the following components that we have not seen before:

- A `Shape` is used to give a component explicit size constraints, typically as a function of its child's size. Here, the `Shape` declares that its acceptable width is between 250 and 400 points, and its preferred width is 300 points. The preferred and minimum height of the `Shape` are the same as those of its child; its maximum height is 25 points more than the maximum of its child.
- `Fill` and `Glue` are used as children of an `HBox` or `VBox`. `Fill` displays as background space that will ``stretch'' as needed in the orientation of its parent. It is used here to keep the `Numeric` component centered vertically between the top of the string `divide` and the bottom of the string `add`. `Glue` displays as background space that doesn't stretch. In this form, it provides 10 points of space. Judicious use of `Fill` and `Glue` will facilitate creating very regular and pleasing-looking user interfaces.
- `Numeric` components are numerical widgets; in our example, the one on the left has an initial value of 5, and the one on the right has an initial value of 2. A user can change the displayed number by clicking on the plus or minus buttons, or by typing in the type-in field located between the buttons. To start typing, the user needs to move the keyboard focus to the type-in region by clicking there with the mouse. Also, nothing about the typing is reported to the application until a carriage return is typed. At that point, the application is notified that the `Numeric` has a new value, but not what the user did to enter this new value. (If the application really wants to find this out, it can inquire whether the user clicked on the plus or minus buttons, or entered a new number in the type-in region.)
- The `Radio` unites all of the `Choice` components which are among its descendants into radio buttons. A `Choice` adds a diamond to the left of its child and causes the diamond of the selected choice to appear dark and recessed. The application is notified whenever the user changes the selected item. Note that a `Radio` does not impose any particular geometric arrangement on the layout of its radio buttons.
- A `Button` adds the ``look-and-feel'' of a button to its child. The application is notified when the user clicks on a button. Again, keep in mind that the contents of a button can be any arbitrary arrangement; here, it's a simple text string.
- The `Guard` component requires that you click on it twice in a row before the program``quits''. The first click removes the ``guard'' (shown visibly by the diagonal lines) and allows subsequent mouse activity to be reported to its descendant, a `Button` in this case. The second click causes the `Button` to be invoked, and the guard to be reinstated. A `Guard` is usually put around a `Button`, but it may also be put around any component. For example, if you wanted to protect the ``divide'' radio button, you'd simply wrap a `Guard` component around the first `Choice` expression.

In FormsVBT, as in most GUI toolkits, an application is structured as an initialization routine, which runs in one thread, and a collection of event-handling procedures, which run in other threads. When an application is run, it initializes dialogs and then transfers control to the toolkit. The main thread waits until the toolkit returns control, which it does when all the dialogs have been deleted.

Here is the complete application for the three-cell calculator (see the file `Calc3Cell.m3`):

    MODULE Calc3Cell EXPORTS Main;
    IMPORT Fmt, FormsVBT, Text, Trestle, VBT;

    PROCEDURE NewForm (): FormsVBT.T =
      VAR
        fv  := FormsVBT.NewFromFile ("Calc3Cell.fv");
        qcl := NEW (FormsVBT.Closure, apply := Quit);
        ccl := NEW (FormsVBT.Closure, apply := Compute);
      BEGIN
        FormsVBT.Attach (fv, "exit", qcl);
        FormsVBT.Attach (fv, "num1", ccl);
        FormsVBT.Attach (fv, "num2", ccl);
        FormsVBT.Attach (fv, "functions", ccl);
        RETURN fv
      END NewForm;

    PROCEDURE Quit (cl  : FormsVBT.Closure;
                    fv  : FormsVBT.T;
                    name: TEXT;
                    time: VBT.TimeStamp) =
      BEGIN
        Trestle.Delete (fv)
      END Quit;

    PROCEDURE Compute (cl  : FormsVBT.Closure;
                       fv  : FormsVBT.T;
                       name: TEXT;
                       time: VBT.TimeStamp) =
      VAR
        answer: REAL;
        first        := FLOAT (FormsVBT.GetInteger (fv, "num1"));
        second       := FLOAT (FormsVBT.GetInteger (fv, "num2"));
        fn           := FormsVBT.GetChoice (fv, "functions");
      BEGIN
        IF Text.Equal (fn, "add") THEN
          answer := first + second
        ELSIF Text.Equal (fn, "sub") THEN
          answer := first - second
        ELSIF Text.Equal (fn, "mul") THEN
          answer := first * second
        ELSIF Text.Equal (fn, "div") THEN
          answer := first / second
        END;
        FormsVBT.PutText (fv, "result", Fmt.Real (answer))
      END Compute;

    BEGIN
      VAR fv := NewForm(); BEGIN
        Trestle.Install(fv);
        Trestle.AwaitDelete(fv)
      END
    END Calc3Cell.

The parameters to an event-handler (e.g., `Quit` and `Compute` in the `Calc3Cell` program) identify the dialog (`fv`) in which the event happened and the name of the interactor causing the event.

The event-handler's first parameter, named `cl` in this example, is a `FormsVBT.Closure` that is specified when the event-handler is attached. Its `apply` method is the event-handler. The standard way of passing additional information to the event-handler is to create a subtype of `FormsVBT.Closure`, with new fields, and possibly new methods, for handling the new information. The `time` parameter is a timestamp associated with the user event that caused the event-handler to be invoked. The timestamp is needed for certain operations, like acquiring the keyboard focus.

We say that a component ``generates an event'' when the user does something in a component that causes the event-handler to be invoked. The semantics of what causes an event to be generated is specific to each component.

The Three-Cell Calculator application creates a form and passes it to Trestle, the window manager, which ``installs'' it, just as the ``Hello FormsVBT!'' application did. Here, as part of building a form from the S-expression in file `Calc3Cell.fv`, we also attach event-handlers to the components to which the application will respond. The `Quit` event-handler, which is attached to the component named `exit` (the button labeled ``QUIT''), deletes the window from Trestle. The `Compute` event-handler, which is attached to both of the `Numeric` components as well as the radio buttons, retrieves the values stored in both `Numeric` components, determines which arithmetic function the user selected, performs the operation, and then displays the result.

Exercise 3 Add your favorite operator to the application and to the user interface. (If you're undecided about which operator is your favorite, try GCD.)

## Improving Readability

The Three-cell Calculator S-expression illustrates a number of common abbreviations that help make the FormsVBT language more readable.

A percent sign is an abbreviation for the `Name` property. That is, the FormsVBT parser reads `%xyz` exactly as if it were `(Name xyz)`.

An equals sign is an abbreviation for the property called `Value`. That is, the FormsVBT parser reads `= xyz` exactly as if it were `(Value xyz)`. By convention, any component whose value can be changed interactively by a user has a `Value` property.

Components that display some type of object, like a string or a pixmap, specify the object using a property called `Main`. For example, to display a pixmap from a file named `Trumpet`, you'd say `(Pixmap (Main "Trumpet"))`. However, the `Main` property can be abbreviated by omitting the keyword `Main` and the associated parentheses, e.g., `(Pixmap "Trumpet")`.

A `Text` component that has no properties other than `Main` can be further abbreviated simply by giving a string. For example, `(Text (Main "QUIT"))` can be reduced to `(Text "QUIT")` and then to `"QUIT"`. Other examples of this are the children of the four `Choice` components in the last program. If you want to specify any properties on a `Text` component (such as a name, font, color, or alignment), you can abbreviate `Main`, but you still need to write `(Text ...)`.

Boolean properties have a value of either `TRUE` or `FALSE`. The default value of all Boolean properties is `FALSE`. Mentioning the name of a Boolean property is an abbreviation for specifying a true value. For example, in the Three-Cell Calculator, the token `LeftAlign` is an abbreviation for `(LeftAlign TRUE)`.

Finally, leaf components without any properties can be written without parentheses, e.g., `Fill`.

The following chart summarizes these abbreviations:

`(Text "t")` | `"t"`
`(Name n)` | `%n`
`(Value v)` | `=v`
`(Main m)` | `m`
`(boolprop TRUE)` | `boolprop`
`(proplessleaf)` | `proplessleaf`

Exercise 4 The following interface contains a textual label, a type-in field, and a button:

The interface is 250x75 points, and it uses `Button`, `Frame`, `Pixmap`, `Rim`, `Shape`, `Text`, and `TypeIn` components, in addition to some `HBox`es, `VBox`es, `Glue`s, and `Fill`s. Appendix [ap:longcatalog] describes the class-specific properties for each component. Write a concise FormsVBT expression for this form.

FormsVBT provides two additional ways to make S-expressions more readable. First, an S-expression can be split across multiple files (resources). To insert a file named `HelpDialog.fv`, just include the expression

        (Insert "HelpDialog.fv")

wherever you want the file to be inserted. The `Insert` expression can appear anywhere in an S-expression; logically, it is replaced by the contents of the named file before the S-expression is parsed.

The second way to make the form more readable is by using macros. Syntactically, a macro is an inherited property with the name `Macro`. For details on macros, see Section [sec:language-macros] .

## Separating the UI from the Application

One of the ways that user interface toolkits like FormsVBT simplify the construction of interactive, graphical applications is by forcing a separation of the interaction-specific parts from the application-specific parts. This allows the interface designer to concentrate on the design of the interface and the application programmer on the implementation of the application-specific code.

In FormsVBT, the only UI components known to the application are those that are given names. The application is insensitive to the layout of components and to the existence of all unnamed components. There is even some insulation between the application and the UI for named components: one component may be replaced with another whose behavior with respect to the application is the same.

For instance, in the Three-Cell Calculator interface from Section [sec:calcIntro] , we could replace the `Text`-component named `result` with any other component that can store text, such as a `Typescript`. A `Typescript` would capture a history of all values computed by the application. (We would also need to delete the `LeftAlign` and `Main` properties to make this change.)

We could also replace the radio buttons with items in a pulldown menu by replacing this expression

      (Radio %functions
        (VBox
          (Choice %div "divide")
          (Choice %mul "multiply")
          (Choice %sub "subtract")
          (Choice %add "add")))

with the following expression:

      (Menu "?" (Radio %functions =add
        (HBox
          (VBox
            (Choice MenuStyle %div "divide")
            (Choice MenuStyle %mul "multiply"))
          (VBox
            (Choice MenuStyle %sub "subtract"))
            (Choice MenuStyle %add "add")))))

See Fig. [fig:calc3cell-menu] .

A modified UI for the three-cell calculator application. The cursor (not visible in the figure) is over the string ``multiply.''

The first child of a `Menu` is the ``anchor'' of a pulldown menu; click on it to get a menu displayed. The second child of a `Menu` is an arbitrary S-expression, displayed when the user clicks on the anchor. In the example above, the first child is a `Text` component displaying a question mark. The second child contains four radio buttons, arranged in a 2-by-2 matrix. The `MenuStyle` property causes a radio button to react when the mouse rolls into it rather than on a mouse click.

The contents of this menu emphasizes an earlier point about composition. A `Menu` does not impose any structure on the contents of the menu. One merely composes a `Menu` out of 2 children: a child that is the anchor button, and a child that appears when the anchor button is activated. A ``traditional'' pulldown menu is a `VBox` whose children are `MButton`s.

Exercise 5 Change the program so that a symbol for the current operator is displayed instead of the question mark. Hint: Assign name to the quoted question mark, by using the expanded format `(Text %op "?")`, and call `FormsVBT.PutText` to change what is displayed in a `Text` component.

## Subwindows

The three-cell calculator will crash if we try to divide by 0. Let's change the application to pop up a dialog box warning the user if there is an attempt to divide by 0. We need to modify the `Compute` event-handler by adding a test for a divisor equal to zero just before the division:

        ...
        ELSIF Text.Equal (fn, "div") THEN
          IF second = 0.0 THEN
            FormsVBT.PopUp (fv, "errorWindow");
            RETURN
          END;
          answer := first / second
        END;
        ...

The call to `FormsVBT.PopUp` will cause the named dialog to appear.

It is easy to add a dialog named `errorWindow` to the calculator's S-expression that was given in Section [sec:calcIntro] . The S-expression becomes the following:

        (ZSplit
          (ZBackground (Shape ...))
          (ZChassis %errorWindow
            (Title "Error Message")
            (Rim (Pen 20)
              (Text %errorText "Can't divide by zero."))))

A `ZSplit` takes an arbitrary number of children and displays them as overlapping windows. The first child is the background; it is always visible. The visibility and location of the other children are under program control. The `ZChassis` wraps a ``banner'' around its child; the banner is responsive to mouse activity for the common window controls of closing, moving, and resizing. A call to `FormsVBT.PopUp` will cause a specified child of a `ZSplit` to appear. By default, a `ZChassis` is not initially visible.

Another common use of subwindows is to allow a user to specify additional information for a command. For example, the ``Save As...'' button found in many applications pops up a dialog box, which is a subwindow, with a way to enter the name of a file. A button like ``About Bazinga...'' pops up a subwindow containing information about the application called Bazinga.

In situations like these, it's a burden on the programmer to write an event-handler that simply calls `FormsVBT.PopUp`. To simplify this common case, FormsVBT provides a `PopButton`. This component is just like a `Button`, but before its event-handler is called, it causes a designated subwindow to appear. In practice, applications often don't need to attach any event-handler to a `PopButton`.

For grins, we'll now change the original three-cell calculator user interface so that the radio buttons are in a subwindow that is completely controlled by the user. Clicking on the `"?"` menu will cause the subwindow to appear. The window can be closed and repositioned without any application code. We need make two small changes to the original S-expression given in Section [sec:calcIntro] to add subwindows. First, replace the radio buttons by a button that causes a subwindow to pop-up. That is, change

      (Radio %functions ...)

to

      (PopButton (For fnWindow) "?")

Second, move the `Radio` expression into a subwindow, by enclosing it in a `ZChassis`, and wrapping a `ZSpit` around the root. Now, `Calc3Cell.fv` looks like this:

      (ZSplit
        (ZBackground (Shape ...))
        (ZChassis %errorWindow ...)
        (ZChassis %fnWindow
          (Radio %functions ...)))

Exercise 6 Add an ``About Three-Cell Calculator...'' button. It should pop-up a subwindow with appropriate information. If you want to put the button inside of a pull-down menu, use `PopMButton`.

## Modal Dialogs

When a subwindow appears, the rest of the form and all other subwindows remain active. In the case of the operator-subwindow in Section [sec:subwindows] (i.e., the `ZChassis` named `fnWindow`), this behavior was desirable. However, this behavior may not be desirable for the error-message subwindow. That is, some application writers would like to force the user to explicitly close the error message subwindow before continuing to interact in the application. In the UI jargon, this is called a modal dialog.

A simple way to do this is to bring up the error subwindow as before, but also to ``deactivate'' the background---make it unresponsive to user actions---while the subwindow is displayed. When the dialog is finished, we ``re-activate'' the background. A FormsVBT component called `Filter` is used to set the reactivity of its child to be active (the default case), passive (mouse and keyboard events are not sent), dormant (like passive, but it also grays out the child and changes the cursor), or vanished (like passive, but also draws over the child in the background color, thereby making it invisible).

Changing the modeless subwindow in the calculator so that it is modal requires only a trivial change. First, add a `Filter` just inside the `ZBackground`. Name this component `zbg`. Second, in the application, add

        FormsVBT.MakePassive(form, "zbg")

after the call to `FormsVBT.PopUp`. Finally, you need to register an event-handler for the `ZChassis` named `errorWindow`. The event handler will be invoked when the subwindow is closed; it contains the following line:

        FormsVBT.MakeActive(form, "zbg")

You might also wish to eliminate the banner on the subwindow. To do so, change the `ZChassis` to be a `ZChild`, and add a `CloseButton` somewhere in the subwindow. The `CloseButton` button will cause the subwindow in which it is contained to be taken down. Fig. [fig:modal] shows the modified application.

The three-cell calculator application with a modal dialog.

Exercise 7 Make the error window in the three-cell calculator modal in the manner suggested in this section: In the `.fv` file, add a `Filter` inside the `ZBackground`, change the error subwindow from a `ZChassis` to a `ZChild`, and add a `CloseButton` to the error window. In the `.m3` file, change the application code so that the background is made passive when the error window appears, and re-activated after error window disappears.

Exercise 8 When the error dialog appears while the subwindow containing operators is visible, the operators are not deactivated, although the main form is deactivated. Change the form so that everything except the error subwindow is made passive. Don't modify the application! (Hint: Use two `ZSplit`s, one the background child of the other.)

## A File Viewer

It's easy to hook up the FormsVBT text-editing widget to an application to make a bona fide text editor. The file-viewer application, shown in Fig. [fig:viewer] , contains a type-in area on the top for entering the name of a file and a fully functional text editor that occupies the bulk of the window. The text editor is in read-only mode.

The S-expression for the application, in the file `Viewer.fv`, is quite simple:

    (Rim (Pen 10) (Font (WeightName "Bold"))
      (VBox
        (HBox
          (Frame Lowered (TypeIn %fileName))
          (Glue 10)
          (Button %exit "QUIT"))
        (Glue 10)
        (Shape (Height 200 + inf) (Width 300 + inf)
          (Frame Lowered (TextEdit ReadOnly %editor)))))

The application is structured as in the three-cell calculator application in Section [sec:calcIntro] . A `NewForm` procedure converts the S-expression into a runtime object and registers event-handlers. Only one event-handler is needed here; `ReadFile` is attached to the type-in field `fileName`. It is invoked whenever you type a carriage return in the type-in field. The code is straightforward:

    PROCEDURE ReadFile (cl  : FormsVBT.Closure;
                        fv  : FormsVBT.T;
                        name: TEXT;
                        time: VBT.TimeStamp) =
      VAR fname := FormsVBT.GetText (fv, "fileName");
      BEGIN
        TRY
          FormsVBT.PutText (fv, "editor", GetFile (fname));
        EXCEPT
          Rd.Failure =>
            FormsVBT.PutText (fv, "editor", "");
            FormsVBT.PutText (fv, "fileName", "");
        END;
      END ReadFile;

The event-handler first retrieves the string you typed into the type-in field named `fileName`. It then calls an internal procedure `GetFile` to retrieve the contents of a file by that name, and finally stores the contents into the text-editor widget. If an error is encountered while trying to retrieve the contents of the file, `ReadFile` catches the exception that is raised and just erases the contents of the type-in field and the text editor. The application is shown in Fig. [fig:viewer] .

A simple file viewer application.

The file viewer application again, but now, file names can be specified in the type-in field at the top or using the file browser at the left.

Exercise 9 Add a Reset button to the left of the Quit button. Clicking on this button should clear the contents of the type-in field. For extra credit, interpret a double click to also clear the contents of the editor. To detect a double-click, you will need to examine the `VBT.MouseRec` that is available from `FormsVBT.GetTheEvent` to the Reset button's event-handler.

Exercise 10 Add a pop-up to signal when the file could not be opened, rather than clearing the type-in field.

If you substitute `FileBrowser` for `TypeIn`, you'll be able to traverse the file system by double-clicking on directories (those items ending with a slash) in a browser. The file browser generates an event when you double-click on a file. Note that the application does not need be changed at all!

While it's nice to be able to traverse the hierarchy by mousing around in the file browser, there are times when it is more desirable simply to type in a pathname. No problem. We'll just add a type-in field to the S-expression. Here's the new S-expression (see Fig. [fig:viewer2] ):

    (Rim (Pen 10) (ShadowSize -1)
         (BgColor "White") (LightShadow "Black")
         (DarkShadow "Black")
      (VBox
        (HBox
          (Frame Lowered (TypeIn %fileNameString))
          (Glue 10)
          (Button %exit "QUIT"))
        (Glue 10)
        (HBox
          (Shape (Width 100)
            (Frame Lowered (FileBrowser %fileName)))
          (Glue 10)
          (Shape (Height 200 + inf) (Width 300 + inf)
            (Frame Lowered (TextEdit ReadOnly %editor))))))

(A negative value for the inherited property `ShadowSize` is a convention for telling FormsVBT to give feedback using a flat, 2-d style rather than a Motif-like, 3-d style.)

We also need to change the application slightly to register the `ReadFile` event-handler with the type-in field (i.e., `fileNameString`) as well as with the file browser (i.e., `fileName`). In addition, procedure `ReadFile` itself needs to be changed trivially to initialize `fname` from the interactor that caused the event-handler to be invoked:

        VAR fname := FormsVBT.GetText (fv, name);

So far so good, but there's no tie between the file browser and the type-in field.

Exercise 11 Implement event-handler methods for the file browser (`fileName`) and the type-in field (`fileNameString`) to keep them synchronized. That is, typing a path into the type-in field should cause the browser to change the directory it is displaying. The directory displayed by the file browser is set by calling `FormsVBT.PutText`, passing in the name of the directory to be displayed. What happens if you specify a directory that doesn't exist?

If you didn't do the last exercise, now is your last chance...

It turns out that FormsVBT already provides a component that ties a type-in field to a file browser. The component is called a `Helper`, and it has a class-specific property called `For` that names the file browser to which it is tied. So, if you change the expression

        (TypeIn %fileNameString)

to

        (Helper (For fileName))

and replace the initialization of variable `fname` in the original program as described above, then the type-in field and the file browser will stay synchronized, without any application-code intervention.

Exercise 12 What happens when you replace ```Helper`'' by ```DirMenu`''? What happens when you tie a file browser to both a `Helper` and `DirMenu`?