SML PROGRAMMING

GUITOOL and Dialogs

PLP Staff



GUITOOL is most useful for static interfaces; that is, menus and dialog boxes whose structures do not vary depending on the situation. If you have a menu or dialog box that cannot be represented by a static file, you can do one of two things:
  1. Embed the necessary code in a standard routine.
  2. Redesign it.
For example, in V5N6 I presented a routine to generate a checklist of items on the fly; the disadvantage of the dialog was that it was ill suited to handle large numbers of items. However, it could be re-engineered to a static pattern by the addition of arrow buttons:

E_ITEMC.DLG

BEGIN  101  0.00  0.00 11.00 18.00 v
CBOX   102  0.50  1.50  1.00  1.50 v
LTEXT  103  0.50  4.00  1.00 11.00 f
CBOX   104  1.50  1.50  1.00  1.50 v
LTEXT  105  1.50  4.00  1.00 11.00 f
CBOX   106  2.50  1.50  1.00  1.50 v
LTEXT  107  2.50  4.00  1.00 11.00 f
CBOX   108  3.50  1.50  1.00  1.50 v
LTEXT  109  3.50  4.00  1.00 11.00 f
CBOX   110  4.50  1.50  1.00  1.50 v
LTEXT  111  4.50  4.00  1.00 11.00 f
CBOX   112  5.50  1.50  1.00  1.50 v
LTEXT  113  5.50  4.00  1.00 11.00 f
CBOX   114  6.50  1.50  1.00  1.50 v
LTEXT  115  6.50  4.00  1.00 11.00 f
CBOX   116  7.50  1.50  1.00  1.50 v
LTEXT  117  7.50  4.00  1.00 11.00 f
PBUT  -118  0.50 15.00  1.00  2.00 v ^
PBUT  -119  7.50 15.00  1.00  2.00 v v
PBUT  -120  9.25  1.00  1.50  8.00 v OK
PBUT  -121  9.25  9.00  1.50  8.00 v Cancel
END


If the list of items exceeds the size of the dialog box, the "^" and/or
"v" buttons are enabled to allow the user to scroll through the list.
(Another dialog box, E_ITEMR.DLG, replaces CBOX widgets with RBUT,
enabling a single choice item picker.)  Now let's use GUITOOL directives
to create and populate the dialog and return the response:

*ROUTINE getitem e_itemc.dlg 1 e_itemr.dlg 1 *REM arg1 = C or R (check box or radio button) *REM arg2 = title for dialog box &define i -9 &var &define wtype -11 &var &define title -12 &var &define numit -13 &var &define pos -14 &var &define resp -15 &var &define wksp -16 &var &define temp -19 &var &rem **** load items starting at variable 130 &extract [wtype] -1 1 &value [title] -2 &value [wksp] WKSP &openw [wksp]t$temp.lis ITEMS &closew &open [wksp]t$temp.lis error &sv [numit] 0 &while &do &read [temp] [break] &extract -1 [temp] 1 &if &nm %-1 &do &extract -2 [temp] 2 &extract -3 [temp] 3 &extract -4 [temp] 4 &extract -5 [temp] 5 &sv [temp] "%-2 %-3 %-4 %-5 0" &value %<[numit] + 130> [temp] &inc [numit] &end &end &close & DEL [wksp]t$temp.lis &rem **** initialize and open dialog &openw [winfile] *W S 101 [title] &sv [i] 1 &while &rn [i] 1 %<[numit] min 8> &do &extract [temp] %<[i] + 129> 1 *W S %<[i] * 2 + 101> [temp] &inc [i] &end &sv [pos] 1 *W G 118 &if &eq %<[numit] max 8> 8 &do *W G 119 &else *W E 119 &end &if &rn [numit] 1 7 &do &cv [i] [numit] + 1 &while &rn [i] 1 8 &do *W G %<[i] * 2 + 100> &inc [i] &end &end &closew &sv [resp] CANCEL &if &eq C [wtype] &do *OPEN e_itemc.dlg &else *OPEN e_itemr.dlg &end &rem **** pick loop *PICK 118 &openw [winfile] &if &eq %<[pos] min 7> [pos] &do &sv [temp] 1 &else &cv [temp] [pos] - 7 &end &if &eq [temp] 1 &do *W G 118 &end *W E 119 &r chkitem [pos] [temp] [wtype] [numit] &closew &sv [pos] [temp] *PICK 119 &openw [winfile] &if &eq %<( [pos] + 14 ) max [numit] > [numit] &do &cv [temp] [pos] + 7 &else &cv [temp] [numit] - 7 &end *W E 118 &if &eq %<[temp] + 7> [numit] &do *W G 119 &end &r chkitem [pos] [temp] [wtype] [numit] &closew &sv [pos] [temp] *PICK 120 &sv [resp] OK *CLOSE *PICK 121 *CLOSE *ENDPICK &rem **** generate reply &if &eq CANCEL [resp] &do &return [resp] &end &r chkitem [pos] [pos] [wtype] [numit] &sv [i] 1 &sv [resp] 0 &while &rn [i] 1 [numit] &do &extract [temp] %<[i] + 129> 5 &if &eq [temp] 1 &do &inc [resp] &extract -1 %<[i] + 129> 1 &extract -2 %<[i] + 129> 2 &extract -3 %<[i] + 129> 3 &extract -4 %<[i] + 129> 4 &sv %<[resp] + 500> "%-1 %-2 %-3 %-4" &end &inc [i] &end &return [resp] &rem **** I/O error trap &label error &type "I/O Error" &sv [resp] CANCEL &return [resp]

Thanks to GUITOOL, the only difficulty is maintaining the check state of the items, which is handled by routine CHKITEM:

*ROUTINE chkitem

*REM check and set state of items

&define i -9 &var
&define old -11 &var
&define new -12 &var
&define wtype -13 &var
&define numit -14 &var
&define name -15 &var
&define state -16 &var
&define unchk -17 &var

&value [old] -1
&value [new] -2
&value [wtype] -3
&value [numit] -4
&sv [i] 1
&sv [unchk] 0
&while &rn [i] 1 8 &do
   &value -1 %<[i] * 2 + 100>
   &cv [unchk] [unchk] + %-1
   &inc [i]
&end
&sv [i] 1
&while &rn [i] 1 [numit] &do
   &if &rn [i] [old] %<[old] + 7> &do
      &value [state] %<[i] - [old] * 2 + 102>
      &extract -1 %<[i] + 129> 1
      &extract -2 %<[i] + 129> 2
      &extract -3 %<[i] + 129> 3
      &extract -4 %<[i] + 129> 4
      &sv %<[i] + 129> "%-1 %-2 %-3 %-4 [state]"
   &elseif &eq R [wtype] &and &ne [unchk] 0 &do
      &extract -1 %<[i] + 129> 1
      &extract -2 %<[i] + 129> 2
      &extract -3 %<[i] + 129> 3
      &extract -4 %<[i] + 129> 4
      &sv %<[i] + 129> "%-1 %-2 %-3 %-4 0"
   &end
   &if &ne [old] [new] &and &rn [i] [new] %<[new] + 7> &do
      &extract [name] %<[i] + 129> 1
      &extract [state] %<[i] + 129> 5
      *W S %<[i] - [new] * 2 + 102> [state]
      *W S %<[i] - [new] * 2 + 103> [name]
   &end
   &inc [i]
&end
&return
Thus when a dialog is scrolled, the check state of items is preserved:

Figure 1 Figure 2

Similarly, when the R option is used and an item is checked, the previously checked item is unchecked. Should the number of items be less than 8, the appropriate widgets are grayed:

Figure 3

When the "OK" button is clicked, GETITEM returns the number of items picked (0 if none) and loads those item names and definitions into SML variables starting at number 501 (a distinct advantage over the old method, which returned a string of item names that was limited to 80 characters). The number in turn may be passed to a FORMS or CALC routine.

One disadvantage of this approach is that a separate dialog box is required for a radio button list. Also, supporting the arrow buttons requires a bit of code. Nonetheless, by taking this approach I'm simplifying the dialog-related code, and by enhancing the returned information content I'm able to apply this routine to more situations.

Project Files

The chief purpose of a GUI should be to make life easier for the user, and saved sessions can greatly reduce repetitive effort. Two basic approaches may be taken:

  1. Write a list of parameters which may be read by the application and reapplied.
  2. Write an SML file which may be executed to restore the previous session.
While the former approach is more amenable to upward compatibility, the latter is simpler to implement. Two potential pitfalls with the latter approach are: 1) executing a command syntax which is no longer valid, and 2) assigning data to an incorrect variable. The first is not very likely, but the second may be of concern in an application which stores certain parameters in variables (especially those which may not be obtained using the SHOW command). For example, the application for Version 3.4.2 might store back coverage symbols in variables 63 and 64, and when retooled for Version 3.5 might move those settings to variables 35-38.

The basic strategy is to restrict variable assignment as much as possible; if an application can read a value using &value or SHOW, there's no need to maintain a global variable. The following example applies the above approach to the ETOOL application developed in the previous issue (V6N4); only a few edit parameters are saved, just enough to give you an idea of what's involved. First, lets make some changes to the beginning of E_MAIN.MNU:

POPUP Project
   MENUI Open...,       OPENP
   MENUI Save,          SAVEP
   MENUI Save As...,    SAVEPAS
POPEND
POPUP Cover
   MENUI Open...,       SETCOV
   MENUI Save,          SAVE
   MENUI Switch,        SWITCH
   MENUI Remove,        REMCOV
   MENUI Back Cover,    BACKCOV
   MENUI Remove Back,   REMBACK
POPEND
Then add some *PICK entries to the menu loop:

*PICK OPENP
   &r openp

*PICK SAVEP
   &r savep

*PICK SAVEPAS
   &r savepas
All three routines store the project file name in variable 51. Variable 52 is used as a flag to indicate whether changes have been made in pertinent project settings (e.g. setting an edit cover or back cover). Let's look at routine SAVEP first:

*ROUTINE savep

*REM **** save session settings

&define project 51 &var
&define saved 52 &var
&define i -11 &var
&define editn -12 &var
&define backn -13 &var
&define temp -19 &var
&define winrtn 1 &var

&if &eq "x[project]" "x" &do
   WIN FILE 3 etp 'Save Project'
   &if &eq "x[winrtn]" "x" &do
      &return
   &end
   &value [project] [winrtn]
&end
&openw [project]

*REM **** save editc and backc info
&sv [i] 1
&sv [editn] 0
&sv [backn] 0
&while &rn [i] 1 4 &do
   SHOW BACKCOVER %[i] -1
   &if &ne "x%-1" "x" &do
      &inc [backn]
      &value [temp] %<34 + [i]>
      *W BACKC %-1 [temp]
      *W &sv %<34 + [backn]> [temp]
   &end
   SHOW EDITCOVER %[i] -1
   &if &ne "x%-1" "x" &do
      &inc [editn]
      &value [temp] %<30 + [i]>
      *W EDITC %-1
      *W &sv %<30 + [editn]> [temp]
   &end
   &inc [i]
&end
&sv -1 X
SHOW EDITCOVER 0 -1
SHOW EDITFEAT -2
&if &ne X %-1 &do
   *W EDITC %-1
   *W EDITF %-2
&end

*REM **** save draw settings
SHOW DRAWE -1 -2 -3 -4 -5
*W DRAWE ARC %-1 NODE %-2 LABEL %-3 TIC %-4 ANNO %-5
SHOW SETDRAWS -1 -2
*W SETDRAWS %-1
*W SETDRAWS %-2
SHOW NODEC NODE -1
*W NODEC NODE %-1
SHOW NODEC DANGLE -1
*W NODEC DANGLE %-1
SHOW NODEC PSEUDO -1
*W NODEC PSEUDO %-1
SHOW BACKE -1 -2 -3 -4 -5
*W BACKE ARC %-1 NODE %-2 LABEL %-3 TIC %-4 ANNO %-5
SHOW LINESET -1
*W LINESET %-1
SHOW MARKERSET -1
*W MARKERSET %-1
SHOW TEXTSET -1
*W TEXTSET %-1

*REM **** save edit settings
SHOW MAPE -1 -2 -3 -4
*W MAPE %-1 %-2 %-3 %-4
SHOW EDITD -5
SHOW SNAPD -6
SHOW WEED -7
SHOW GRAIN -8
MAPE ZOOM 2
SHOW EDITD -15
SHOW SNAPD -16
SHOW WEED -17
SHOW GRAIN -18
MAPE %-1 %-2 %-3 %-4
&if &ne %-5 %-15 &do
   &sv -5 DEFAULT
&end
&if &ne %-6 %-16 &do
   &sv -6 DEFAULT
&end
&if &ne %-7 %-17 &do
   &sv -7 DEFAULT
&end
&if &ne %-8 %-18 &do
   &sv -8 DEFAULT
&end
*W EDITD %-5
*W SNAPD %-6
*W WEED %-7
*W GRAIN %-8

*REM **** wrap up
*W DRAW
&closew
&sv [saved] YES
&return
In this example, variables 31-34 store the current edit feature for each edit cover, and 35-38 store the symbol for each back cover (more on this in the next issue). Because SHOW only returns a number for the edit parameters EDITD, SNAPD, WEED, and GRAIN, the routine changes the map extent temporarily to see whether they are set to DEFAULT. Note also that the [winrtn] alias needs to be defined: if a *ROUTINE directive does not declare any dialogs, GUITOOL will not automatically define the [winrtn] and [winfile] aliases.

Since routine SAVEP saves an executable SML file, routine OPENP requires very little coding:

*ROUTINE openp

&define project 51 &var
&define saved 52 &var
&define winrtn 1 &var

&if &eq [saved] NO &do
   WIN MB 3 'Save Project Changes?'
   &if &eq [winrtn] YES &do
      &r savep
   &elseif &eq [winrtn] CANCEL &do
      &return
   &end
&end
WIN FILE 1 etp 'Load Project'
&if &eq "x[winrtn]" "x" &do
   &return
&end
&value [project] [winrtn]
&sv -1 1
&sv -2 .FALSE.
&sv -3 .FALSE.
&while &rn %-1 1 4 &do
   SHOW EDITCOV %-1 -4
   &if &ne "x%-4" "x" &do
      &sv -2 .TRUE.
   &end
   SHOW BACKCOV %-1 -4
   &if &ne "x%-4" "x" &do
      &sv -3 .TRUE.
   &end
   &inc -1
&end
&if &eq %-2 .TRUE. &do
   REMOVEEDIT ALL
   Y
&end
&if &eq %-3 .TRUE. &do
   REMOVEBACK ALL
&end
&r [project]
&sv [saved] YES
&return
Note that the routine checks for the presence of edit and/or back coverages and removes them if necessary. Routine SAVEPAS requires even less code:

*ROUTINE savepas

*REM **** save project settings as new file

&define project 51 &var
&define winrtn 1 &var

WIN FILE 3 etp 'Save Project'
&if &eq "x[winrtn]" "x" &do
   &return
&else
   &value [project] [winrtn]
   &r savep
&end
&return
Oops! I've just about run out of space. I'll continue this discussion in the next issue, showing some new tricks for maintaining edit and back coverages, as well as presenting a nifty way to launch project files through File Manager or Windows Explorer.

Next: Project Files (Continued)

PLP