Article View: net.micro.atari16
Article #969ProGem Parts 13-14 (1 of 2)
From: Jwahar R. Bammi
Date: Fri, 27 Jun 1986 06:58
Date: Fri, 27 Jun 1986 06:58
971 lines
37331 bytes
37331 bytes
Here are the next two installments of Tim Orens Professional Gem
Seminar. I am including toc.prf and macros.prf again.
#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# wind13.prf
# apndx10.prf
# This archive created: Fri Jun 27 02:51:51 1986
# By: Jwahar R. Bammi ()
export PATH; PATH=/bin:$PATH
echo shar: extracting "'wind13.prf'" '(18936 characters)'
if test -f 'wind13.prf'
then
echo shar: over-writing existing file "'wind13.prf'"
fi
sed 's/^X//' << \SHAR_EOF > 'wind13.prf'
X.!****************************************************************************
X.!
X.! ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION.
X.!
X.! ** Professional GEM ** by Tim Oren
X.!
X.! Proff File by ST enthusiasts at
X.! Case Western Reserve University
X.! Cleveland, Ohio
X.! uucp : decvax!cwruecmp!bammi
X.! csnet: bammi@case
X.! arpa : bammi%case@csnet-relay
X.! compuserve: 71515,155
X.!
X.!****************************************************************************
X.!
X.!
X.!****************************************************************************
X.!
X.! Begin Part XIII
X.!
X.!****************************************************************************
X.!
X.PART XIII A New Form Manager
X.PP
XThis is the 13th installment of ST PRO GEM, and the first
Xdevoted to explaining a large piece of code. This article is also
Xthe second in a series of three concerning GEM user interface
Xtechniques. The code is an alternate form (dialog) manager for
XGEM. It is stored as GMCL13.C in DL3 of PCS-58. You should go
Xand download it now, or you will have no hope of following this
Xdiscussion.
X.PP
XWhat is unique about this version of the form manager?
XFirst, it implements all of the functions of the standard GEM
Xform_do routine, as well as adding a "hot spots" feature which
Xcauses selectable objects to become mouse-sensitive, just like
Xthe entries in menu dropdowns. The second (and obvious)
Xdifference is that this form manager is provided in source code
Xform. This gives you the freedom to examine it and change it to
Xsuit your own needs.
X.PP
XI have several purposes in presenting this code. It is
Xintended as an example of GEM program structure, and an
Xapplication of some of the techniques presented in earlier
Xcolumns. It is also relevant to the continuing thread discussing
Xthe necessity of feedback when constructing a user interface.
X.PP
XAlso, this issue represents the beginning of a fundamental
Xchange in direction for ST PRO GEM. Since this column began last
XAugust, Atari ST developers have increased not only in number, but
Xin sophistication. A number of books, as well as back issues of
XST PRO GEM, are now available to explain the basics of the ST and
XGEM. Quick answers to common questions are available here on
XCompuserve's PCS-57 from Atari itself, or from helpful members of
Xthe developer community.
X.PP
XTo reflect these changes, future columns will discuss more
Xadvanced topics in greater depth, with an accent on code which can
Xbe adapted by developers. The program presented in this issue
Xwill be a basis for a number of these discussions. There will be
Xfewer "encyclopediac" treatments of AES and VDI function calls.
XFinally, to give me the time required to create this code or clean
Xup tools from my "bag of tricks", ST PRO GEM will probably convert
Xto a monthly format around the start of summer.
X.SH ON WITH THE SHOW
XTaking your listing in hand, you will
Xquickly notice two things. First, this program uses the infamous
Xportability macros, so that it may be used with Intel versions of
XGEM. Second, the routines are arranged "bottom up", with the main
Xat the end, and subroutines going toward the beginning. (This is
Xa carry-over from my days with ALGOL and PASCAL.) You should now
Xturn to the form_do entry point near the end of the code.
X.PP
XOne change has been made in the standard calling sequence for
Xm_do. The starting edit field is now a pointer to a value,
Xrather than the value itself. The new form_do overwrites the
Xinitial value with the number of the object being edited when the
Xdialog terminated. Using this information, your program can
Xrestore the situation when the dialog is next called. As before,
Xif there is NO editable field, the initial value should be zero.
X.PP
XThere are several local variables which maintain vital state
Xinformation during the dialog interaction. Edit_obj is the number
Xof the editable object currently receiving keystrokes. Next_obj
Xis set when the mouse is clicked over an object. If the object
Xhappens to be editable, next_obj becomes the new edit_obj.
X.PP
XThree variables are associated with the "hot-spot" feature.
XIf hot_mode is set to M1_ENTER, then the mouse is outside the area
Xof the dialog. If it equals M1_EXIT, then the mouse is currently
Xin the dialog. If it is in the dialog, hot_obj indicates whether
Xthere is an active "hot" object. If its value is NIL (-1), then
Xthere is no active object. Otherwise, it is equal to the number
Xof the object which is currently "hot", that is, inverted on the
Xscreen. Finally, hot_rect is the current wait rectangle. If the
Xmouse is outside of the window, then the wait rectangle equals the
Xdialog's ROOT. If there is a current hot object, then hot_rect
Xequals that object's screen rectangle. If the mouse is in the
Xdialog, but not within a hot object, then the wait rectangle
Xdefines the area within which no further collision checks are
Xnecessary. This is arrived at through an algorithm explained
Xbelow.
X.PP
XForm_do's initialization code sets up the hot-spot variables
Xto trigger if the mouse is within the dialog. It also sets
Xstarting values for edit_obj and next_obj which will cause the
Xedit startup code to be activated.
X.PP
XThe main portion of form_do is a loop, exhibiting the type of
Xevent driven structure discussed in the last column. Before
Xentering the evnt_multi wait, the status of next_obj and edit_obj
Xare checked to see if a new object should be initialized for
Xediting. If so, objc_edit is called with the EDINIT function
Xcode. NOTE: the objc_edit calling sequence used in this program
Xdiffers from the one given in the AES manual, which is incorrect!
XYou should check the bindings you are using to be sure they will
Xwork with this code, and modify as necessary.
X.PP
XThe evnt_multi is set up to wait for a mouse click (single or
Xdouble), for a keyboard input, or for the mouse to make a
X"significant" movement, as discussed above. Notice that since
Xform_do is used as a subroutine, it does not handle messages which
Xare normally processed by the main loop of your application.
XNotice that this creates a mode, and that this routine as written
Xhandles modal dialogs. You could, however, use this code as the
Xbasis for a non-modal dialog handler by drawing the dialog within
Xa window, and combining the main loop of form_do with the main
Xloop of your application. (This possibility may be examined in
Xfuture columns. In the meantime, it is left as an exercise for
Xthe reader.)
X.PP
XThe event bit vector is returned to the variable "which".
XSince events are not mutually exclusive, each possible event type
Xmust be examined in turn before returning to the evnt_multi call.
XThe form manager's event handling routines are form_hot, for mouse
Xrectangle event, form_keybd, for character input, and form_button,
Xfor mouse clicks. Form_keybd and form_button are allowed to
Xterminate the dialog by returning a value of false to the loop
Xcontrol variable "cont". If termination is imminent, or the user
Xhas clicked on a new editable object, objc_edit is called with
XEDEND to remove the cursor from the old object. The normal flow
Xof control then returns to edit setup and evnt_multi.
X.PP
XA few cleanup actions are performed upon termination. If the
Xterminating object (stored in next_obj) is not the same as the
Xhot_obj, then a race condition has occured and the hot object must
Xbe cleared with objc_toggle before exiting. After this test, the
Xfinal edit_obj value is passed back via the parameter, and the
Xterminating object is returned as the function value.
X.SH RELAXEN UND WATCHEN DAS BLINKENLICHTE
XForm_hot is
Xresponsible for maintaining on-screen hot-spots, and correctly
Xupdating the internal hot-spot variables. It is about halfway
Xthrough the listing.
X.PP
Xhe first action in form_hot is to determine if the mouse has
Xjust exited an object which is "hot. In this case, objc_toggle is
Xcalled to unhighlight the object and reset the SELECTED flag.
X.PP
XThe current mouse position is passed to form_hot by form_do.
XIt is checked against the root rectangle of the dialog to see if
Xthe mouse is inside the dialog. If not, the program must wait for
Xit to re-enter, so form_hot sets the rectangle and waiting mode
Xaccordingly, and returns NIL as the new hot_obj.
X.PP
XWhen the mouse is within the dialog, a regular objc_find call
Xdetermines the object at which it is pointing. For an object to
Xbe mouse-sensitive, it must be SELECTABLE and not DISABLED. If
Xthe found object meets these tests, the mouse will "hover" over
Xthe object, waiting to leave its screen rectangle. Since the
Xobject might already be SELECTED (and hence drawn reversed), this
Xis checked before objc_toggle is called to do the highlighting and
Xselection of the object, which becomes the hot_obj. (If the
Xobject was already SELECTED, the hot_obj becomes NIL.)
X.PP
XThe toughest condition is when the mouse is within the
Xdialog, but not over a mouse-sensitive object. The regular GEM
Xevent structure will not work, because it can only wait on two
Xrectangles, and there may be many more selectable objects in a
Xdialog tree. I have found a way around this limitation using a
Xcombination of the map_tree utility (introduced in ST PRO GEM #5)
Xwith the principle of visual hierarchy in object trees.
X.PP
XIn summary, the algorithm attempts to find the largest
Xbounding rectangle around the current mouse position, within which
Xthere are no mouse-sensitive objects. It starts with a rectangle
Xequal to the dialog root, and successively "breaks" it with the
Xrectangle of each mouse-sensitive object. The next few paragraphs
Xexamine this method in detail.
X.PP
XSince C lacks the dynamic scoping of LISP, from which
Xmap_tree was derived, it is necessary to set up some globals to be
Xused during the rectangle break process. Br_rect is the GRECT of
Xthe current bounding rectangle. Br_mx and br_my hold the current
Xmouse position. Br_togl is a switch which determines whether the
Xnext break will be attempted horizontally or vertically. After
Xinitializing these variables, form_hot uses map_tree to invoke the
Xbreak_obj routine for every object in the dialog.
X.PP
XBreak_obj first intersects the rectangle of the object with
Xthe current bounding rectangle. If they are disjoint, then
Xneither the object nor any of its offspring can possible affect
Xthe operation, so FALSE is returned, causing map_tree to ignore
Xthe subtree.
X.PP
XThe object is next checked to see if it is mouse-sensitive.
XAs before, it must be SELECTABLE and not DISABLED, and it must not
Xbe hidden (this was checked automatically by objc_find before).
XIf these conditions are met, then the object intrudes into the
Xcurrent bounding rectangle. To maintain the desired condition,
Xpart of the rectangle must be removed or "broken away".
X.PP
XIn many cases, the break operation can be done either
Xhorizontally or vertically. Since we have no prior information as
Xto which way the mouse will move next, break_obj uses the br_togl
Xflag to alternate which direction it will try first. This should
Xyield the most nearly square rectangle.
X.PP
XThe break_x and break_y routines are very similar. In each
Xcase, the segment occupied by the breaking object is compared to
Xthe point occupied by the mouse. If the point is within the
Xsegment, there is no possible break in this dimension, and FALSE
Xis returned. If the point lies outside the segment, then the
Xrectangle may be successfully broken by reducing this dimension.
XThis is done, and TRUE is returned to report success.
X.PP
XThe break_y routine also employs a look-ahead test to prevent
Xa possible infinite loop. It is conceivable, though not likely,
Xthat someone might nest a non-SELECTABLE object completely within
Xanother SELECTABLE object(s). If the mouse point is within such
Xan object, the algorithm will not be able to select a break
Xdimension. In the current version, the mouse rectangle is simply
Xforced to a single pixel for this case. (Note that is is NOT
Xsufficent to simply wait on the non-selectable object's rectangle,
Xsince other SELECTABLE objects may overlap it and follow it in
Xtree order.)
X.PP
XSince map_tree examines all possible objects, br_rect will be
Xthe correct bounding rectangle at completion. Note that you can
Xreadily adapt this technique to other uses, such as hot-spotting
Xwhile dragging objects. It is much less demanding of CPU
Xresources than other methods, such as repetitive objc_finds.
X.SH WHAT A CHARACTER!
XThe form_keybd routine acts as a filter on
Xcharacter input. When it recognizes a control character, it
Xprocesses it and zeroes the keyboard word. Other chararacters are
Xpassed on to objc_edit to be inserted in edit_obj. If there is no
Xediting object, the character goes to the bit bucket.
X.PP
XThe form_keybd given implements the standard GEM
Xfunctionality with two minor additions. First, a carriage return
Xin a dialog with no DEFAULT exit object is taken as a tab. This
Xallows <CR> to be used "naturally" in dialogs with several lines
Xof text input. Second, tabs and backtabs "wrap around" from top
Xto bottom of the dialog, and are done by "walking the tree",
Xrather than relying on the LASTOB flag to signal the end of the
Xdialog. This allows the new form manager to handle dialog trees
Xwhich are not contiguous in memory.
X.PP
XThe code sets up several global variables for use by mapped
Xfunctions. Fn_obj is the output from both find_tab and find_def.
XFn_dir is an input to find_tab. Fn_last, fn_prev, and fn_last are
Xused while searching for tab characters.
X.PP
XA carriage return results in a search of the entire tree,
Xusing map_tree and find_def, for an object with its DEFAULT flag
Xset. Its SELECTED flag is set and it is inverted on the screen to
Xindicate the action taken. Form_keybd returns a FALSE to force
Xtermination of the main form_do loop. If no DEFAULT is found,
Xcontrol passes to the tab code.
X.PP
XThe tabbing procedure is somewhat complicated because the
Xsame code is used for forward and backward tabbing. The old value
Xof edit_obj (the object being tabbed FROM) is placed into fn_last.
XFn_dir is set to one for a forward tab, and zero for a backward
Xtab.
X.PP
XThe general strategy is to scan the entire tree for EDITABLE
Xobjects, always saving the last one found in fn_prev. When
Xtabbing forward fn_last is checked against fn_prev. A match
Xindicates that the current object is the one desired. When
Xtabbing backward the current object is checked against fn_last.
XIf they match, fn_prev is the desired object. This procedure
Xrequires two passes when the tab "wraps around" the tree, that is,
Xwhen the desired object as at the opposite end of the traverse
Xfrom the old editing object.
X.PP
XThe result of the tab operation is written back into
Xform_do's next_obj parameter, and becomes the new editing object
Xat the beginning of the next loop.
X.SH BUTTON DOWN
XThe form_button procedure is lengthy because it
Xmust recognize and handle mouse clicks on several types of
Xobjects: EDITABLE, SELECTABLE, and TOUCHEXIT. The first section
Xof code rejects other objects, which cannot accept a click.
X.PP
XThe next piece of form_button makes a special check for a
Xdouble click on a TOUCHEXIT object. This will cause the high bit
Xof the returned object number to be set. (By the way, this also
Xoccurs in the standard form_do.) This flag allows user dialog
Xcode to perform special processing on the object.
X.PP
XThe largest piece of form_button handles the various cases in
Xwhich the SELECTABLE flag may be set. Setting the RBUTTON (radio
Xbutton) flag causes all of the object's siblings in the tree to be
Xdeselected at the time the object is clicked. The do_radio
Xroutine uses the get_parent utility to find the ancestor, and then
Xperforms the deselect/select operation.
X.PP
XIf the SELECTABLE object is not TOUCHEXIT, then graf_watchbox
Xis used to make sure that the mouse button comes back up while it
Xis within the object. Otherwise, the click is cancelled. Care is
Xnecessary here, since the hot-spot code may have already set the
XSELECTED flag for the object. (We cannot be sure of this, for a
Xrace condition may have occurred!)
X.PP
XIf the SELECTABLE object is TOUCHEXIT, then the application
Xhas requested that form_do exit without waiting for the button to
Xgo back up. In both this and regular form_do, TOUCHEXIT objects
Xare used when you want to provide immediate response (animation)
Xwithin the context of a dialog.
X.PP
XThe final parts of form_button do cleanup. If the clicked
Xobject was already hot-spotted, hot_obj must be reset to NIL,
Xotherwise form_do will carefully unselect the object which has
Xjust been selected!
X.PP
XIf the EXIT or TOUCHEXIT flags are in force, form_button
Xreturns FALSE to force the completion of form_do. For EDITABLE
Xobjects, next_obj is left intact to replace edit_obj during the
Xnext loop. Otherwise, next_obj has done its job and is zeroed,
Xand form_button returns TRUE for continuation.
X.PP
XThis concludes the tour of the alternate form_do. The best
Xcure for any confusion in this explanation is to compile the code
Xinto an application and watch how it runs with different
Xresources, or attack it with a debugger.
X.SH OPERATORS ARE STANDING BY
XI encourage you to modify this
Xcode to meet your particular needs and incorporate it into your
Xapplication. I would like to request than anyone who comes up
Xwith significant improvements (or bug fixes) send them to me so
Xthey can be made generally available. You can do this via the
XANTIC ONLINE Feedback, or by sending E-mail to 76703,202.
X.PP
XSpeaking of Feedback, I would also like comments on the
Xproposed change of direction for the column, and more suggestions
Xfor future topics. The next installment will be a further
Xdiscussion of interface design. Topics now queued for future
Xarticles include the file selector and DOS error handling, a new
Xobject editor, and customized drag box and rubber box routines.
XDiscussions on VDI workstations and printer output are on hold
Xpending release of the GDOS by Atari. If there are items which
Xyou want to appear here, you must let me know!
X.!
X.!
X.!*****************************************************************************
X.!* *
X.!* End Part XIII *
X.!* *
X.!*****************************************************************************
SHAR_EOF
if test 18936 -ne "`wc -c 'wind13.prf'`"
then
echo shar: error transmitting "'wind13.prf'" '(should have been 18936 characters)'
fi
echo shar: extracting "'apndx10.prf'" '(14690 characters)'
if test -f 'apndx10.prf'
then
echo shar: over-writing existing file "'apndx10.prf'"
fi
sed 's/^X//' << \SHAR_EOF > 'apndx10.prf'
X.!****************************************************************************
X.!
X.! ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION.
X.!
X.! ** Professional GEM ** by Tim Oren
X.!
X.! Proff File by ST enthusiasts at
X.! Case Western Reserve University
X.! Cleveland, Ohio
X.! uucp : decvax!cwruecmp!bammi
X.! csnet: bammi@case
X.! arpa : bammi%case@csnet-relay
X.! compuserve: 71515,155
X.!
X.!****************************************************************************
X.!
X.! Begin Appendix 10
X.!
X.!***************************************************************************
X.!
X.!
X.AP X Sample Code for Part XIII
X#include "portab.h" /* portable coding conv */
X#include "machine.h" /* machine depndnt conv */
X#include "obdefs.h" /* object definitions */
X#include "gembind.h" /* gem binding structs */
X#include "taddr.h"
X
X#define M1_ENTER 0x0000
X#define M1_EXIT 0x0001
X
X#define BS 0x0008
X#define TAB 0x0009
X#define CR 0x000D
X#define ESC 0x001B
X#define BTAB 0x0f00
X#define UP 0x4800
X#define DOWN 0x5000
X#define DEL 0x5300
X /* Global variables used by */
X /* 'mapped' functions */
XMLOCAL GRECT br_rect; /* Current break rectangle */
XMLOCAL WORD br_mx, br_my, br_togl; /* Break mouse posn & flag */
XMLOCAL WORD fn_obj; /* Found tabable object */
XMLOCAL WORD fn_last; /* Object tabbing from */
XMLOCAL WORD fn_prev; /* Last EDITABLE obj seen */
XMLOCAL WORD fn_dir; /* 1 = TAB, 0 = BACKTAB */
X
X/************* Utility routines for new forms manager ***************/
X
X VOID
Xobjc_toggle(tree, obj) /* Reverse the SELECT state */
X LONG tree; /* of an object, and redraw */
X WORD obj; /* it immediately. */
X {
X WORD state, newstate;
X GRECT root, ob_rect;
X
X objc_xywh(tree, ROOT, &root);
X state = LWGET(OB_STATE(obj));
X newstate = state ^ SELECTED;
X objc_change(tree, obj, 0, root.g_x, root.g_y,
X root.g_w, root.g_h, newstate, 1);
X }
X
X.bp
XVOID /* If the object is not already */
Xobjc_sel(tree, obj) /* SELECTED, make it so. */
X LONG tree;
X WORD obj;
X {
X if ( !(LWGET(OB_STATE(obj)) & SELECTED) )
X objc_toggle(tree, obj);
X }
X
X VOID /* If the object is SELECTED, */
Xobjc_dsel(tree, obj) /* deselect it. */
X LONG tree;
X WORD obj;
X {
X if (LWGET(OB_STATE(obj)) & SELECTED)
X objc_toggle(tree, obj);
X }
X
X VOID /* Return the object's GRECT */
Xobjc_xywh(tree, obj, p) /* through 'p' */
X LONG tree;
X WORD obj;
X GRECT *p;
X {
X objc_offset(tree, obj, &p->g_x, &p->g_y);
X p->g_w = LWGET(OB_WIDTH(obj));
X p->g_h = LWGET(OB_HEIGHT(obj));
X }
X.bp
X VOID /* Non-cursive traverse of an */
Xmap_tree(tree, this, last, routine) /* object tree. This routine */
X LONG tree; /* is described in PRO GEM #5. */
X WORD this, last;
X WORD (*routine)();
X {
X WORD tmp1;
X
X tmp1 = this; /* Initialize to impossible value: */
X /* TAIL won't point to self! */
X /* Look until final node, or off */
X /* the end of tree */
X while (this != last && this != NIL)
X /* Did we 'pop' into this node */
X /* for the second time? */
X if (LWGET(OB_TAIL(this)) != tmp1)
X {
X tmp1 = this; /* This is a new node */
X this = NIL;
X /* Apply operation, testing */
X /* for rejection of sub-tree */
X if ((*routine)(tree, tmp1))
X this = LWGET(OB_HEAD(tmp1));
X /* Subtree path not taken, */
X /* so traverse right */
X if (this == NIL)
X this = LWGET(OB_NEXT(tmp1));
X }
X else /* Revisiting parent: */
X /* No operation, move right */
X {
X tmp1 = this;
X this = LWGET(OB_NEXT(tmp1));
X }
X }
X.bp
X WORD /* Find the parent object of */
Xget_parent(tree, obj) /* by traversing right until */
X LONG tree; /* we find nodes whose NEXT */
X WORD obj; /* and TAIL links point to */
X { /* each other. */
X WORD pobj;
X
X if (obj == NIL)
X return (NIL);
X pobj = LWGET(OB_NEXT(obj));
X if (pobj != NIL)
X {
X while( LWGET(OB_TAIL(pobj)) != obj )
X {
X obj = pobj;
X pobj = LWGET(OB_NEXT(obj));
X }
X }
X return(pobj);
X }
X
X WORD
Xinside(x, y, pt) /* determine if x,y is in rectangle */
X WORD x, y;
X GRECT *pt;
X {
X if ( (x >= pt->g_x) && (y >= pt->g_y) &&
X (x < pt->g_x + pt->g_w) && (y < pt->g_y + pt->g_h) )
X return(TRUE);
X else
X return(FALSE);
X }
X
X WORD
Xrc_intersect(p1, p2) /* compute intersection of two GRECTs */
X GRECT *p1, *p2;
X {
X WORD tx, ty, tw, th;
X
X tw = min(p2->g_x + p2->g_w, p1->g_x + p1->g_w);
X th = min(p2->g_y + p2->g_h, p1->g_y + p1->g_h);
X tx = max(p2->g_x, p1->g_x);
X ty = max(p2->g_y, p1->g_y);
X p2->g_x = tx;
X p2->g_y = ty;
X p2->g_w = tw - tx;
X p2->g_h = th - ty;
X return( (tw > tx) && (th > ty) );
X }
X.bp
X VOID
Xrc_copy(psbox, pdbox) /* copy source to destination rectangle */
X GRECT *psbox;
X GRECT *pdbox;
X {
X pdbox->g_x = psbox->g_x;
X pdbox->g_y = psbox->g_y;
X pdbox->g_w = psbox->g_w;
X pdbox->g_h = psbox->g_h;
X }
X
X/************* "Hot-spot" manager and subroutines ***************/
X
X WORD
Xbreak_x(pxy)
X WORD *pxy;
X { /* Breaking object is right of */
X if (br_mx < pxy[0]) /* mouse. Reduce width of */
X { /* bounding rectangle. */
X br_rect.g_w = pxy[0] - br_rect.g_x;
X return (TRUE);
X }
X if (br_mx > pxy[2]) /* Object to left. Reduce width*/
X { /* and move rect. to right */
X br_rect.g_w += br_rect.g_x - pxy[2] - 1;
X br_rect.g_x = pxy[2] + 1;
X return (TRUE);
X }
X return (FALSE); /* Mouse within object segment. */
X } /* Break attempt fails. */
X.bp
X WORD
Xbreak_y(pxy)
X WORD *pxy;
X {
X if (br_my < pxy[1]) /* Object below mouse. Reduce */
X { /* height of bounding rect. */
X br_rect.g_h = pxy[1] - br_rect.g_y;
X return (TRUE);
X }
X if (br_my > pxy[3]) /* Object above mouse. Reduce */
X { /* height and shift downward. */
X br_rect.g_h += br_rect.g_y - pxy[3] - 1;
X br_rect.g_y = pxy[3] + 1;
X return (TRUE);
X }
X /* Emergency escape test! Protection vs. turkeys who nest */
X /* non-selectable objects inside of selectables. */
X if (br_mx >= pxy[0] && br_mx <= pxy[1])
X { /* Will X break fail? */
X br_rect.g_x = br_mx; /* If so, punt! */
X br_rect.g_y = br_my;
X br_rect.g_w = br_rect.g_h = 1;
X return (TRUE);
X }
X return (FALSE);
X }
X.bp
X WORD
Xbreak_obj(tree, obj) /* Called once per object to */
X LONG tree; /* check if the bounding rect. */
X WORD obj; /* needs to be modified. */
X {
X GRECT s;
X WORD flags, broken, pxy[4];
X
X objc_xywh(tree, obj, &s);
X grect_to_array(&s, pxy);
X if (!rc_intersect(&br_rect, &s))
X return (FALSE); /* Trivial rejection case */
X
X flags = LWGET(OB_FLAGS(obj)); /* Is this object a potential */
X if (flags & HIDETREE) /* hot-spot? */
X return (FALSE);
X if ( !(flags & SELECTABLE) )
X return (TRUE);
X if (LWGET(OB_STATE(obj)) & DISABLED)
X return (TRUE);
X
X for (broken = FALSE; !broken; ) /* This could take two passes */
X { /* if the first break fails. */
X if (br_togl)
X broken = break_x(pxy);
X else
X broken = break_y(pxy);
X br_togl = !br_togl;
X }
X return (TRUE);
X }
X.bp
X WORD /* Manages mouse rectangle events */
Xform_hot(tree, hot_obj, mx, my, rect, mode)
X LONG tree;
X WORD hot_obj, mx, my, *mode;
X GRECT *rect;
X {
X GRECT root;
X WORD state;
X
X objc_xywh(tree, ROOT, &root); /* If there is already a hot-spot */
X if (hot_obj != NIL) /* turn it off. */
X objc_toggle(tree, hot_obj);
X
X if (!(inside(mx, my, &root)) ) /* Mouse has moved outside of */
X { /* the dialog. Wait for return. */
X *mode = M1_ENTER;
X rc_copy(&root, rect);
X return (NIL);
X }
X /* What object is mouse over? */
X /* (Hit is guaranteed.) */
X hot_obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my);
X /* Is this object a hot-spot? */
X state = LWGET(OB_STATE(hot_obj));
X if (LWGET(OB_FLAGS(hot_obj)) & SELECTABLE)
X if ( !(state & DISABLED) )
X { /* Yes! Set up wait state. */
X *mode = M1_EXIT;
X objc_xywh(tree, hot_obj, rect);
X if (state & SELECTED) /* But only toggle if it's not */
X return (NIL); /* already SELECTED! */
X else
X {
X objc_toggle(tree, hot_obj);
X return (hot_obj);
X }
X }
X
X rc_copy(&root, &br_rect); /* No hot object, so compute */
X br_mx = mx; /* mouse bounding rectangle. */
X br_my = my;
X br_togl = 0;
X map_tree(tree, ROOT, NIL, break_obj);
X rc_copy(&br_rect, rect); /* Then return to wait state. */
X *mode = M1_EXIT;
X return (NIL);
X }
X.bp
X/************* Keyboard manager and subroutines ***************/
X
X WORD
Xfind_def(tree, obj) /* Check if the object is DEFAULT */
X LONG tree;
X WORD obj;
X { /* Is sub-tree hidden? */
X if (HIDETREE & LWGET(OB_FLAGS(obj)))
X return (FALSE);
X /* Must be DEFAULT and not DISABLED */
X if (DEFAULT & LWGET(OB_FLAGS(obj)))
X if ( !(DISABLED & LWGET(OB_STATE(obj))) )
X fn_obj = obj; /* Record object number */
X return (TRUE);
X }
X
X WORD
Xfind_tab(tree, obj) /* Look for target of TAB operation. */
X LONG tree;
X WORD obj;
X { /* Check for hiddens subtree. */
X if (HIDETREE & LWGET(OB_FLAGS(obj)))
X return (FALSE);
X /* If not EDITABLE, who cares? */
X if ( !(EDITABLE & LWGET(OB_FLAGS(obj))) )
X return (TRUE);
X /* Check for forward tab match */
X if (fn_dir && fn_prev == fn_last)
X fn_obj = obj;
X /* Check for backward tab match */
X if (!fn_dir && obj == fn_last)
X fn_obj = fn_prev;
X fn_prev = obj; /* Record object for next call. */
X return (TRUE);
X }
X
X WORD
Xform_keybd(tree, edit_obj, next_obj, kr, out_obj, okr)
X LONG tree;
X WORD edit_obj, next_obj, kr, *out_obj, *okr;
X {
X if (LLOBT(kr)) /* If lower byte valid, mask out */
X kr &= 0xff; /* extended code byte. */
X fn_dir = 0; /* Default tab direction if backward. */
X switch (kr) {
X case CR: /* Zap character. */
X *okr = 0;
X /* Look for a DEFAULT object. */
X fn_obj = NIL;
X map_tree(tree, ROOT, NIL, find_def);
X /* If found, SELECT and force exit. */
X if (fn_obj != NIL)
X {
X objc_sel(tree, fn_obj);
X *out_obj = fn_obj;
X return (FALSE);
X } /* Falls through to */
X case TAB: /* tab if no default */
X case DOWN:
X fn_dir = 1; /* Set fwd direction */
X case BTAB:
X case UP:
X *okr = 0; /* Zap character */
X fn_last = edit_obj;
X fn_prev = fn_obj = NIL; /* Look for TAB object */
X map_tree(tree, ROOT, NIL, find_tab);
X if (fn_obj == NIL) /* try to wrap around */
X map_tree(tree, ROOT, NIL, find_tab);
X if (fn_obj != NIL)
X *out_obj = fn_obj;
X break;
X default: /* Pass other chars */
X return (TRUE);
X }
X return (TRUE);
X }
X.bp
X/************* Mouse button manager and subroutines ***************/
X
X WORD
Xdo_radio(tree, obj)
X LONG tree;
X WORD obj;
X {
X GRECT root;
X WORD pobj, sobj, state;
X
X objc_xywh(tree, ROOT, &root);
X pobj = get_parent(tree, obj); /* Get the object's parent */
X
X for (sobj = LWGET(OB_HEAD(pobj)); sobj != pobj;
X sobj = LWGET(OB_NEXT(sobj)) )
X { /* Deselect all but... */
X if (sobj != obj)
X objc_dsel(tree, sobj);
X }
X objc_sel(tree, obj); /* the one being SELECTED */
X }
X
X WORD /* Mouse button handler */
Xform_button(tree, obj, clicks, next_obj, hot_obj)
X LONG tree;
X WORD obj, clicks, *next_obj, *hot_obj;
X {
X WORD flags, state, hibit, texit, sble, dsbld, edit;
X WORD in_out, in_state;
X
X flags = LWGET(OB_FLAGS(obj)); /* Get flags and states */
X state = LWGET(OB_STATE(obj));
X texit = flags & TOUCHEXIT;
X sble = flags & SELECTABLE;
X dsbld = state & DISABLED;
X edit = flags & EDITABLE;
X
X if (!texit && (!sble || dsbld) && !edit) /* This is not an */
X { /* interesting object */
X *next_obj = 0;
X return (TRUE);
X }
X
X if (texit && clicks == 2) /* Preset special flag */
X hibit = 0x8000;
X else
X hibit = 0x0;
X
X if (sble && !dsbld) /* Hot stuff! */
X {
X if (flags & RBUTTON) /* Process radio buttons*/
X do_radio(tree, obj); /* immediately! */
X else if (!texit)
X {
X in_state = (obj == *hot_obj)? /* Already toggled ? */
X state: state ^ SELECTED;
X if (!graf_watchbox(tree, obj, in_state,
X in_state ^ SELECTED))
X { /* He gave up... */
X *next_obj = 0;
X *hot_obj = NIL;
X return (TRUE);
X }
X }
X else /* if (texit) */
X if (obj != *hot_obj) /* Force SELECTED */
X objc_toggle(tree, obj);
X }
X
X if (obj == *hot_obj) /* We're gonna do it! So don't */
X *hot_obj = NIL; /* turn it off later. */
X
X if (texit || (flags & EXIT) ) /* Exit conditions. */
X {
X *next_obj = obj | hibit;
X return (FALSE); /* Time to leave! */
X }
X else if (!edit) /* Clear object unless tabbing */
X *next_obj = 0;
X
X return (TRUE);
X }
X.bp
X/************* New forms manager: Entry point and main loop *************/
X
X WORD
Xform_do(tree, start_fld)
X REG LONG tree;
X WORD *start_fld;
X {
X REG WORD edit_obj;
X WORD next_obj, hot_obj, hot_mode;
X WORD which, cont;
X WORD idx;
X WORD mx, my, mb, ks, kr, br;
X GRECT hot_rect;
X WORD (*valid)();
X /* Init. editing */
X next_obj = *start_fld;
X edit_obj = 0;
X /* Initial hotspot cndx */
X hot_obj = NIL; hot_mode = M1_ENTER;
X objc_xywh(tree, ROOT, &hot_rect);
X /* Main event loop */
X cont = TRUE;
X while (cont)
X {
X /* position cursor on */
X /* the selected */
X /* editting field */
X if (edit_obj != next_obj)
X if (next_obj != 0)
X {
X edit_obj = next_obj;
X next_obj = 0;
X objc_edit(tree, edit_obj, 0, &idx, EDINIT);
X }
X /* wait for button or */
X /* key or rectangle */
X which = evnt_multi(MU_KEYBD | MU_BUTTON | MU_M1,
X 0x02, 0x01, 0x01,
X hot_mode, hot_rect.g_x, hot_rect.g_y,
X hot_rect.g_w, hot_rect.g_h,
X 0, 0, 0, 0, 0,
X 0x0L,
X 0, 0,
X &mx, &my, &mb, &ks, &kr, &br);
X
X if (which & MU_M1) /* handle rect. event */
X hot_obj = form_hot(tree, hot_obj, mx, my, &hot_rect, &hot_mode);
X /* handle keyboard event*/
X if (which & MU_KEYBD)
X { /* Control char filter */
X cont = form_keybd(tree, edit_obj, next_obj, kr, &next_obj, &kr);
X if (kr && edit_obj) /* Add others to object */
X objc_edit(tree, edit_obj, kr, &idx, EDCHAR);
X }
X /* handle button event */
X if (which & MU_BUTTON)
X { /* Which object hit? */
X next_obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my);
X if (next_obj == NIL)
X next_obj = 0;
X else /* Process a click */
X cont = form_button(tree, next_obj, br,
X &next_obj, &hot_obj);
X }
X /* handle end of field */
X /* clean up */
X if (!cont || (next_obj != edit_obj && next_obj != 0))
X if (edit_obj != 0)
X objc_edit(tree, edit_obj, 0, &idx, EDEND);
X }
X /* If defaulted, may */
X /* need to clear hotspot*/
X if (hot_obj != (next_obj & 0x7fff))
X if (hot_obj != NIL)
X objc_toggle(tree, hot_obj);
X /* return exit object */
X /* hi bit may be set */
X /* if exit obj. was */
X /* double-clicked */
X *start_fld = edit_obj;
X return(next_obj);
X }
X.!
X.!****************************************************************************
X.!
X.! End Appendix 10
X.!
X.!****************************************************************************
SHAR_EOF
if test 14690 -ne "`wc -c 'apndx10.prf'`"
then
echo shar: error transmitting "'apndx10.prf'" '(should have been 14690 characters)'
fi
# End of shell archive
exit 0
--
Jwahar R. Bammi
Usenet: .....!decvax!cwruecmp!bammi
CSnet: bammi@case
Arpa: bammi%case@csnet-relay
CompuServe: 71515,155
Technical Details
Message-ID:
<1512@cwruecmp.UUCP>Path:
arc1-ca.pugleaf.net!utzoo!watmath!clyde!caip!topaz!ll-xn!nike!ucbcad!ucbvax!decvax!cwruecmp!bammiShow Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: arc1-ca.pugleaf.net!utzoo!watmath!clyde!caip!topaz!ll-xn!nike!ucbcad!ucbvax!decvax!cwruecmp!bammi From: bammi@cwruecmp.UUCP (Jwahar R. Bammi) Newsgroups: net.micro.atari16 Subject: ProGem Parts 13-14 (1 of 2) Message-ID: <1512@cwruecmp.UUCP> Date: Fri, 27-Jun-86 02:58:02 EDT Article-I.D.: cwruecmp.1512 Posted: Fri Jun 27 02:58:02 1986 Date-Received: Sat, 28-Jun-86 08:49:02 EDT Organization: CWRU Dept. Computer Eng., Cleveland, OH Lines: 971