SML PROGRAMMING

GUITOOL, Dialogs, and Project Files

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
Now we're going to take a look at how that information is maintained when opening and removing edit and back coverages. When opening an edit coverage, the user is automatically asked to set the edit feature, so the only change necessary is to add some lines at the end of routine SETEDITF:

*REM **** store edit feature
&sv -2 1
SHOW EDITCOVER 0 -3
&while &rn %-2 1 4 &do
   SHOW EDITCOVER %-2 -4
   &if &eq "x%-4" "x%-3" &do
      &value %<%-2 + 30> -1
      &break
   &end
   &inc -2
&end
&return %-1
The four edit cover slots are searched for a match to editcover 0, and the edit feature (variable -1) is stored in the appropriate location. Also, the following line is placed at the end of the "*PICK SETCOV SETFEAT 106" entry in routine ETOOL:

   &sv [saved] NO
In the last article some entries were added to E_MAIN.MNU to add back coverages and remove edit or back coverages; now we'll add some *PICK directives to routine ETOOL to support them:

*PICK REMCOV
   *OPEN remcov H EDIT

*PICK BACKCOV
   *OPEN setbackc H
   &sv [saved] NO

*PICK REMBACK
   *OPEN remcov H BACK
Let's look at routine SETBACKC first. It uses a new dialog named E_SETBC.DLG:

BEGIN     0  0.00  0.00  6.00 24.00 v Back Coverage
EBOX    101  0.50  1.00  1.50 20.00 v
PBUT   -102  0.50 21.50  1.50  1.50 v »
LTEXT     0  2.50  1.00  1.00  8.00 v Symbol:
EBOX    103  2.50  9.00  1.50  6.00 v 2
PBUT   -104  2.50 15.50  1.50  1.50 v »
PBUT   -105  4.25  1.00  1.50  8.00 v OK
PBUT   -106  4.25  9.00  1.50  8.00 v Cancel
Figure 4

The code for routine SETBACKC is as follows:

*ROUTINE setbackc
e_setbc.dlg 1

*REM **** get back cover

&define i -10 &var
&define pos -11 &var
&sv [i] 1
&sv [pos] 0
&while &rn [i] 1 4 &do
   SHOW BACKCOVER %[i] -2
   &if &eq "x%-2" "x" &do
      &cv [pos] 34 + [i]
      &break
   &end
   &inc [i]
&end
&if &eq [pos] 0 &do
   WIN MB 1 'No more room for back coverages'
   &return
&end
&sv -1
&sv -2
*OPEN e_setbc.dlg
*PICK 102
   *FILE 101 C * 'Enter coverage'
*PICK 105
   &sv -1 %101
   &sv -2 %103
   *CLOSE
*PICK 106
   *CLOSE
*ENDPICK
&if &eq "x%-1" "x" &do
   &return
&end
&if &eq "x%-2" "x" &do
   &sv -2 1
&end
BACKC %-1 %-2
&value %[pos] -2
&sv [saved] NO
&return
The code is almost identical to routine SETEDITC save that it supports an extra widget (103) for the back symbol. (Support for widget 104, launching the symbol picker, will be discussed in the next article.) In fact, it would be more efficient to combine SETEDITC and SETBACKC, as is done in routine REMCOV. Routine REMCOV uses E_GETCC.DLG:

BEGIN  101  8.00 40.00  8.00 36.00 v
CBOX   102  1.00  1.00  1.00  1.50 v
LTEXT  103  1.00  4.00  1.00 31.00 v
CBOX   104  2.00  1.00  1.00  1.50 v
LTEXT  105  2.00  4.00  1.00 31.00 v
CBOX   106  3.00  1.00  1.00  1.50 v
LTEXT  107  3.00  4.00  1.00 31.00 v
CBOX   108  4.00  1.00  1.00  1.50 v
LTEXT  109  4.00  4.00  1.00 31.00 v
PBUT  -110  6.00  1.00  1.50  8.00 v OK
PBUT  -111  6.00  9.00  1.50  8.00 v Cancel
Figure 5

*ROUTINE remcov
e_getcc.dlg 1

*REM **** remove edit or back cover

&define type -8 &var
&define i -9 &var
&value [type] -1
&openw [winfile]
&if &eq [type] EDIT &do
   *W S 101 Remove Edit Coverage(s)
&else
   *W S 101 Remove Back Coverage(s)
&end
&sv [i] 1
&while &rn [i] 1 4 &do
   &if &eq [type] EDIT &do
      SHOW EDITCOV %[i] -1
   &else
      SHOW BACKCOV %[i] -1
   &end
   &if &eq "x%-1" "x" &do
      *W G %<[i] * 2 + 100>
   &else
      &value %<-10 - [i]> -1
      &rem &value -2 -1 %
      *W S %<[i] * 2 + 101> %-1
   &end
   &inc [i]
&end
&closew
&sv -1 CANCEL
*OPEN e_getcc.dlg
*PICK 110
   &sv -1 OK
   *CLOSE
*PICK 111 49
   *CLOSE
*ENDPICK
&if &eq %-1 CANCEL &do
   &return
&end
&sv [i] 1
&while &rn [i] 1 4 &do
   &value -1 %<[i] * 2 + 100>
   &if &eq %-1 1 &do
      &value -2 %<-10 - [i]>
      &if &eq [type] EDIT &do
         REMOVEE %-2
         &type " "
         Y
         &sv %<30 + [i]> NONE
      &else
         REMOVEB %-2
         &sv %<34 + [i]> 0
      &end
   &end
   &inc [i]
&end
&sv [saved] NO
&return
First, the routine sets the title of the dialog box. Then it searches through the four slots of edit or back coverages: where one is filled, the appropriate text widget is assigned that value, otherwise the widget is left blank and the checkbox grayed. If a particular coverage is removed, its edit feature or back symbol variable is cleared.

Launching Project Files

Launching a project file from Windows Explorer or File Manager is simply a matter of making a program association:

  1. Start Windows Explorer
  2. Click "View | Options..."
  3. Click "File Types"
  4. Click "New Type..."
  5. Under description, type "ETool Project"
  6. Under extension, type "etp"
  7. Click "New..."
  8. Under action, type "open"
  9. Under application, type "scrwin.exe x etstart"
  10. Optional: change the icon to %ARC%\CMD\ARCEDITW.ICO
Figure 6

ETSTART.SML, which should reside in %ARC%\UTOOL, contains the following code:

&value 51 -1
&sv 52 "ETP"
arceditw etool
QUIT
All that's left is to write some code so that ETOOL knows a project is being launched. First, the following line in routine ETOOL:

DRAWE ARC LABEL NODE ERRORS TIC IDS
is replaced with:

&r init
Routine INIT has the following code:

*ROUTINE init

&sv -1 1
&while &rn %-1 1 4 &do
   &sv %<%-1 + 30> NONE
   &sv %<%-1 + 34> 0
   &inc -1
&end
&if &ne "x[saved]" "xETP" &do
   &sv [project]
&end
&if &ne "x[project]" "x" &do
   &r [project]
&else
   DRAWE ARC LABEL NODE ERRORS TIC IDS
   BACKE ARC
&end
&sv [saved] YES
Thus, when a project file is opened in Explorer, its name is passed to ETSTART.SML, which assigns the filename to variable 51 and the "ETP" flag to 52. Routine INIT, after clearing the edit feature and back symbol variables, runs the project file if the "ETP" flag is set.

Next: COORD vs. UBUT


Return to ArcTips page