Using QuickSilver Windows

By: Ramón H. Venegas

The user interface is all important for the success of any software program. Currently, if a user is used to light-bar menus and windows, then any lesser interface would be judged unacceptable. This type of interface can easily be implemented in any xBASE dialect. The next step in the interface evolution is another story. Users are now demanding an "MS Windows" interface. To us developers that means that at the very minimum we must implement a character based Common User Access (CUA) interface, which is what "MS Windows" does in graphics mode.

Unfortunately, there is no easy way to implement CUA in any xBASE dialect, yet. So what do we do in the mean time? I for one use the windowing features available in QuickSilver (QS) to provide as much CUA-like functionality as possible to my users. Using QS one can have create browse windows, dialogue boxes, moving windows, etc. The key is that you can create windows, what you put inside them (or on their "frame") is up to you! Next, I'd like to describe a couple of QS UDFs that I use to open and close windows and a procedure to move a window around the screen with the cursor keys.

Creating and Disposing of QS Windows

Programming with QS windows requires at least defining the window name and coordinates of two corners (i.e., WSET WINDOW wname TO TopRow, TopLeftCol, BottRow, BottLetfCol), selecting one of 100 window areas (i.e., WSELECT warea) and then activating it (i.e., WUSE wname). Window areas are similar to DBF areas, because you can only have on window defined (opened) per window area.

Guided by structure programming principles and my desire to minimize "version shock," I always try to confine all windowing commands within UDFs. These UDFs also save me from having to keep track of available or used window areas and names.

I use the UDFs in Fig. 1 and 2 to define, create and then dispose of all windows in my applications. OpenWnd() is a UDF that creates a window while ClsWnd() disposes of it. OpenWnd() requires as parameters a window title (wtitle) and four screen coordinates TopRow, TopLeftCol, BottRow, BottLetfCol (tr,tc,br,bc). ClsWnd() merely requires that the window to be disposed of (i.e., closed and released) be active when the function is called.

Both functions use global variables that track window numbers and names. One global, cur_wndo, tracks the window area numbers where windows are opened and the other global, cur_wndn, tracks the window names. These two globals free me from having to track used window areas and window names. The globals also prevent me closing overlapping windows in the wrong order. Overlapping QS windows should be closed from outermost to innermost.

Using OpenWnd() would look like this:

Wnd_1=open_wnd(" Window 1 ",8,41,11,78)

SET COLOR TO W/GR+

CLEAR CHARACTER CHR(233)

* put code related to Wnd_1 here

?? ClsWnd() && - disposes of Wnd_1!

which opens a 40 column window with 6 screen rows, Fig. 3. One would next set any colors and background character desired. Color settings apply to the current window only. Note that I say 6 rows. The contents of the window start at screen row 8 and end at row 11. The window borders occupy rows 7 and 12 and columns 40 and 79. All SAYs and GETs are then automatically related to the current window dimensions. For example @ 3,20 would point to middle of the last row in this window. Any SAYs and GETs with row number greater than 3 would be invalid since there are only 4 rows in this window, 0 through 3! Closing this window would merely require a call to ClsWnd(), as in ?? ClsWnd(). The window number returned by OpenWnd() could be used to refer later to a particular window, e.g., in case windows are accessed randomly. A second call to OpenWnd() would create another window, possibly overlapping Wnd_1. Two successive calls to ClsWnd() would close both windows from last to first. A full screen window requires 4,144 bytes of RAM in my monochrome hercules compatible monitor.

QS also provides features to further customize the frame of a window. You could for example display a scrolling bar on the right-hand border of the frame or specify a double line frame. The WSET WINDOW TO command contains syntax to define a character for the left-top corner, top-border, right-top corner, left-border, right-border, left-bottom corner, bottom-border and right-lower corner. This syntax permits creating frames that match your specific needs.

Moving Windows "a la SideKick"

QuickSilver offers a full complement of commands and functions to reposition windows under user control. The program WndMove.prg (Fig. 4) uses some of these capabilities to allow the user to drag a window with the cursor keys. WndMove first checks that the currently selected window is not the entire screen (i.e., window area 0) and then accepts INKEY() values for the arrow keys. Inside a CASE construct, each arrow key input is translated into new TopRow and TopColumn coordinates. WMOVE is then used to move the whole window by specifying the new TopRow,TopCol coordinates for the top-left window corner.

WndMove is executed by assigning it to a hot key. In QS this is done with the SET KEY

command. All the contents of the window will also move, including any pending GETs. One must remember to deactivate (SET KEY "F2" TO) a program used in this fashion within the program itself. Forget this and your stack will eventually blow up! Note that WndMove reactivates itself just before returning. A sample program using WndMove is shown in Fig. 5.

You might be thinking what if the user tries to move the window to where it does not fit? Well, you got me there. WndMove as presented here does not keep track of the height or the width of the window being moved. This "anomaly" however can be easily avoided by declaring two more global variables, one for height and one for width. WndMove could use these globals to calculate the last and first allowable top-left corner coordinates of the current window.

QuickSilver windows together with the SET KEY command offer vast CUA-like possibilities. One could for example simulate "nested READs" using ACCEPT or INPUT commands within pop-up windows activated with SET KEY hot keys on top of a data entry window.

I hope the techniques I have described are useful to you. We are still far from true CUA with the current set of QS commands but by carefully implementing what we have we can have a smooth transition to the true CUA capabilities in the awaited ARAGO QuickSilver/dBXL product. In the mean time I recommend taking a look at the WordTech Bulletin Board Technical support, it is filled with useful code and utilities. You can reach that BBS at (415) 254-1141 (1200/2400 8,N,1). WordTech has also recently joined CompuServe. Log into CompuServe and GO PCVENC area 7. Both of these online services provided by WordTech are an excellent resource for novice and expert alike.