            Column 16 - INTERFACE POTPOURRI, Part 1
            ---------------------------------------


This issue of ST PRO GEM, number 16 in the series, presents code 
implementing several user interface techniques: progress indicators, 
rubber boxes, and draggable boxes with mouse sensitive targets. The code 
also includes some utility routines for handling resources, event calls, 
and VDI line drawing.

There are several files for this column. Note the plural - in addition 
to the usual C sources stored in GEMCL16.C, the files GEMCL16.RSC, 
GEMCL16.DFN, GEMCL16.H, and GEMCL16.RSH are a template resource for 
building progress boxes. GEMCL16.RSC is the resource binary, and 
GEMCL16.H is its symbol binding file, to be used with GEMCL16.C. The RSH 
file is a C image of the resource - you would need STCREATE to 
regenerate it.

GEMCL16.DFN is the binary symbol file for the resource. It is in the 
format used by the NEW ST Resource Construction Set. This fixes a number 
of bugs, and has a much faster user interface.


MAKING PROGRESS

The need for feedback in interface designs has been discussed in 
previous columns. One instance which is often necessary is the so-called 
progress indicator. A progress indicator is used when your application 
is doing a long operation. It shows that the function is continuing 
satisfactorily, and is not hung in a loop. When possible, it also gives 
an indication of the fraction of the operation which has been completed. 
The thermometer bars on the Desktop format and copy operations are 
examples.

The sample code shows two types of progress indicator. Both are built 
within the structure of a dialog resource. The first type uses a 
variable line of text to describe each phase of an operation as it 
occurs. The rewriting of the text provides action on the screen; the 
fact that it is different each time gives reassurance that the program 
is not hung. The second type of indicator is the thermometer bar. This 
is more useful when the operation is uniform, allowing you to estimate 
the fraction completed. Let's look at the code.

The routines beg_prog() and end_prog() are common to the two types. The 
code is very similar to the standard dialog handling procedure, but is 
broken into two parts. Beg_prog() assumes that the progress indicator 
box is defined by a dialog tree named PROGRESS. Such a tree is provided 
in GEMCL16.RSC. Beg_prog() makes the usual calls to center and draw the 
box. The rectangle computed in the centering operation is stored via a 
GRECT pointer passed in the parameter. This rectangle compensates for 
the outline around the box, and must be supplied to end_prog() when the 
operation is complete.

The first version of set_prog() in the download implements the changing 
text progress indicator. It looks in a tree labelled STRINGS for the 
object number which is passed as a parameter. It is assumed that this 
object is a G_STRING. The address of the new text is loaded from the 
object's ob_spec field. (For those with the new RCS, it would be easy to 
alter this routine to use free strings. Simply replace the first two 
lines with: rsrc_gaddr(R_STRING, strno, &saddr); and supply parameters 
which are the names of strings in a FRSTR box.)

Once the new text is found, the set_text() utility is called to update 
the TEDINFO attached to object PLINE in the PROGRESS tree. Set_text() 
will insert the new text address in te_ptext, and the new text length in 
te_txtlen. Disp_obj() is then used to redraw only the rectangle 
belonging to PLINE.

PLINE must be defined as a G_BOXTEXT object with a solid white 
background, and with the CENTERED attribute set. It must extend entirely 
across the progress box. This guarantees that the previous text will be 
covered over, and the new text will be centered in the box.

The second version of set_prog() implements the thermometer bar progress 
indicator. The PROGRESS tree also includes an object PROBOX which 
defines the outline of the thermometer. It is a G_BOX object with a 
solid white background, and a one-outside border. The object PROBAR is 
nested inside it, with the left edges matching. PROBAR is also a G_BOX, 
with a solid red background and a one-outside border as well. Set_prog() 
creates the thermometer effect by growing and redrawing PROBAR.

Set_prog() requires two parameters. Maxc is an estimate of the total 
duration of the operation, in arbitrary units. Value is the (new) amount 
completed, in the same units. Set_prog performs two operations. First, 
it computes the fraction value/maxc, and sets PROBAR to that fraction of 
the width of PROBOX. Second, it computes the rectangle which is the 
difference between the old and new widths of PROBAR, and redraws only 
that part of the progress box. This prevents an annoying flash on the 
screen when the indicator is updated.

These two types of progress indicators have been presented in separate 
routines for convenience in explanation. You can easily combine them in 
a single procedure to create an indicator with both effects.

The final progress indicator routine is called esc_prog(). During many 
lengthy operations is desirable to provide an abort option to the user. 
Esc_prog() lets you do this by polling the keyboard for an escape (ESC) 
character. A zero timer value is used to guarantee an immediate return 
if no character is found. Characters other than escape are ignored.

Esc_prog() returns TRUE if an abort is requested, and FALSE if the 
operation is to continue. In your application, you can either pair calls 
to set_prog() and esc_prog(), or recode set_prog() to automatically make 
the abort check. In any case, you should add an information line to the 
progress box, telling the user how the operation may be halted.

Of course, this type of progress indicator is not the only option 
available on the ST. Other ideas such as changing window titles, or 
displaying a succession of differing icons are equally valid. Sometimes 
the nature of your application may suggest an alternate metaphor. For 
instance, the progress of recalculating a spreadsheet might be indicated 
by darkening successive columns in a miniature image of the sheet. 
Occasionally, the computing operation is visual itself, and will not 
require an explicit indicator. An example is redisplaying objects in a 
2D or 3D drawing program.


BOXED IN

The second part of the download implements two types of user interaction 
using the mouse. The first creates a "rubber box" on the screen, that 
is, a box whose size is controlled by moving the mouse. This is similar 
to the AES graf_rubberbox call, but allows the box to move in any 
direction from its origin, while the GEM function only allows movement 
to the lower right.

The second technique allows the user to drag the outline of a box around 
the screen using the mouse. Again, this is similar to the AES 
graf_dragbox call, but this version is augmented with code which 
"hotspots" selectable objects when the mouse and object pass over them. 
These routines are another illustration of the usage of the evnt_multi 
function, and its combination with VDI drawing to create new interaction 
techniques.

The "rubber box" subroutine is called fourway_box(). Its parameters are 
the current VDI handle (NOT a window handle!), and two GRECT pointers. 
The first GRECT must have its g_x and g_y initialized with the fixed 
point of the rubber box. The second GRECT contains an outer bound box 
for the stretching action.

Fourway_box() begins by setting the VDI drawing mode and color. The 
exclusive or, black combination guarantees that redrawing a figure twice 
in the same location will exactly erase it. Next, the routine asserts 
the mouse control flag. This stops the window manager from tracking the 
mouse, with the effect that menus will not drop down during the 
operation.

The fixed coordinates are saved in the variables ox and oy, and an 
initial mouse reading is obtained with graf_mkstate. At this point, the 
event loop is entered.

At each iteration, the loop finds the upper left most of the fixed 
vertex and the current mouse position, and updates the tracking GRECT 
accordingly. A call to the utility rc_intersect() is used to restrict 
the size of the rubber box to the given limiting rectangle. Note that if 
you need a lower limit to the size of the rubber box, it can be achieved 
by adding another GRECT pointer "lower" to the parameter list, and using 
the call rc_union(lower, rubber); This works because the union operation 
selects the larger of two rectangles if they are nested.

Rub_wait() will be described in detail below. Its returns are the new 
mouse position, and an indication of the current mouse button state. If 
the button remains down, the loop continues. When the button is 
released, the rubber box terminates, since it is a "spring-loaded" modal 
operation. Before ending, fourway_box() returns mouse control to the 
window manager. The return from the routine is found in the rubber 
GRECT, and is the final extent of the box.

Rub_wait() is a utility used by both box techniques. Its purpose is to 
do one step of the box animation, and wait for a mouse movement, or the 
release of the button. Rub_wait() preserves the state of the screen.

The first action is to draw an exclusive or'ed dotted line box at the 
given rectangle. Next, rub_wait() calls evnt_multi to wait for the mouse 
button to come up, or the mouse to move out of a one pixel rectangle. 
When the event is detected, the same code is used to remove the box. A 
value of TRUE is returned if the mouse button is still down; the curious 
logical construction is necessary since BOTH events could occur at once.

A short examination of the vdi_xbox() code is also useful. After 
converting the rectangle to polyline format, the vdi_xline() routine is 
called. Vdi_xline draws a dotted line, but does not use the VDI line 
style attribute. This is avoided because the VDI has problems with 
corner points when drawing styled lines in XOR mode. Instead, a 
selection is made from a set of user defined line styles, based on the 
direction of the stroke, and the odd/evenness of the starting horizontal 
pixel. This assures that the figure will be exactly erasable.


HOT STUFF?

The drag box routine is more subtle, because care is needed to correctly 
synchronize the movement of the mouse cursor and the box, and the 
highlighting of target objects. The parameters vdi_handle and limit are 
identical to those in fourway_box(). The GRECT pointed to by box 
contains the width and height of the movable box when hot_dragbox() is 
entered. On exit it also contains the last x,y coordinates of the box. 
The variable tree is a pointer to the root of a resource tree defining 
the hot spots for the drag operation. Only objects tagged SELECTABLE are 
hotspotted. Hot_dragbox() returns the number of such an object if the 
box is "dropped" on it, otherwise a NIL is returned.

Initialization proceeds as above, until the graf_mkstate call. Here is 
the first potential synchronization problem. If the user moves the mouse 
very quickly after initiating the drag, it may already be outside the 
box by the time graf_mkstate samples the position. The min/max 
operations given lock the box onto the cursor, no matter where it has 
strayed. The mouse/box offsets, ox and oy, will remain constant for the 
rest of the operation.

Hover_obj will contain the number of the object which is currently 
highlighted. It is initialized to NIL, indicating no object is currently 
marked. Hot_dragbox() now enters a loop with termination conditions 
identical to the rubber box.

The current desired position of the box is computed by subtracting the 
box/mouse offset from the current mouse position. The rc_constrain() 
call ensures that the box will not leave the bounding rectangle. Note 
that rc_intersect would not work here - it would alter the size of the 
draggable box, rather than "nudging" it back into the bounds.

Upon return from rub_wait(), a number of conditions must be checked to 
determine the correct object to highlight, if any. First, we must make 
sure that the mouse is actually within the legal bounds. If not, there 
may be an ambiguous selection, with the mouse over one object and the 
box over another. We choose to do nothing in this case, and set 
hover_obj to NIL. If the mouse is in bounds, objc_find looks for a 
target object. If one exists, it must be SELECTABLE, or it is forced to 
NIL.

Next the new object, stored in ret_obj, is compared to the old 
highlighted object, in hover_obj. If they are different, a switch must 
be made. Since either could be NIL, a check is necessary before calling 
obj_toggle to invert/reinvert the screen image of the object. When the 
loop is complete, the final hover_obj is returned to normal state before 
its number is returned.

You may notice that this method of highlighting objects is different 
from the incremental tree descent and rectangles method presented in 
column 13. While not as efficient, the objc_find technique is simpler to 
code and may be adequate for many uses. If your program will make heavy 
use of the drag box routine, or will have large trees of target objects, 
you may wish to integrate the incremental hotspotting algorithm with 
hot_dragbox(). This would be simple to do; just use evnt_multi's second 
mouse rectangle for the states associated with the hot- spotter. The 
single pixel rectangles would have to remain, in order to maintain the 
animation effects.


A FEW CHANGES

The observant may have noticed that the promised code for popup menus 
did not make it into this column. Instead, it will appear in column 18 
along with more "graphics potpourri" and feedback replies. The 
intervening installment, number 17, will present and document the source 
code for a complete IBM/Atari GEM Resource conversion program. This will 
appear concurrently with Mark Skapinker's article on IBM/ST GEM 
conversions in the second issue of START. While this program will be of 
direct use to only a minority of ST developers, it will contain utility 
code useful to all, as well as demonstrations of dialog handling and the 
internal structure of resources.

Finally, you may also notice that the so-called portability macros have 
disappeared from the download. Indeed, they are gone for good. Since the 
beginning of this column, the growth of the ST GEM developer community 
has outstripped that on the PC. It no longer seems appropriate to 
inconvenience ST developers and violate standard C syntax for the sake 
of Intel's design flaws. Those who still need compatibility with the PC 
may achieve it by compiling under Intel large model, or by writing "sed" 
scripts to translate (tree+obj)->ob_spec and the like to their macro 
equivalents.


----
