SML PROGRAMMING

GUITOOL

A Buried Jewel

Nestled in the %ARC%\EXAMPLES directory is a treasure that no application developer should work without. GUITOOL.EXE[1] is an executable that processes a fairly simple meta-code file (default suffix .GUI) and any referenced menu or dialog files into a compilable routine file (.R) containing the necessary code to test and/or support the widgets involved.

In addition to speeding up the prototyping of interfaces, GUITOOL provides an additional level of structure to the application code. This not only cosmetic: as I've said in the past, the better your structure, the easier the debugging.

The real beauty of the GUITOOL approach is that you can begin with a basic, operational framework and add functionality as you go along.

Prototyping a Menu

Let's create a relatively simple menu file for ARCEDITW called E_MAIN.MNU:

POPUP Cover
   MENUI Open...,       SETCOV
   MENUI Save,          SAVE
POPEND
POPUP Edit
   MENUI Feature...,    SETFEAT
   MENUI Add,           ADD
   MENUI Delete,        DELETE
   MENUI Move,          MOVE
   MENUI Copy,          COPY
   SEP
   POPUP Arc
      MENUI Split,      SPLIT
      MENUI Unsplit,    UNSPLIT
      MENUI Reshape,    RESHAPE
      MENUI Spline,     SPLINE
      MENUI Flip,       FLIP
   POPEND
   POPUP Vertex
      MENUI Draw,       VDRAW
      MENUI Move,       VMOVE
      MENUI Add,        VADD
      MENUI Delete,     VDEL
   POPEND
   POPUP Anno
      MENUI Adjust,     REPOS
      MENUI Text...,    ANNOTEXT
      MENUI Set Arrow,  SETARROW
      MENUI Del Arrow,  DELARROW
   POPEND
POPEND
POPUP Select
   MENUI One,           SELONE
   MENUI Many,          SELMANY
   MENUI Box,           SELBOX
   MENUI Dangle,        SELDANGLE
   MENUI All,           SELALL
   MENUI None,          SELNONE
POPEND
POPUP Tools
   MENUI Edit,          EDITTOOL
   MENUI Select,        SELTOOL
   MENUI Command,       COMTOOL
   SEP
   MENUI Pin Mode,      PIN
POPEND
MENUI Quit,             QUIT
The following code in ETOOL.GUI will allow us to examine all of the menu widgets and yet exit when "Quit" is clicked:

*AUTHOR My Name
*ROUTINE etool
e_main.mnu 0

*OPEN e_main.mnu
*PICK QUIT
   *CLOSE
The *AUTHOR directive allows GUITOOL to place the author's name in the header remarks for each routine. The *ROUTINE directive begins each routine in the application. Immediately after the *ROUTINE directive, any nonmodal menus or dialog boxes which will be present during the routine are declared (the "0" indicates a menu—any other numeral indicates the dialog box number to be assigned); this allows GUITOOL to create code to handle all of the possible return widgets which will be encountered. The list is terminated by a blank line.

The *OPEN directive tells GUITOOL to open and read the menu. By default, GUITOOL creates a read loop which echoes a message for each return widget picked. The *PICK directive allows you to assign a block of code to a particular widget or set of widgets. In the above example, the *CLOSE directive, which closes the menu and exits the loop, is assigned to widget QUIT.

Now let's run GUITOOL at the DOS prompt as follows:[2]

guitool etool etool
The output routine file, ETOOL.R, has some notable features intended to make life easier for the application developer.

&ROUTINE ETOOL
...
&include ETOOL.INC
&define winrtn    1 &var  Holds WIN command return codes.
&define winfile -20 &var  WIN command communication file.

&REM Set up winfile name
&value [winfile] wksp
&sv [winfile] [winfile]t$ETOOL
Each routine automatically begins with an &include directive (how many of us out there keep forgetting to add one to a new routine?). If the include file, named after the output routine file, is not present, a blank one is automatically created. Two dedicated aliases are also set up: [winrtn] and [winfile]; these provide the programmer with a simple, consistent way to deal with WIN return values and command files.

    &REM *OPEN E_MAIN.MNU
    &r E_MAIN
GUITOOL builds a routine for each menu or dialog box in order to create it; thus it is important that no menu or dialog file has the same name as another routine in the application. The advantages of this approach are two-fold: 1) menus and dialog boxes are automatically embedded in the final code, and 2) menus and dialog boxes may readily be edited during the development phase without repeatedly stripping and replacing &write directives.

&REM Message loop
&while  &do
  win menu r [winfile]
  &if &fn [winfile] &do
    & del [winfile]
  &end
  &LABEL EDITBOX2
  &if &eq [winrtn] -1 &do
     &REM Window closed, get out of loop.
     &break
  &elseif &eq [winrtn] QUIT &do
    &REM Menu Choice QUIT
    &REM *CLOSE
    win menu d
    &break
...
  &elseif &eq [winrtn] SPLIT &do
    &REM Menu Choice SPLIT
    &type "Menu SPLIT picked."
...
  &end
&end

&REM End of message loop
The WIN MENU R loop checks for the [winfile] command file and deletes it afterwards if it exists. Code is created for each possible return value: the close widget value (-1) exits the loop, and values not covered by *PICK directives are simply &typed out. Thus if you were to pick "Edit|Arc|Split":

Figure 1

The message:

Menu SPLIT picked.
would be typed.

Adding Dialog Boxes

Okay, now let's throw in some dialog boxes! First I'll add some that were introduced in the previous article, though the names are changed: E_EDIT.DLG, E_ARC.DLG, E_VRTX.DLG, and E_ANNO.DLG. I'll also add four more:

E_SETEC.DLG:

BEGIN     0 10.00  5.00  4.50 24.00 v Edit Coverage
EBOX    101  0.50  1.00  1.50 20.00 v
PBUT   -102  0.50 21.50  1.50  1.50 v »
PBUT   -103  2.50  1.00  1.50  8.00 v OK
PBUT   -104  2.50  9.00  1.50  8.00 v Cancel
E_SETEF.DLG:
BEGIN     0  5.00  5.00  9.00 16.00 v Edit Feature
GBOX      0  0.00  1.00  6.50 14.00 v
RBUT    101  1.00  2.00  1.00  8.00 v ARC
RBUT    102  2.00  2.00  1.00  8.00 v LABEL
RBUT    103  3.00  2.00  1.00  8.00 v NODE
RBUT    104  4.00  2.00  1.00  8.00 v TIC
RBUT    105  5.00  2.00  1.00  8.00 v ANNO
PBUT   -106  7.00  1.00  1.50  7.00 v OK
PBUT   -107  7.00  8.00  1.50  7.00 v Cancel
END
E_SEL.DLG:
BEGIN     0  5.00 22.00  4.50 16.00 v Select
PBUT   -131  0.00  0.00  1.50  8.00 v One
PBUT   -132  0.00  8.00  1.50  8.00 v Many
PBUT   -133  1.50  0.00  1.50  8.00 v Box
PBUT   -134  1.50  8.00  1.50  8.00 v Dangle
PBUT   -135  3.00  0.00  1.50  8.00 v All
PBUT   -136  3.00  8.00  1.50  8.00 v None
E_COM.DLG:
BEGIN     0 17.50  5.00  4.50 42.00 v Command Tool
EBOX    141  0.50  1.00  1.50 40.00 v
PBUT   -142  2.50  1.00  1.50  8.00 v Apply
Because the edit feature toolboxes depend on the edit feature, I'll need to write a bit of code to support them. ETOOL.GUI is now as follows:

*AUTHOR My Name
*ROUTINE etool
e_main.mnu 0
e_edit.dlg 15
e_arc.dlg 14
e_anno.dlg 14
e_vrtx.dlg 13
e_sel.dlg 12
e_com.dlg 11

&define editcov -11 &var
&define editfeat -12 &var
&define pin -13 &var
&define temp -19 &var

*REM **** set display and turn pin mode off

&r disp
WIN DB U
&sv [pin] U
DRAWE ARC LABEL NODE ERRORS TIC IDS
*OPEN e_main.mnu

*REM **** the following statement is executed
*REM **** before the loop begins

&type "Ok."
*PICK SETCOV SETFEAT 106
   &sv [temp] .TRUE.
   &if &eq [winrtn] SETCOV &do
      *OPEN seteditc H
      &rv [editcov]
      &if &eq "x[editcov]" "x" &do
         &sv [temp] .FALSE.
      &else
         EDITC [editcov]
         DRAW
      &end
   &end
   &if &eq [temp] .TRUE. &do

      *REM **** Set edit feature

      *OPEN seteditf H [editfeat]
      &rv [temp]
      &if &eq [temp] CANCEL &do
         &sv [temp] .FALSE.
      &else
         &sv [editfeat] [temp]
         &sv [temp] .TRUE.
      &end
   &end
   &if &eq [temp] .TRUE. &do

      *REM **** Set appropriate toolbox(es)

      &if &ne [editfeat] NONE &do
         *OPEN e_edit.dlg N
         *OPEN e_sel.dlg N
         &openw [winfile]
         &r setefw
         &closew
      &end
      &if &eq [editfeat] ARC &do
         *OPEN e_arc.dlg N
         *OPEN e_vrtx.dlg N
      &elseif &eq [editfeat] ANNOTATION &do
         *OPEN e_anno.dlg N
         *CLOSE e_vrtx.dlg
      &else
         *CLOSE e_arc.dlg
         *CLOSE e_vrtx.dlg
      &end
   &end
*PICK 105
   &if &eq [editfeat] ARC &do
      *OPEN e_arc.dlg N
   &else
      *OPEN e_anno.dlg N
   &end
*PICK 116
   *OPEN e_vrtx.dlg N
*PICK 142
   %141
*PICK EDITTOOL
   &openw [winfile]
   &r setefw
   &closew
   *OPEN e_edit.dlg N
*PICK SELTOOL
   *OPEN e_sel.dlg N
*PICK COMTOOL
   *OPEN e_com.dlg N
*PICK PIN
   &openw [winfile]
   &if &eq [pin] U &do
      WIN DB P
      *W C PIN
   &else
      WIN DB U
      *W U PIN
   &end
   &closew
*PICK QUIT
   *CLOSE
*ENDPICK ALL
   *REM &openw [winfile] A
   *REM &r setmenu
   *REM &closew
   &type "Ok."
*ENDPICK
   &r Q
Note that I've assigned the same dialog box number to E_ARC.DLG and E_ANNO.DLG; that is permissible only because they are mutually exclusive. When pin mode is active (or the box hasn't been moved), the effect will be that of a single dialog box which changes its structure.

In routines ETOOL and SETEDITC, I introduce the remaining GUITOOL directives. "*OPEN dialog_file N" opens a nonmodal dialog file that has been declared in the *ROUTINE list. "*OPEN routine H/I" opens a routine modally, creating a new dialog box group using WIN DB BH/BI (discussed in the last article). *W is a shortcut for &write that automatically adds quotes. *ENDPICK allows the developer to insert code to be executed after the read loop is terminated. *ENDPICK ALL allows the insertion of code to be executed after each pick in the read loop (e.g. to gray or enable menu widgets).

DISP and Q are external routines that set the graphic display (see ARC Utilities). Routine SETEDITC, which invokes a dialog box to set the edit cover, is simplicity itself:

*ROUTINE seteditc
e_setec.dlg 1

*REM **** Get edit cover
*OPEN e_setec.dlg
*PICK 102
   *FILE 101 C * 'Enter coverage'
*PICK 103
   &sv -1 %101
   *CLOSE
*PICK 104
   *CLOSE
*ENDPICK
&return %-1
Figure 2

Note the use of the *FILE directive, which invokes the file picker and assigns the return value to the specified widget. Thus when the ">>" button is clicked and a coverage picked, that value is returned to the dialog box.

Routine SETEDITF sets the edit feature:

*ROUTINE seteditf
e_setef.dlg 1

*REM **** Get edit feature

&openw [winfile]
&if &eq %-1 LABEL &do
   *W C 102
&elseif &eq %-1 NODE &do
   *W C 103
&elseif &eq %-1 TIC &do
   *W C 104
&elseif &eq %-1 ANNOTATION &do
   *W C 105
&else
   *W C 101
&end
&closew
*OPEN e_setef.dlg
*PICK 50 106
   &if &eq %101 1 &do
      EDITF ARC
   &elseif &eq %102 1 &do
      EDITF LABEL
   &elseif &eq %103 1 &do
      EDITF NODE
   &elseif &eq %104 1 &do
      EDITF TIC
   &else
      EDITF ANNOTATION
   &end
   *CLOSE
*PICK 49 107
   *CLOSE
*ENDPICK
SHOW EDITFEATURE -1
&return %-1
Figure 3

One the edit feature is picked, the appropriate toolboxes are opened:

Figure 4

Routine SETEFW sets the feature button for E_EDIT.DLG:

*ROUTINE setefw

*REM **** Set editf button value
&define editfeat -1 &var
SHOW EDITFEATURE [editfeat]
&if &eq [editfeat] ARC &do
   &sv -2 ARC...
   &sv -3 E
&elseif &eq [editfeat] ANNOTATION &do
   &sv -2 ANNO...
   &sv -3 E
&else
   &sv -2 [editfeat]
   &sv -3 G
&end
*W S 105 %-2
*W %-3 105
At this point, the only widgets which are enabled are those which set the edit cover, set the edit feature, invoke dialog boxes, toggle pin mode, execute a command in the command tool, or exit the application. The command tool is invoked by picking "Tools|Command":

Figure 5

Assuming that the GUI is set up to our satisfaction, we can start plugging in more code. Most of the picks are easy:

*PICK SPLIT 111
   SPLIT
but more advanced functionality will require some coding. Nonetheless, GUITOOL simplifies just about any programming centered around dialog boxes.

Although GUITOOL is a powerful application development system, it does have its limitations. For example, you cannot use a separate routine to add nonmodal dialog boxes to the current list: the routine will have its own read loop that will not terminate until the boxes you add are closed.

Also, GUITOOL is geared more towards static interfaces; if you have a menu or dialog box that cannot be represented by a static file, you must do one of two things: 1) embed the necessary code in a standard routine, or 2) redesign it.

In the next article, I'll show an example of redesigning a dialog box to make it more GUITOOL-friendly.

Next: More GUITOOL Stuff


[1]The accompanying file, GUITOOL.TXT, contains documentation.

[2]GUITOOL.EXE should be copied to a directory somewhere in your path, such as the %ARC%\CMD directory:

copy %arc%\examples\guitool.exe %arc%\cmd

Return to ArcTips page