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.
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, QUITThe 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 *CLOSEThe *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 etoolThe 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$ETOOLEach 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_MAINGUITOOL 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 loopThe 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":
The message:
Menu SPLIT picked.would be typed.
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 CancelE_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 ENDE_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 NoneE_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 ApplyBecause 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 QNote 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
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
One the edit feature is picked, the appropriate toolboxes are opened:
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 105At 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":
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 SPLITbut 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.
[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