Permission to reprint or excerpt is granted only if the following line appears at the top of the article: ANTIC PUBLISHING INC.,COPYRIGHT 1986. REPRINTED BY PERMISSION. PROFESSIONAL GEM By Tim Oren Column #16 - Interface Potpourri #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. The downloads for this column are available on DL3 of the ATARI16 SIG: PCS-58. Note the plural - in addition to the usual C sources stored in GMCL16.C, the files GMCL16.RSC, GMCL16.DFN, GMCL16.H, and GMCL16.RSH are a template resource for building progress boxes. GMCL16.RSC is the resource binary, and GMCL16.H is its symbol binding file, to be used with GMCL16.C. The RSH file is a C image of the resource - you would need STCREATE to regenerate it. GMCL16.DFN is the binary symbol file for the resource. It is in the format used by the NEW ST Resource Construction Set. If you are a developer, you should download this new version from DL7 of PCS-57. It 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 GMCL16.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.