home *** CD-ROM | disk | FTP | other *** search
- **Professional GEM**
- by Tim Oren
-
- THE DIALOG HANDLER
- 11/7/85
-
-
- A MEANINGFUL DIALOG
-
- This issue of ST PRO GEM begins an exploration of ST GEM's dialog
- handler. I will discuss basic system calls for presenting the
- dialog, and then continue with techniques for initializing and
- reading on/off button and "radio" button objects. We will also take
- some short side-trips into the operation of the GEM Resource
- Construction Set to assist you in building these dialogs.
-
- There are a number of short C routines which accompany this column.
- These are stored as file GEMCL3.XMO in DL 5 on SIG*ATARI. Before
- reading this column, you should visit SIG*ATARI (go pcs-132) and
- download this file.
-
- DEFINING TERMS
-
- A dialog box is an "interactive form" in which the user may enter
- text and indicate selections by pointing with the mouse. Dialogs in
- GEM are "modal", that is, when a dialog is activated other screen
- functions such as menus and window controls are suspended until the
- dialog is completed.
-
- In most cases, the visual structure of a GEM dialog is specified
- within your application's resource file. The GEM Resource
- Construction Set (RCS) is used to build a picture of the dialog.
-
- When the RCS writes out a resource, it converts that picture into a
- tree of GEM drawing objects and stores this data structure within the
- resource. Before your application can display the dialog, it must
- load this resource file and find the address of the tree which
- defines the dialog.
-
- To load a resource, the AES checks its size and allocates memory for
- the load. It then reads in the resource, adjusting internal pointers
- to reflect the load address. Finally, the object sizes stored in the
- resource are converted from characters to pixels using the system
- font size.
-
- (A note for those with Macintosh experience: Although Mac and GEM
- resources share a name, there are fundamental differences which can
- be misleading. A Mac resource is a fork within a file; a GEM
- resource is a TOS file by itself. Mac resources may be paged in and
- out of memory; GEM resources are monolithic. GEM resources are
- internally tree structured; Mac resources are not. Finally, Mac
- resources include font information, while ST GEM does this with font
- loading at the VDI level.)
-
- The resource load is done with the GEM AES call:
-
- ok = rsrc_load(ADDR("MYAPP.RSC"));
-
- "MYAPP" should be replaced with the name of your program. Resources
- conventionally have the same primary name as their application, with
- the RSC extent name instead of PRG. The ok flag returned by
- rsrc_load will be FALSE is anything went wrong during the load.
-
- The most common causes of failure are the resource not being in the
- application's subdirectory, or lack of sufficient memory for GEM to
- allocate space for the resource. If this happens, you must terminate
- the program immediately.
-
- Once you have loaded the resource, you find the address of a dialog's
- object tree with:
-
- rsrc_gaddr(R_TREE, MYDIALOG, &tree);
-
- Tree is a 32-bit variable which will receive the address of the root
- node of the tree.
-
- The mnemonic MYDIALOG should be replaced with the name you gave your
- dialog when defining it in the RCS. At the same time that it writes
- the resource, RCS generates a corresponding .H file containing tree
- and object names. In order to use these mnemonics within your
- program, you must include the name file in your compile: #include
- "MYAPP.H"
-
- BUG ALERT!
-
- When using the DRI/Alcyon C compiler, .H files must be in the
- compiler's home directory or they will not be found. This is
- especially annoying using a two floppy drive ST development system.
- The only way around this is to explicitly reference an alternate
- disk in the #include, for instance: "B:MYAPP.H"
-
- Now that the address of the dialog tree has been found, you are ready
- to display it. The standard (and minimal) sequence for doing so is
- given in routine hndl_dial() in the download. We will now walk
- through each step in this procedure.
-
- The form_center call establishes the location of the dialog on the
- screen. Dialog trees generated by the RCS have an undefined origin
- (upper-left corner).
-
- Form_center computes the upper-left location necessary to center the
- dialog on the screen, and inserts it into the OB_X and OB_Y fields of
- the ROOT object of the tree. It also computes the screen rectangle
- which the dialog will occupy on screen and writes its pixel
- coordinates into variables xdial, ydial, wdial, and hdial.
-
- There is one peculiarity of form_center which occasionally causes
- trouble. Normally the rectangle returned in xdial, etc., is exactly
- the same size as the basic dialog box.
-
- However, when the OUTLINED enhancement has been specified for the
- box, form_center adds a three pixel margin to the rectangle returned.
- This causes the screen area under the outline to be correctly redrawn
- later (see below). Note that OUTLINED is part of the standard dialog
- box in the RCS. Other enhancements, such as SHADOWED or "outside"
- borders are NOT handled in this fashion, and you must compensate for
- them in your code.
-
- The next part of the sequence is a form_dial call with a zero
- parameter. This reserves the screen for the dialog action about to
- occur. Note that the C binding given for form_dial in the DRI
- documents is in error: there are nine parameters, not five. The
- first set of xywh arguments is actually used with form_dial calls 1
- and 2 only, but place holders must be supplied in all cases.
-
- The succeeding form_dial call (parameter one) animates a "zoom box"
- on the screen which moves and grows from the first screen rectangle
- given to the second rectangle, where the dialog will be displayed.
-
- The use of this call is entirely optional. In choosing whether to
- use it or not, you should consider whether the origin of the "zoom"
- is relevant to the operation. For instance, a zoom from the menu
- bar is relatively meaningless, while a zoom from an object about to
- be edited in the dialog provides visual feedback to the user, showing
- whether the correct object was chosen.
-
- If the origin is not relevant, then the zoom is just a time-waster.
- If you decide to include these effects, consider a "preferences"
- option in your app which will allow the experienced and jaded user
- to turn them off in the interests of speed.
-
- The objc_draw call actually displays the dialog on the screen. Note
- that the address of the tree, the beginning drawing object, and the
- drawing depth are passed as arguments, as well as the rectangle
- allotted for the dialog.
-
- In general, dialogs (and parts of dialogs) are ALWAYS drawn
- beginning at the ROOT (object zero). When you want to draw only a
- portion of the dialog, adjust the clipping rectangle, but not the
- object number. This ensures that the background of the dialog is
- always drawn correctly.
-
- The objc_xywh() utility in the download can be used to find the
- clipping rectangle for any object within a dialog, though you may
- have to allow an extra margin is you have used shadows, outlines, or
- outside borders with the object.
-
- Calling form_do transfers control to the AES, which animates the
- dialog for user interaction. The address of the dialog tree is
- passed as a parameter. The second paramter is the number of the
- editable object at which the text cursor will first be positioned.
- If you have no text fields, pass a zero. Note that again the DRI
- documents are in error: passing a -1 default may crash the system.
- Also be careful that the default which you specify is actually a text
- field; no error checking is performed.
-
- The form_do call returns the number of the object on which the
- clicked to terminate the dialog. Usually this is a button type
- object with the EXIT and SELECTABLE attributes set. Setting the
- DEFAULT attribute as well will cause an exit on that object is a
- carriage return is struck while in the dialog.
-
- If the top bit of the return is set, it indicates that the exit
- object had the TOUCHEXIT attribute and was selected with a
- double-click. Since very few dialogs use this combination, the
- sample code simply masks off the top bit.
-
- The next form_dial call reverses the "zoom box", moving it from the
- dialog's location back to the given x,y,w,h. The same cautions apply
- here as above.
-
- The final form_dial call tells GEM that the dialog is complete, and
- that the screen area occupied by the dialog is now considered "dirty"
- and needs to be redrawn. Using the methods described in our last
- column, GEM then sends redraws to all windows which were overlaid,
- and does any necessary redrawing of the menu or desktop itself.
-
- There is one notable "feature" of form_dial(3): It always redraws
- an area which is two pixels wider and higher than your request! This
- was probably included to make sure that drop-shadows were cleaned up,
- and is usually innocuous.
-
- A HANDY TRICK
-
- Use of the form_dial(3) call is not limited to dialogs. You can
- use it to force the system to redraw any part of the screen. The
- advantage of this method is that the redraw area need not lie
- entirely within a window, as was necessary with the send_redraw
- method detailed in the last column. A disadvantage is that this
- method is somewhat slower, since the AES has to decide who gets the
- redraws.
-
- CLEAN UP
-
- As a last step, you need to clear the SELECTED flag in the object
- which was clicked. If you do not do this, the object will be drawn
- inverted the next time you call the dialog. You could clear the flag
- with the GEM objc_change call, but it is inefficient since you do
- not need to redraw the object.
-
- Instead, use the desel_obj() code in the download, which modifies
- the object's OB_STATE field directly. Assuming that ret_obj
- contains the exit object returned by hndl_dial, the call:
-
- desel_obj(tree, ret_obj);
-
- will do the trick.
-
- RECAP
-
- The basic dialog handling method I have described contains three
- steps: initialization (rsrc_gaddr), dialog presentation (hndl_dial),
- and cleanup (desel_obj).
-
- As we build more advanced dialogs, these same basic steps will be
- performed, but they will grow more complex. The initialization will
- include setting up proper object text and states, and the cleanup
- phase will also interrogate the final states of objects to find out
- what the user did.
-
- BUTTON, BUTTON
-
- The simple dialogs described above contain only exit buttons as
- active objects. As such, they are little more than glorified alert
- boxes.
-
- We will now increase the complexity a little by considering non-exit
- buttons. These are constructed by setting the SELECTABLE attribute
- on a button object. At run-time, such an object will toggle its
- state between selected (highlighted) and non-selected whenever the
- user clicks on it. (You can set the SELECTABLE attribute of other
- types of objects and use them instead of actual buttons, but be sure
- that the user will be able to figure out what you intend!)
-
- Having non-exit buttons forces us to consider the problem of
- initializing them before the dialog, and interrogating and resetting
- them afterward.
-
- Since a button is a toggle, it is usually associated with a flag
- variable in the program. As part of the initialization, you should
- test the flag variable, and if true call:
-
- sel_obj(tree, BTNOBJ);
-
- which will cause the button to appear highlighted when the dialog is
- first drawn. Sel_obj() is in the download. BTNOBJ is replaced with
- the name you gave your button when you defined it in the RCS. Since
- the button starts out deselected, you don't have to do anything if
- your flag variable is false.
-
- After the dialog has completed, you need to check the object's state.
- The selectp() utility does so by masking the OB_STATE field. You can
- simply assign the result of this test to your flag variable, but be
- sure that the dialog was exited with an OK button, not with a CANCEL!
- Again, remember to clean up the button with desel_obj(). (It's often
- easiest to deselect all buttons just before you leave the dialog
- routine, regardless of the final dialog state.)
-
- WHO'S GOT THE BUTTON?
-
- Another common use of buttons in a dialog is to select one of a set
- of possible options. In GEM, such objects are called radio buttons.
- This term recalls automobile radio tuners where pushing in one button
- pops out any others. In like fashion, selecting any one of a set of
- radio buttons automatically deselects all of the others.
-
- To use the radio button feature, you must do some careful work with
- the Resource Construction Set.
-
- First, each member of a set of radio buttons must be children of the
- same parent object within the object tree. To create this structure,
- put a hollow box type object in the dialog, make it big enough to
- hold all of the buttons, and then put the buttons into the box one at
- a time.
-
- By nesting the buttons within the box object, you force them to be
- its children. Each of the buttons must have both the SELECTABLE and
- RADIO BUTTON attributes set. When you are done, you may make the
- containing box invisible by setting its border to zero, but do not
- FLATTEN it!
-
- Since each radio button represents a different option, you must
- usually assign a name to each object. When initializing the dialog,
- you must check which option is currently set, and turn on the
- corresponding button only. A chain of if-then-else structures
- assures that only one button will be selected.
-
- At the conclusion of the dialog, you must check each button with
- selectp() and make the appropriate adjustments to internal variables.
- Again, an if-then-else chain is appropriate since only one button may
- be selected. Either deselect the chosen button within this chain or
- do them all at the end.
-
- There is one common use of radio buttons in which you may short-cut
- this procedure. If the buttons each represent one possible value of
- a numeric variable, for instance, a set of selector buttons
- representing colors from zero to seven, then you can compute the
- initial object directly.
-
- In order for this technique to work, you must use a special
- capability of the RCS. Insert the object corresponding to a zero
- value at the top (or left) of your array of buttons, then put the
- "one" button below (or right) of it, and so on.
-
- When the buttons are complete, the SORT operation is used to
- guarantee that the top/left object is in fact the first child of the
- parent box with the others following in order. Due to the details of
- object tree structure (to be discussed in the next column), this will
- guarantee that these objects are contiguous in the resource.
-
- If you assign a name (say BUTTON1) to the first button, then you can
- initialize the correct button with the call:
-
- sel_obj(tree, BUTTON1 + field);
-
- where field is the variable of interest.
-
- When the dialog is complete, you can scan the radio buttons to
- compute the new value for the underlying variable. The encode()
- procedure in the download will do this. As always, remember to
- deselect the buttons at the end.
-
- You can use offsets or multipliers if your variable's values don't
- start with zero or increment by one. If the values are irregular you
- may be able to use a lookup table, at the cost of additional code.
-
- COMING UP NEXT
-
- In the next column, I will discuss the internal structure of object
- trees. Then we'll use that knowledge to build a piece of code which
- will "walk" an entire tree and apply a function to each object.
- We'll apply this code to do all of the button deselects with a
- single call! I'll also look at handling editable text fields and
- discuss some ways to alter a dialog's appearance at run-time.
-
- DISPELL GREMLINS
-
- An editing error caused an omission in the first installment of ST
- PRO GEM. The window components RTARROW and DNARROW should have been
- listed along with HSLIDE as the horizontal equivalents of the
- vertical slider components which were discussed.
-
-
-