8. A GUI-Based Conversion Program

Our third CLEAN program, winconvert, indicates the format for GUI-based application programs. Such programs are again state transition functions on the world, but are based around two special types. These are a user-defined internal state (in this case State is the file system) and a system-defined state which is parameterised on the user-defined state and must be unique. The type declarations below establish State as shorthand for UNQ FILES, and IO as shorthand for UNQ IOState State.

On the Macintosh, this program must be compiled with the 'No Console' option. This generates a runnable Macintosh application. It should be noted that CLEAN programs on the Macintosh can only manipulate text and data files, and cannot access file icons or other items in a file's resource fork.

MODULE winconvert;
IMPORT lifeio;
IMPORT deltaEventIO, deltaIOSystem, deltaFileSelect;
FROM deltaDialog IMPORT Beep, OpenNotice;

:: UNQ State -> FILES;
:: UNQ IO -> IOState State;

All GUI-based CLEAN programs require a special events object to be extracted from the world, and in this case we also need the file system, since we are using it as an internal user-defined state. The StartIO operator takes the menu system, the initial user-defined state and the events object, and starts the interactive system. On termination of interaction, it returns the final value of the user-defined state and the final events object, both of which we put back into the world.

   Start world
     -> world''',
        (filesystem, world'): OpenFiles world,
        (events, world''): OpenEvents world',
        (filesystem', events'): StartIO [menus] filesystem [] events,
        world''': CloseFiles filesystem' (CloseEvents events' world''),

The menu system consists of only one menu, entitled File, which is a pull-down menu containing two items. Notice that the entire specification is a term of an algebraic data type, containing titles, menu command-key short-cuts, ability to be selected, and method functions which are executed when a menu item is selected. These methods are state transition functions on the two internal states.

        menus: MenuSystem [filemenu],
        filemenu: PullDownMenu 1 "File" Able [
                MenuItem 1 "Convert" (Key 'C') Able ConvertFunction,
                MenuItem 2 "Quit" (Key 'Q') Able QuitFunction];

The method for the Quit menu item alters the system-defined state using QuitIO, which causes the execution of StartIO to terminate, and hence stops the program:

:: QuitFunction State IO -> (State, IO);
   QuitFunction s io -> (s, QuitIO io);

The method for the Convert menu item produces an error message in a dialog box if there are problems in opening or closing files. These dialog boxes are opened by applying a function to the system-defined state. Care is required in returning the correct file system s2, s3, etc. for each case, but the type-checker will detect failure to do this correctly.

:: ConvertFunction State IO -> (State, IO);
   ConvertFunction s1 io 
     -> (s2, io'), IF NOT openselected
     -> (s3, Error (+S "Can't open file " input) io'), IF NOT okinput
     -> (s4, Error (+S "No cells found in " input) io'), IF = numcells 0
     -> (s5, io''), IF NOT saveselected
     -> (s6, Error (+S "Can't open file " output) io''), IF NOT okopen
     -> (s7, Error (+S "Can't close file " output) io''), IF NOT okclose
     -> (s7, Message ["File Converted", cellcount] io''),

The predefined input-file selection dialog box is used to select an input file name. This box allows the user to browse through the file system, which is thus provided as an argument. The result is a boolean value (openselected) which indicates if the user pressed the Open or Cancel buttons, the file name selected, and new values of the file system and system-defined state. If the Cancel button was pressed, the guarded rule above terminates ConvertFunction without producing a message.

        (openselected, input, s2, io'): SelectInputFile s1 io,

The selected file is then opened, read, and closed. This is just as in the previous program in section 7, but the file is opened as a unique file and read using ReadPtsList, so that it can be closed. Since only a limited number of files can be open at one time, it is important that a program which repeatedly opens files also closes them. Notice that for this program we ignore errors in closing the input file (i.e. we do not use the boolean result dummy).

        (okinput, g, s3): FOpen input FReadText s2,
        (points, g'): ReadPtsList g,
        (numcells, statistics): StatPnt points,
        (a, totx, c, b, toty, d): statistics,
        (dummy, s4): FClose g' s3,

The output file name is selected using the predefined output-file selection dialog box. This box again allows the user to browse through the file system, but also takes two string arguments: the `Save as PostScript:' prompt, and the default output file name. The result is similar to that of SelectInputFile. The format of this dialog box on the Macintosh is shown in Figure 2. This predefined dialog box automatically requests confirmation if the selected output file already exists.

        (saveselected, output, s5, io''): 
                SelectOutputFile "Save as PostScript:" 
                                        (+S input ".ps") s4 io',

The output file is opened, written, and closed as in section 7:

        (okopen, h, s6): FOpen output FWriteText s5,
        cellcount: Concat ["[", ITOS numcells, " cells]"],
        message: Concat ["Life pattern ", input, " ", cellcount],
        h': WritePS message ((a,b),(c,d)) points h,
        (okclose, s7): FClose h' s6;

Messages are produced using notice boxes, which are simple dialog boxes of predefined format, containing a number of lines of text, specified as a list of strings, and one or more buttons, each with an identifying number and label. Figure 3 shows the confirmation notice produced by Message ["File Converted", cellcount] after successfully completing input and output. The OpenNotice function returns the identifying number of the button pressed by the user, which is of no interest when there is only one button. Notices, like the file-selector dialog boxes, are modal -- they must be dealt with by the user before any other action can take place.

:: Message [STRING] IO -> IO;
   Message mess io -> io',
                      (button, io'): OpenNotice notice io,
                      notice: Notice mess (NoticeButton 1 "OK") [];

Error messages are also produced using the Message function, after applying the self-explanatory Beep function to the system-defined state:

:: Error STRING IO -> IO;
   Error str io -> Message ["AN ERROR HAS OCCURRED", "", str] (Beep io);

UpTop Level

BackA File Conversion Program

ForwardAn Abstract Data Type for Animating the Game of Life