[net.micro.atari16] ProGem parts 13-14

bammi@cwruecmp.UUCP (Jwahar R. Bammi) (06/27/86)

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

bammi@cwruecmp.UUCP (Jwahar R. Bammi) (06/27/86)

#!/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:
#	wind14.prf
#	tut.prf
#	macros.prf
# This archive created: Fri Jun 27 02:53:58 1986
# By:	Jwahar R. Bammi ()
export PATH; PATH=/bin:$PATH
echo shar: extracting "'wind14.prf'" '(22068 characters)'
if test -f 'wind14.prf'
then
	echo shar: over-writing existing file "'wind14.prf'"
fi
sed 's/^X//' << \SHAR_EOF > 'wind14.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 XIV
X.!
X.!****************************************************************************
X.!
X.PART XIV USER INTERFACES - Part 2
X.PP
XThis  issue of ST PRO GEM (#14) continues the  discussion  of
Xuser  interface  design which began in episode eight.   It  begins
Xwhere  we left off,  with a further treatment of the mode problem,
Xand  proceeds  into  topics such as visual  grammar  and  layered
Xinterfaces.
X.PP
X     Note  that  there  is  no  download  for  this  column.   The
Xdownloads  will return with the next issue,  a discussion of using
Xthe  GEM DOS file system within a GEM application.   Specifically,
Xit  will include sample code for using the file selector,  the GEM
Xform_error  alerts,  and some utilities for manipulating file  and
Xpath names.  There will also be a feedback section.  The following
Xtwo columns will be devoted to "graphics potpourri",  a collection
Xof  small  but useful GEM utilities such as  popup  menus,  string
Xediting, and source code for drag and rubber box operations.
X.SH MODES AGAIN
XIf a program is modeless, it acts predictably,
Xwhich turns out to be very important.   On the other hand,  a good
Xdefinition  for  "modes"  is hard to find.   In  column  eight,  I
Xsuggested  that  a  mode exists when you cannot  use  all  of  the
Xcapabilities  of the program without performing some  intermediate
Xstep.   If  this  is  less  than clear,  here  are  two  alternate
Xdefinitions offering different views of the problem.
X.SH THE "TWO USER TEST"
XConsider  the  following  thought
Xexperiment:   Imagine  that  your ST (and GEM) had two  mice,  two
Xcursors,  and  two  users.   Could  they both effectively use  the
Xprogram at the same time?  If so, the application is modeless.  If
Xthere are points where one user can be "locked out" by the actions
Xof  the other,  then a mode exists at that point.  Let's  consider
Xsome examples of this test.
X.PP
XIn any program which uses the GEM menu system, one user could
Xstop  the  other by touching a menu hotspot and dropping  a  menu.
XThis constitutes an inherent mode in the GEM architecture.
X.PP
XOn  the  GEM Desktop,  two users could open windows and  view
Xfiles without interference.   However, as soon as one person tries
Xto delete a file (assuming the verify option is on),  the other is
Xbrought  to  a halt as a dialog appears.   Thus,  we have found  a
Xmodal dialog.
X.PP
XIn  many "Paint-type" programs,  such as MacPaint,  PC Paint,
Xand  GEM Paint,  two artists could co-exist quite well,  utilizing
Xthe  on-screen  palette  and tool  selection.   Of  course,  these
Xprograms  also contain modal dialogs for such operations  as  file
Xand  brush  shape  selection.   In contrast,  consider  the  paint
Xprogram  DEGAS  for  the ST.   Here,  two artists could only  work
Xtogether as long as neither wanted to change tool or color.   Then
Xthe  display  would have to be flipped to  the  selection  screen,
Xstopping the other user.  This is a mode in the DEGAS interface.
X.PP
X(By the way, this test is not just academic.  The grand-daddy
Xof all mouse based systems, NLS, demonstrated by Doug Englebart in
X1968,  had  two  mice  and two users,  one of whom was  physically
Xremote.   Cooperative  techniques  such as this are still  largely
Xunexplored and unexploited.)
X.SH ONE LINER
XHere's  a  terse definition by  Jef  Raskin:  A
Xprogram is modeless if a given action has one and only one result.
XAgain, let's run a few examples.
X.PP
XThe  menu  dropdowns are clearly modal  by  this  definition.
XBefore  the  menu was activated,  window control points  could  be
Xactivated with a click.   However, when the dropdown is visible, a
Xclick action is interpreted as a menu selection or a dismissal  of
Xthe dropdown.   Similarly, dialogs are modal because the action of
Xmoving  the mouse into the menu bar no longer causes the  dropdown
Xto appear.
X.PP
XI am typing this using the First Word editor program.  It has
Xa  nice desktop level box full of characters where I can click  to
Xget  symbols which the ST keyboard won't produce.   However,  if I
Xinvoke  the  find or replace string dialog,  the  click-in-the-box
Xaction  doesn't  work anymore.   This is a mode in the First  Word
Xinterface.
X.PP
XFinally, consider an "old style" menu program, the kind where
Xyou  type in the number of the desired action from a list.   Since
Xthe  number  "2" might mean "Insert the record" in one  menu,  and
X"Purge  the file" in another,  such a program is clearly modal  by
XRaskin's definition.
X.PP
XThese  three definitions say almost the same thing,  but from
Xdifferent  viewpoints.   Depending  on the situation,  one or  the
Xother  may  be more intuitive for you.   The goal of this type  of
Xanalysis  is to root out unnecessary modes,  and to make sure that
Xthose  which remain only appear when requested by the user,  offer
Xsome visual cue such as a rubber line or standard dialog box,  and
Xare used consistently throughout the application.
X.SH PREDICTABILITY FOREVER AND EVER AND EVER
XAs  Raskin's
Xdefinition  makes  clear,  when the modes go away,  the  interface
Xbecomes  predictable.   Predictability  leads to the formation  of
Xhabits   of   use.     Habits  reduce  "think  time"  and   become
Xprogressively faster due to the Power Law of Practice discussed in
Xcolumn eight.  This is exactly what we want!
X.PP
XThere is another benefit of predictability.   A habit learned
Xin  one  part  of a program with a  consistent  interface  can  be
Xtransferred  and  used elsewhere in the application.   If  several
Xprograms share the same style of interface, the same habits can be
Xused across a complete set of products.  Learning time for the new
Xfunctions becomes shorter,  and the user is more likely to use the
Xnew feature.
X.SH IS A BOGEYMAN!
XMost  casual  users are  scared  silly  of
Xcomputers  and programs.   (If you have any doubt,  eavesdrop on a
Xsecretary with a new word processor,  or the doctor's receptionist
Xcoping with an insurance data entry program.)  In most cases, they
Xhave  a  right to be frightened.   Even  experienced  programmers,
Xprone  to  toss  the manuals and hack  away,  know  that  moderate
Xparanoia  is  the best way to deal with an unknown  program.   How
Xmust this feel to someone whose ability to perform (or lose) their
Xjob depends on an unpredictable (aha!) black box.
X.PP
XSo here's another way in which predictability works.   But to
Xproduce  a truly fearless user,  we need other qualities as  well.
XOne  is robustness,  meaning that the program will not crash given
Xnormal or even bizarre actions by the user.   Another is feedback,
Xwhich shuts off invalid options,  reinforces correct actions,  and
Xgives  reassurance  that  an  operation  is  proceeding  normally.
XFinally, we need forgiveness, in the form of inverse operations or
XUndo options, when the inevitable mistake is made.
X.PP
XThe  ultimate  goal is make the program  discoverable.   This
Xmeans  the user should be able to safely "wing it" after  a  short
Xsession  with  the application and its interface.   This  practice
Xought to be considered the norm anyway, since the manual is always
Xacross  the office or missing when an esoteric and  half-forgotten
Xfeature  is  needed.   If it is possible to muddle through such  a
Xsituation  by  trial  and  error,   without  causing  damage,  the
Ximmediate  problem  will  be  solved,   and  the  user  will  gain
Xconfidence.
X.SH GOOD GRAMMAR OR...
XSo exactly what are these habits that are
Xsupposed  to be so helpful?   One of the most useful patterns is a
Xconsistent  command  grammar  for the  program.   This  may  sound
Xstrange,   since   we  have  supposedly  abandoned  command   line
Xinterfaces  in the graphics world,  but in fact,  the same type of
Xrules apply.   For instance, in the world of A> we might issue the
Xcommand:
X.br
X.sp
X.ce
Xcopy a:foobar.txt b:
X.PP
XBy  analogy  to  Englist grammar,  this command contains  a  verb,
X"copy",  a  file  as  subject:  "a:foobar.txt",  and a location as
Xan object: "b:".  The equivalent GEM Desktop operation is:
X.BO
X- Move mouse to foobar.txt icon in a: window
X.EO
X.BO
X- Press mouse button
X.EO
X.BO
X- Move mouse to b: icon
X.EO
X.BO
X- Release mouse button
X.EO
X.PP
XThe  operation  can be described as a  select-drag-drop  sequence,
Xwith  the select designating the subject file,  the drag  denoting
Xthe  operation  (copy),  and the location of the drop showing  the
Xobject.   A  grammar still exists,  but its "terminal symbols" are
Xcomposed  of  mouse  actions interpreted in  the  context  of  the
Xcurrent screen display, rather than typed characters.
X.PP
XOne  useful way to analyze simple grammars,  including  those
Xused  as  command  languages,  is to separate  them  into  prefix,
Xpostfix,  and infix forms.   In a prefix grammer, the operation to
Xbe  performed precedes its operands,  that is,  its subject(s) and
Xobject(s).   The  DOS copy command given above is an example of  a
Xprefix  command.   LISP  is  an example of a language  which  uses
Xprefix specification for its commands.
X.PP
XPostfix grammars specify the action after all of the operands
Xhave been given.   This command pattern is familiar to many as the
Xway  in  which  Hewlett-Packard calculators  work.   FORTH  is  an
Xexample of a language which uses a postfix grammar.
X.PP
XInfix  notation  places the verb,  or operator,  between  its
Xsubject and object.   Conventional algebraic notation is infix, as
Xare most computer languages such as C or PASCAL.   The example GEM
Xcommand  given  above  is also infix,  since the  selection  of  a
Xsubject  file  preceded  the action,  which was  followed  by  the
Xdesignation of an object.
X.PP
XThe  "standard" GEM command grammar,  as used in the products
Xproduced  by Digital Research,  is in fact infix.   This is not to
Xsay that GEM enforces such a convention,  or that it is rigorously
Xfollowed.  However, when there is no pressing reason for a change,
Xadoption  of an infix command grammar will make  your  application
Xfeel most like others which users may have seen.
X.PP
XThe general problem of specifying a graphic command  language
Xcan be difficult, but much of the problem has already been handled
Xon the ST.   Part of the solution is by constraint:  the input and
Xoutput hardware of the ST are predefined,  so most developers will
Xnot  need  to  worry about choosing a pointing  device  or  screen
Xresolution.   The  other part of the standard solution is the  GEM
Xconvention for mouse usage.  I am going to review these rules, and
Xthen describe of the situations in which they have been bent,  and
Xfinally  some alternate approaches which may prove useful to  some
Xdevelopers.
X.SH SPECIFYING A SUBJECT
XThere are really two sets of methods
Xfor  designating what is to be affected by an operation.   One set
Xis  used when distinct objects are to be affected.   Examples  are
Xfile and disk icons in the Desktop and trees in the RCS.   Another
Xset of designation methods is used when continuous material,  such
Xas text or bit images, is being handled.
X.PP
XWhen dealing with objects, a single mouse click (down and up)
Xover the object selects it.   The application should show that the
Xselection  has occurred by changing the appearance of the  object.
XThe  most  common  methods are inverting the  object,  or  drawing
X"handles" around it.
X.PP
XMany   operations   allow  "plural",   or  multiple   object,
Xselections.  The GEM convention is that a click on an object while
Xthe  shift key is held down extends the selection by  adding  that
Xobject.   If the shift-clicked object was already selected,  it is
Xdeleted from the selection list.
X.PP
XAnother  way to select multiple objects is to use  a  "rubber
Xbox"  to enclose them.   This operation begins with drag on a part
Xof  the  view where no object is present.   The  application  then
Xanimates a rubber box on the screen as long as the mouse button is
Xheld  down.   When the button is released,  all objects within the
Xcurrent extent of the box are selected.   A shift-drag combination
Xcould be used to add the objects to an existing selection list.
X.PP
XSelecting  part of a text or bit plane display is  also  done
Xwith a rubber box.   Since there are no "objects" in the view, any
Xmouse  drag  is  interpreted  as  the  beginning  of  a  selection
Xoperation.   In  the  simplest  case,  a bit plane,  the rectangle
Xwithin the box when the button is released is the selected extent.
X.PP
XWhen  the  underlying data has structure,  such as words  and
Xlines  of  text,  the display should reflect this fact during  the
Xselection  operation.   Typically,  text selection is indicated by
Xinversion  of  the  characters  rather than  a  rubber  box.   The
Xselection  extends  along the starting line so long as  the  mouse
Xstays  within the line.   If the mouse move off the starting  text
Xline, the implied selection is all characters between the starting
Xcharacter  and the character currently under the mouse,  which  is
Xnot necessarily a rectangular area.
X.PP
XAn  extended  "plural"  selection may be  supported  in  text
Xediting.   The  use of the shift key is also conventional in  this
Xapplication.
X.SH ACTION
XWith the subject designated, the user can now choose
Xan operation.   In many cases,  this will be picked from the menu,
Xin  which  case  the  entire  command  is  complete.    Some  menu
Xselections will lead to dialogs,  in which the interaction methods
Xare  regulated  by  the GEM form manager.   When  the  command  is
Xcompleted,  it  is  often  helpful if the application  leaves  the
Xobjects  (or areas) selected and ready for another  operation.   A
Xsingle click away from any object is interpreted as cancelling the
Xselections.
X.PP
XMany  operations  are indicated by gestures  on  the  screen.
XUsually,   this  is  some  variant  of  a  drag  operation.    The
Xinterpretation of the gesture may depend on the type and  location
Xof the selected subject,  which part of it is under the mouse, and
Xin what location the drag terminates.
X.PP
X"Handles"  are small boxes or dot displayed around an  object
Xwhen it is selected.   A drag beginning with the mouse on a handle
Xis  usually  interpreted  as  a resizing  operation,  if  this  is
Xappropriate.   The  pointing  finger  mouse form is  displayed  to
Xindicate  the operation in progress,  and a rubber version of  the
Xobject  is animated on the screen to show the user the  result  if
Xthe  button  were released.   In some cases,  where an  underlying
X"snap"  grid  exists,  the  animated  object may  change  size  in
Xdiscrete steps.
X.PP
XDragging  a non-handle area of a selected object  is  usually
Xinterpreted  as  the  beginning  of  a  move  function.   In  most
Xapplications,  a  move  of a single object may be started  without
Xpre-selection.   Simply  beginning the drag on the object is taken
Xto imply selection.  The spread hand, or "grabber", mouse form  is
Xtypically displayed during a drag operation.
X.PP
XDragging  may  denote copying or movement,  or  more  complex
Xfunctions such as instantiation or generalization.   The operation
Ximplied by movement on the screen will differ among  applications,
Xand  often  within  the  same  application,  depending  on  target
Xlocation.   This  target is the recipient of the command's action,
Xor its object, in an English grammar sense.
X.PP
XFor  example,  a  drag from window to window in  the  Desktop
Xdenotes a copy.   On the other hand, dragging the same icon to the
Xtrashcan  deletes it completely.   Dragging an object from the RCS
Xpartbox  to the editing view creates a new copy of that  prototype
Xobject.   Dragging  the  same object within the edit  view  simply
Xchanges its placement.
X.PP
XThere   are  some  mouse  actions  which   are   conventional
X"abbreviations".   A  double click on an object is interpreted  as
Xboth a selection and an action.   Usually, the double click action
Xis the same as the Open entry in the "File" menu.
X.PP
XWhen  the  usual interpretation of a drag is  movement,  then
Xshift-drag  may be used as an enhanced varient  implying  copying.
XFor  instance,  shift-dragging  an object within the  RCS  editing
Xwindow  makes  a  copy of the object and places it  in  the  final
Xlocation.
X.PP
XTo return to the beginning of this discussion, the reason for
Xadopting  these conventional usages is to build an interface  that
Xpromotes  habits.   Particularly,  a  standard grammar for  giving
Xcommands helps answer the question "What comes next?".   It breaks
Xthe user's actions into logical phrases,  or chunks,  which may be
Xthought of a whole, rather than one action at a time.
X.SH DIFFERENT FOLKS, DIFFERENT STROKES
X.PP
XThere  are  always
Xexceptions to a rule,  or so it seems.   In this case, consistency
Xof   the  interface  grammar  is  sometimes  traded  off   against
Xconsistency of metaphor,  preservation of screen space,  and "fast
Xpath" methods for experts.
X.PP
XOne example is the use of "tools" in Paint and Draw programs.
XIn  such  programs,  an  initial  click is made on  a  tool  icon,
Xdenoting the operation to be applied to all following  selections.
XThis is an prefix style of grammar,  and stands in contrast to the
Xusual  method  of selecting subject object(s) first.   Because  of
Xthis contrast,  it is sometimes called "moding the cursor".   (Try
Xapplying the tests above to be sure it really is a mode.)
X.PP
XIn  these  cases,  there  are two reasons for  accepting  the
Xnonstandard  method.   The first is consistency of metaphor.   The
X"user model" portrayed in the programs is an artist's work  table,
Xwith  tools,  palette,  and  so  on.   The cursor moding action is
Xequivalent  to  picking up a working tool.   The second reason  is
Xspeed.   In a Paint program,  the "canvas" is often modified,  and
Xspeed  in  creating or changing the bits is  important.   In  more
Xobject  oriented applications such as Desktop or RCS,  the objects
Xare more persistent.   Speed is then more essential when adding or
Xchanging properties of the objects.
X.PP
XWhen  command  styles  are mixed in this  fashion,  you  must
Xdesign very carefully to avoid conflicts or apparent  side-effects
Xin  the  command language.   For example,  in GEM Draw picking  an
Xaction from the Edit menu cancels the current cursor mode  without
Xwarning.   Confusion  from  such side-effects may cancel  out  the
Xbenefits of the mixed grammar.
X.PP
XThe  subject  of command speed  deserves  further  attention.
XWhile  the  novice approaching a program needs  full  feedback,  a
Xperson who uses it day in and day out will learn the program,  and
Xwant  faster  ways  to get the job done,  even if  they  are  more
Xarcane.  The gives rise to a "layered" style of interface.
X.PP
XA layered interface is designed so that the visual grammar is
Xobvious,  as  we  have discussed.   However, there are one or more
Xsets of "accelerators" built into the program, which may be harder
Xto find but faster to use.  One example is condensed mouse actions
Xsuch  as the double-click.   For instance,  attempting to select a
Xblock  of text which extends beyond a window is  impossible  using
Xthe  basic metaphor.   The novice will simply do the operation  in
Xpieces.   A  layered interface might put a less obvious Mark Begin
Xand  Mark End option in the menus.   Another way is to take a drag
Xwhich  extends outside the window as a request to begin  scrolling
Xin that direction, while extending the current selection.
X.PP
XOne  of  the most common and useful  accelerator  methods  is
Xfunction  keys.   Using  this approach,  single key equivalents to
Xactions are listed in the menu.   Striking this key when an object
Xis  selected  will cause the action to occur.   Note that this  is
Xmost  useful if some keyboard driven method of  object  selection,
Xsuch as tabbing, is also available.  Otherwise, the time switching
Xfrom  the  mouse,  used to select the object,  to the keyboard for
Xcommand input, may well cancel any advantage.
X.PP
XFinally,  radical  departures  from the GEM metaphor  may  be
Xuseful when attempting to replicate the look of another system, or
Xtrying  to  meet severe constraints,  such as display space.   One
Xexample  would  be discarding the standard GEM menus in  favor  of
X"popup"  menus which appear next to the current mouse position  in
Xresponse  to  a click on the second button.   This method has  the
Xadvantage  of preserving the menu space at the top of the  screen,
Xand  is potentially faster because the menu appears right next  to
Xthe  current mouse position.   The drawbacks are lack of a  visual
Xcue for naive users trying to find the commands,  and the need for
Xcustom coding to build the popups.
X.SH MORE TO COME
XWe have reached the end of the second sermon
Xon  user  interface.   In a future column,  I will look at "higher
Xlevel"  topics  relating to the design of the  application's  user
Xmetaphor.   These  include  issues of object  orientation,  direct
Xmanipulation,   and  the  construction  of  microworlds.   In  the
Xmeantime,  several  of  the  more practical columns  will  present
Ximplementions  of  techniques such as accelarator keys  and  popup
Xmenus which I have discussed this time.
X.PP
XTHANKS AND APOLOGIES to the following people whose public and
Xpublished   remarks  have  formed  part  of  the  basis  of   this
Xdiscussion:  Jef Raskin, Bill Buxton, Adele Goldberg, James Foley,
Xand Ben Schneidermann.  As always, any errors are my own.
X.!
X.!
X.!*****************************************************************************
X.!*									      *
X.!*				End Part XIV				      *
X.!*									      *
X.!*****************************************************************************
SHAR_EOF
if test 22068 -ne "`wc -c 'wind14.prf'`"
then
	echo shar: error transmitting "'wind14.prf'" '(should have been 22068 characters)'
fi
echo shar: extracting "'tut.prf'" '(1236 characters)'
if test -f 'tut.prf'
then
	echo shar: over-writing existing file "'tut.prf'"
fi
sed 's/^X//' << \SHAR_EOF > 'tut.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 Tutorial
X.!
X.!***************************************************************************
X.!
X.!
X.so macros.prf
X.so wind1.prf
X.so wind2.prf
X.so wind3.prf
X.so wind4.prf
X.so wind5.prf
X.so wind6.prf
X.so wind7.prf
X.so wind8.prf
X.so wind10.prf
X.so wind11.prf
X.so wind12.prf
X.so wind13.prf
X.so wind14.prf
X.!
X.! Add any further Parts Above
X.!
X.cl 0 APPENDICES
X.so apndx1.prf
X.so apndx2.prf
X.so apndx3.prf
X.so apndx4.prf
X.so apndx5.prf
X.so apndx6.prf
X.so apndx7.prf
X.so apndx8.prf
X.so apndx9.prf
X.so apndx10.prf
X.!
X.! Add any further Appendicies Above
X.!
X.so toc.prf
X.!
X.!
X.!****************************************************************************
X.!
X.!			End Tutorial
X.!
X.!***************************************************************************
SHAR_EOF
if test 1236 -ne "`wc -c 'tut.prf'`"
then
	echo shar: error transmitting "'tut.prf'" '(should have been 1236 characters)'
fi
echo shar: extracting "'macros.prf'" '(1170 characters)'
if test -f 'macros.prf'
then
	echo shar: over-writing existing file "'macros.prf'"
fi
sed 's/^X//' << \SHAR_EOF > 'macros.prf'
X.!****************************************************************************
X.!
X.!				Macros
X.!
X.!****************************************************************************
X.!
X.! Begin New Part
X.!
X.define PART
X.he /Professional GEM//#/
X.bp
X.bold on
X.ce 1
XPART - $1
X.br
X.sp 1
X.ce 1
X$2 $3 $4 $5 $6 $7 $8 $9
X.bold off
X.cl 0 PART - $1.  $2 $3 $4 $5 $6 $7 $8 $9
X.he /Professional GEM/Part $1/#/
X.sp 1
X.en
X.!
X.! Section Heading
X.!
X.define SH
X.cl 1 $1 $2 $3 $4 $5 $6 $7 $8 $9
X.ne 5
X.sp 2
X.bold 1
X$1 $2 $3 $4 $5 $6 $7 $8 $9
X.br
X.sp 1
X.ti +5
X.en
X.!
X.! Figure Begin
X.!
X.define FB
X.ne 3
X.br
X.cl 2 $1
X.sp 1
X.ce on
X.en
X.!
X.! Figure End
X.!
X.define FE
X.br
X.ce off
X.sp 1
X.en
X.!
X.! Paragraph
X.!
X.define PP
X.ne 3
X.sp 1
X.ti +5
X.en
X.!
X.! Begin Option
X.!
X.define BO
X.sp 1
X.in +4
X.en
X.!
X.! End Option
X.define EO
X.in -4
X.en
X.!
X.!
X.define AP
X.fi
X.he /Professional GEM/Appendix $1/#/
X.bp
X.cl 1 Appendix - $1 $2 $3 $4 $5 $6
X.ce on
X.bold on
XAppendix - $1
X.br
X$2 $3 $4 $5 $6
X.bold off
X.ce off
X.sp 3
X.nf
X.en
X.!
X.!
X.!****************************************************************************
X.!
X.!				End Macros
X.!
X.!****************************************************************************
SHAR_EOF
if test 1170 -ne "`wc -c 'macros.prf'`"
then
	echo shar: error transmitting "'macros.prf'" '(should have been 1170 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