SML PROGRAMMING

DOS User Interfaces (Part 1)

Although the POPUP command is now considered obsolete in light of the WIN command, I felt that a pair of articles discussing it would be interesting, challenging, and fun!

Menus

The POPUP command allows the creation of a menu system for an SML application running in DOS. Using POPUP's NONE parameter in combination with SCREENSAVE and SCREENRESTORE allows pulldown submenus. Let's plunge right in and imagine a DOS ARCEDIT shell with three or more menu levels, the top level being controlled by two routines: "init" and "mainmenu".

   &routine init

   ...
   &value 52 WKSP
   SCREENSAVE %52t$main.ras POPUP 8 16 1 50
   &while &do
      POPUP mainmenu SML
   &end
   &return
We'll assume that the first part of "init" initializes the ARCEDIT session and/or restores the previous session, sets DISPLAY 4, and so on. Then the SCREENSAVE command captures the portion of the screen that will be covered by the Main menu so that it may be restored before executing a command. Note that the POPUP coordinate and size values correspond to character rows and columns on the screen (1-30 and 1-80, respectively, using DISP 4 with IBMVGA12.SYS).

The POPUP command is contained in an endless loop so that the program always returns to the menu and cannot be escaped by hitting the accent grave character <`> key. A separate routine will handle shutting down the application, saving settings and using QUIT to terminate the session. Routine "mainmenu" contains the Main menu definition as well as the code for handling responses:

   &routine mainmenu
   &rem 1 2 8 16 1 50 NONE
   &rem File Edit Query/Calc Select Display Settings Help
   &rem EOF
   &if &eq %1 FILE &do
      POPUP filemenu SML
   &elseif &eq %1 EDIT &do
      SCREENSAVE %52t$edit.ras POPUP 10 21 14 12
      &sv 10
      &while &ne %10 EXIT &do
         POPUP editmenu SML
      &end
      SCREENRESTORE %52t$edit.ras
      & DEL %52t$edit.ras
   &elseif...
      etc. etc.
   &end
   &return
Menu definitions are contained within the initial &rem statements, which are automatically preserved by COMPSML due to the previously encountered POPUP call (isn't that neat?). Because blank lines are also preserved, there must NOT be one between the &routine statement and the &rem statement which defines the POPUP parameters, or else the SML processor will woof when the program is executed.

The first parameter indicates that return values are to be passed to variable 1. The second parameter, specifying POPUP option 2, indicates that any string token in a line (not preceded by "`") will act as a button widget, returning its upper case equivalent. The next four parameters define the position and size (because a border is added to the popup window, its actual size will be increased by two rows and columns), and the "NONE" parameter indicates that the window will not automatically be dismissed when exited (i.e. there is no automatic SCREENSAVE and SCREENRESTORE). In this example, the menu will consist of a single row of seven buttons. The EOF terminates the definition. The top level menu window is pretty straightfoward and looks like this:

Figure 1

The remainder of the routine handles the response contained in variable 1, in this case calling the second level submenus. Because the File menu has no submenus, it may be treated fairly simply:

   &routine filemenu
   &rem 10 1 10 16 16 20
   &rem New
   &rem Open
   ...
   &rem Quit
   &rem EOF
   &if &eq %10 NEW &do
      &run new
   &elseif...
      etc. etc.
   &elseif &eq %10 QUIT &do
      &run quit
   &end
   &return
Note that the response variable is 10. Also note the use of POPUP option 1, which assumes that the entire line is selectable (unless preceded by "`") but only the first string token will be returned.

A typical command routine will erase the main menu beforehand. Afterwards, it will resample the display to reflect any possible changes:

   &routine new

   SCREENRESTORE %52t$main.ras
   & DEL %52t$main.ras
   ...
   SCREENSAVE %52t$main.ras POPUP 8 16 1 50
   &return
Because the Edit menu has submenus, it needs to be treated in a fashion similar to the Main menu. The response variable 10 is also used to look for a second level EXIT—this allows the user to escape out of a third level submenu without popping all the way back to the Main menu. This also means that every command routine executed by this menu (such as routine "editfeat") should set variable 10 to EXIT.

   &routine editmenu
   &rem 10 1 10 21 14 12 NONE
   &rem Feature
   &rem Initialize
   &rem Add
   &rem Delete
   &rem `-----------
   &rem Move
   &rem Nudge
   &rem Copy
   &rem Rotate
   &rem `-----------
   &rem Arc
   &rem Vertex
   &rem Anno
   &rem Tic
   &rem EOF
   &if &eq %10 FEATURE &do
      &run editfeat
   &elseif ...
      etc. etc.
   &elseif &eq %10 VERTEX &do
      POPUP vmenu SML
   &elseif ...
      etc. etc.
   &end
   &return

   &routine vmenu
   &rem 11 1 21 34 5 8
   &rem Draw
   &rem Add
   &rem Delete
   &rem Move
   &rem Tic
   &rem EOF
   SCREENRESTORE %52t$edit.ras
   & DEL %52t$edit.ras
   SCREENRESTORE %52t$main.ras
   & DEL %52t$main.ras
   &if &eq %11 DRAW &do
      V DRAW
   &elseif &eq %11 ADD &do
      V ADD
   &elseif &eq %11 DELETE &do
      V DELETE
   &elseif &eq %11 MOVE &do
      V MOVE
   &elseif &eq %11 TIC &do
      &run vtic
   &end
   SCREENSAVE %52t$main.ras POPUP 8 16 1 50
   SCREENSAVE %52t$edit.ras POPUP 10 21 14 12
   &sv 10 EXIT
   &return
When the Vertex submenu is invoked, it looks like this:

Figure 2

Note that two levels of menus now need to be erased and updated when executing the commands. The Edit menu is erased first because it overlaps the Main menu (this a lot like pushing to and popping from a stack).

Dialog Boxes

The POPUP command also allows the creation of dialog boxes, which may contain text, buttons, and/or edit boxes. A definition file contains the basic information needed to populate the window, while the POPUP command itself defines how the window is to be created. As an example I'm going to *ahem* appropriate Steve Bower's pan/zoom dialog box from the previous PLP issue and discuss what's going on in it:
   POPUP pzd.mnu 49 1 1 56 12 22
When a dialog box is used to set and/or return values rather than execute routines, a different form of the POPUP command is used. First the definition file is specified, then the variable which will receive the return value (49), then the POPUP option (1), then the position and size information. In this case, a window 12 rows deep and 22 columns wide will be created at screen position 1,56. Note that if the user presses <`> to escape the dialog box, the value "EXIT" will be returned to variable 49.

If the data in the definition file exceed the number of rows assigned to the dialog box when using POPUP option 1 or 2, a little "PgDn" button will appear in the lower left corner. Hitting the "PgDn" button or <Page Down> key will scroll the contents. A "Home" button will then appear which the user can click to go back to the top (or hit the <Home> key). If the number of columns is exceeded, the POPUP command will bail out. Let's review the contents of PZD.MNU:

   Zoom`In
   Zoom`Out
   Pan`Up
   Pan`Left
   Pan`Right
   Pan`Down
   Previous`Mapextent

   Zoom`out`factor: %40
   Pan`Screens    : %41

   RETURN
In this example there are 8 buttons and 2 edit boxes (blank lines are not selectable). The accent grave characters between words keep the entire string together as a single token; they appear as spaces in the dialog box, but are preserved in the response value. If the edit box variables already contain values when the POPUP command is issued, those values will be displayed. For example, if variable 40 contains "2" and variable 41 contains "0.5" the dialog box will look like this:

Figure 3

If the "Zoom In" button is pressed, the value "ZOOM`IN" will be returned to variable 49. To activate an edit box, the user clicks on the line with the mouse or scrolls to it with the arrow keys and hits <Enter>. A cursor appears, and the user may enter a new value; any existing value is wiped out beforehand. When <Enter> is hit, the value is assigned to the variable indicated in the definition file. Also, the number of characters which may be entered is limited by the space allotted; scrolling is not supported.

Any string entered is converted to upper case. Despite what the "SML User's Guide" says, if a string containing spaces is entered, even if you use quotes, only the first token will be returned, in upper case. If you connect words with "`" they will all be kept, in upper case; the accent grave characters will appear as spaces in the dialog box, but will be kept in the value (e.g. "Help`me" will appear as "HELP ME" but will be returned as "HELP`ME").

If, however, the variable reference in the box is directly preceded by "`", strings containing blanks may be entered. If POPUP option 0 or 1 is specified, the string will be preserved as is. In option 2, however, "`" will disable a variable reference unless it is immediately preceded by a field validation tag, in which case the variable contents will have spaces replaced with "`".

[TIP: In the SML Developer's Toolkit are two routines, @STRAC2SP and @STRSP2AC, which convert "`" characters in a string to spaces and vice versa. The former is useful for handling return values and the latter for building or updating definition files.]

It may be desirable to check the validity of values as they are entered into edit boxes. Let's add some "error prevention" to the above example:

   &routine getzoom

   &sv 40 2
   &sv 41 0.5
   &value 52 WKSP
   &sv 10
   &value 11 40
   &value 12 41
   &sv 13
   SCREENSAVE %52t$temp.ras POPUP 1 55 12 23
   &while &ne %10 EXIT &do
      POPUP pzd.mnu 49 1 1 56 12 22 NONE %13
      &if &eq "%49" "_40" &do
         &run numcheck 40 11
         &sv 13 "_Zoom`out`factor:"
      &elseif &eq "%49" "_41" &do
         &run numcheck 41 12
         &sv 13 "_Pan`Screens    :"
      &else
         &sv 10 EXIT
      &end
   &end
   SCREENRESTORE %52t$temp.ras
   & DEL %52t$temp.ras
   &return

   &routine pzd.mnu B
   Zoom`In
   Zoom`Out
   Pan`Up
   Pan`Left
   Pan`Right
   Pan`Down
   Previous`Mapextent

   _Zoom`out`factor: %40
   _Pan`Screens    : %41

   RETURN
   &routine numcheck

   &value 3 %1
   &if &nn %3 &do
      &value %1 %2
   &else
      &cv 4 %3 abs
      &if &ne %3 %4 &or &eq %3 0 &do
         &value %1 %2
      &end
   &end
Note the use of &routine option B for the dialog box definition; thus any blank lines between that statement and the next &routine will appear in the dialog box. The underscore character ("_") in the above code represents ASCII character 254, which normally appears in DOS as a solid box. The character acts as a field validation tag: after the user enters a value in that edit box, the character plus the variable number will be returned to variable 49. It appears as a space in the dialog box, so I increased the box's size by one column and nudged it to the left.

Routine "numcheck" checks to see that the content of the edit box variable is a positive number; otherwise, the value is reset to the original. Variable 13 is set as a pointer for POPUP's {cursor_position} option so that the user is returned directly to the edit box that was just set.

Dressing Up Your Popup

Typical popup windows are pretty boring: they lack color, and widgets don't resemble their functions. You can, however, with creative use of escape sequences and extended characters, create some nifty looking windows. If you're really ambitious you can even create ANSI-style art; your creativity is limited only by the 23-row 78-column limit imposed on popup sizes.

Escape sequences allow you to define foreground and background colors:

   <ESC>2D
The sequence will generally appear in DOS as a left pointing arrow immediately followed by two hexadecimal characters (0-F, where A-F are equivalent to 10-15). The first digit defines the foreground color (in this case red) and the second digit defines the background color (in this case color 13, which is "light yellow"). In addition, the extended character set includes characters to draw and shade boxes. Let's spruce up our dialog box a bit with the following definition:

Figure 4

Click here to download the actual code.

Note that the presence of escape sequences eliminates the need to put acute grave characters in front of the extended characters. It's also easier to compose lines and boxes first and then add escape sequences afterwards. I don't bother to "close" the edit boxes because, as values are entered, the edit line extends all the way to the border. The result looks like this (when converted to grayscale):

Figure 5

Because of the change in size, the POPUP position and size parameters should be changed to the following:

   1 50 23 25
Finally, there's no need for a popup window to look like a box. By coloring the outer portions of the window with the "transparent" escape sequence (<ESC>88) you can create just about any shape you please:

Figure 6

Click here to download the actual code.

Note that when a transparent escape sequence is given immediately at the beginning of a definition file, the border is automatically not drawn.

Next: Fun With Widgets


Hey! No footnotes this time!


Return to ArcTips page