Professional GEM - Part III - The dialog handler

From Atari Wiki
Revision as of 13:39, 13 September 2006 by Zorro 2 (talk | contribs)
Jump to navigation Jump to search


        Professional GEM                                               16


                                   PART III

                             THE DIALOG HANDLER



        A MEANING FULL DIALOG

             This issue  of  ST PRO GEM begins an exploration of ST GEM's
        dialog   handler.    I   will  discuss  basic  system  calls  for
        presenting  the  dialog,  and  then  continue with techniques for
        initializing   and  reading  on/off  button  and  "radio"  button
        objects.   We   will  also  take  some  short side-trips into the
        operation  of  the GEM Resource Construction Set to assist you in
        building these dialogs.  

             There are  a number of short C routines which accompany this
        column.   These  are  stored  as  file  GEMCL3.XMO  in  DL  5  on
        SIG*ATARI.   Before   reading   this  column,  you  should  visit
        SIG*ATARI (go pcs-132) and download this file.  



             DEFENING TERMS

             A dialog  box is an "interactive form" in which the user may
        enter  text  and  indicate selections by pointing with the mouse.
        Dialogs  in  GEM are "modal", that is, when a dialog is activated
        other  screen  functions  such  as menus and  window controls are
        suspended until the dialog is completed.  

             In most  cases,  the  visual  structure  of  a GEM dialog is
        specified  within  your  application's  resource  file.   The GEM
        Resource  Construction  Set  (RCS)  is used to build a picture of
        the dialog.  

             When the  RCS  writes  out  a  resource,  it  converts  that
        picture  into  a tree of GEM drawing objects and stores this data
        structure  within  the  resource.   Before  your  application can
        display  the  dialog,  it  must  load this resource file and find
        the  address of the tree which defines the dialog.  

             To load  a  resource,  the AES checks its size and allocates
        memory  for  the  load.  It then reads in the resource, adjusting
        internal  pointers  to  reflect  the  load address.  Finally, the
        object   sizes   stored   in  the  resource  are  converted  from
        characters to pixels using the system font size.  

             A note  for  those  with Macintosh experience:  Although Mac
        and   GEM   resources   share   a  name,  there  are  fundamental
        differences  which  can  be misleading.  A Mac resource is a fork
        within  a  file;  a  GEM  resource  is a TOS file by itself.  Mac


        


        Professional GEM            Part III                           17


        resources  may  be  paged in and out of memory; GEM resources are
        monolithic.   GEM  resources  are internally tree structured; Mac
        resources   are   not.    Finally,  Mac  resources  include  font
        information,  while ST GEM does this with font loading at the VDI
        level.  

             The resource load is done with the GEM AES call: 

                        ok = rsrcload(ADDR("MYAPP.RSC"));

        "MYAPP"  should  be  replaced  with  the  name  of  your program.
        Resources  conventionally  have  the  same  primary name as their
        application,  with  the  RSC  extent name instead of PRG.  The ok
        flag  returned  by  rsrcload will be FALSE is anything went wrong
        during the load.  

             The most  common  causes  of  failure  are  the resource not
        being  in  the  application's subdirectory, or lack of sufficient
        memory  for  GEM  to  allocate  space  for the resource.  If this
        happens, you must terminate the program immediately.  

             Once you  have  loaded the resource, you find the address of
        a dialog's object tree with: 

                       rsrcgaddr(RTREE, MYDIALOG, &tree);

        Tree  is  a 32-bit variable which will receive the address of the
        root node of the tree.  

             The mnemonic  MYDIALOG  should be replaced with the name you
        gave  your  dialog when defining it in the RCS.  At the same time
        that  it  writes  the  resource, RCS generates a corresponding .H
        file  containing  tree  and  object names.  In order to use these
        mnemonics  within your program, you must include the name file in
        your compile:  #include "MYAPP.H" 


        BUG ALERT !

             When using  the  DRI/Alcyon  C compiler, .H files must be in
        the  compiler's  home  directory or they will not be found.  This
        is  especially  annoying  using a two floppy drive ST development
        system.  The  only  way around this is to explicitly reference an
        alternate  disk  in  the  #include,  for  instance:  "B:MYAPP.H".
        [Ed.  Note:  Use the -i flag with the C pre-processor to name the
        include directories].  

             Now that  the address of the dialog tree has been found, you
        are  ready  to  display  it.  The standard (and minimal) sequence
        for  doing so is given in routine hndldial() in the download.  We
        will now walk through each step in this procedure.  



        


        Professional GEM            Part III                           18



             The formcenter  call  establishes the location of the dialog
        on  the  screen.   Dialog  trees  generated  by  the  RCS have an
        undefined origin (upper-left corner).  

             Formcenter computes  the  upper-left  location  necessary to
        center  the dialog on the screen, and inserts it into the OBX and
        OBY  fields of the ROOT object of the tree.  It also computes the
        screen  rectangle  which  the  dialog  will  occupy on screen and
        writes  its pixel coordinates into variables xdial, ydial, wdial,
        and hdial.  

             There is  one  peculiarity  of formcenter which occasionally
        causes  trouble.  Normally the rectangle returned in xdial, etc.,
        is exactly the same size as the basic dialog box.  

             However, when  the  OUTLINED  enhancement has been specified
        for  the  box,  formcenter  adds  a  three  pixel  margin  to the
        rectangle  returned.  This  causes  the  screen  area  under  the
        outline  to  be  correctly  redrawn later (see below).  Note that
        OUTLINED  is  part  of the standard dialog box in the RCS.  Other
        enhancements,  such  as  SHADOWED  or  "outside"  borders are NOT
        handled  in  this  fashion,  and you must compensate  for them in
        your code.  

             The next  part  of  the  sequence  is a formdial call with a
        zero  parameter.   This reserves the screen for the dialog action
        about  to  occur.  Note  that the C binding given for formdial in
        the  DRI  documents  is  in error: there are nine parameters, not
        five.   The  first  set  of  xywh arguments is actually used with
        formdial  calls  1 and 2 only, but place holders must be supplied
        in all cases.  

             The succeeding  formdial  call  (parameter  one)  animates a
        "zoom  box"  on  the  screen which moves and grows from the first
        screen  rectangle given to the second rectangle, where the dialog
        will be displayed.  

             The use  of  this  call  is  entirely optional.  In choosing
        whether  to use it or not, you should consider whether the origin
        of  the  "zoom"  is  relevant  to the operation.  For instance, a
        zoom  from  the  menu bar is relatively meaningless, while a zoom
        from  an  object about to be edited in the dialog provides visual
        feedback  to  the  user,  showing  whether the correct object was
        chosen.  

             If the  origin  is  not  relevant,  then  the zoom is just a
        time-waster.  If  you decide to include these effects, consider a
        "preferences"   option   in    your  app  which  will  allow  the
        experienced  and  jaded user to turn them off in the interests of
        speed.  



        


        Professional GEM            Part III                           19



             The objcdraw  call  actually  displays  the  dialog  on  the
        screen.  Note that the address of the tree, the beginning drawing
        object,  and  the  drawing depth are passed as arguments, as well
        as the rectangle allotted for the dialog.  

             In general,  dialogs  (and  parts  of  dialogs)  are  ALWAYS
        drawn  beginning  at  the  ROOT  (object zero).  When you want to
        draw    only  a  portion  of  the  dialog,  adjust  the  clipping
        rectangle,  but  not  the  object  number.  This ensures that the
        background of the dialog is always  drawn correctly.  

             The objcxywh()  utility  in the download can be used to find
        the  clipping  rectangle  for  any object within a dialog, though
        you  may  have to allow an extra margin is you have used shadows,
        outlines, or outside borders with the object.  

             Calling formdo  transfers control to the AES, which animates
        the  dialog for user interaction.  The address of the dialog tree
        is  passed  as a parameter.  The second paramter is the number of
        the  editable  object  at  which  the  text  cursor will first be
        positioned.  If  you have no text fields, pass a zero.  Note that
        again  the  DRI  documents are in error: passing a -1 default may
        crash  the  system.  Also  be  careful that the default which you
        specify   is   actually  a  text  field;  no  error  checking  is
        performed.  

             The formdo  call  returns  the number of the object on which
        the  clicked  to  terminate the dialog.  Usually this is a button
        type   object  with  the  EXIT  and  SELECTABLE  attributes  set.
        Setting  the DEFAULT attribute as well will cause an exit on that
        object is a carriage return is struck while in the dialog.  

             If the  top bit of the return is set, it indicates that  the
        exit  object  had the TOUCHEXIT attribute and was selected with a
        double-click.   Since  very few dialogs use this combination, the
        sample code simply masks off the top bit.  

             The next  formdial  call  reverses the "zoom box", moving it
        from  the  dialog's location back to the given x,y,w,h.  The same
        cautions apply here as above.  

             The final  formdial  call  tells  GEM  that  the  dialog  is
        complete,  and that the screen area occupied by the dialog is now
        considered  "dirty"  and  needs to be redrawn.  Using the methods
        described  in  our  last  column,  GEM  then sends redraws to all
        windows  which were overlaid, and does any necessary redrawing of
        the menu or desktop itself.  

             There is  one  notable  "feature" of formdial(3):  It always
        redraws  an  area  which is two pixels wider and higher than your
        request!    This   was   probably  included  to  make  sure  that


        


        Professional GEM            Part III                           20


        drop-shadows were cleaned up, and is usually innocuous.  


        A HANDY TRICK

             Use of  the formdial(3) call is not limited to dialogs.  You
        can  use  it  to  force  the  system  to  redraw  any part of the
        screen.   The  advantage  of  this method is that the redraw area
        need  not lie entirely within a window, as was necessary with the
        sendredraw  method  detailed  in the last column.  A disadvantage
        is  that  this  method  is  somewhat slower, since the AES has to
        decide who gets the redraws.  


        CLEAN UP

             As a  last  step, you need to clear the SELECTED flag in the
        object  which  was  clicked.   If  you do not do this, the object
        will   be  drawn inverted the next time you call the dialog.  You
        could  clear  the  flag  with  the GEM objcchange call, but it is
        inefficient since you do not need to redraw the object.  

             Instead, use  the  deselobj()  code  in the  download, which
        modifies  the  object's  OBSTATE  field directly.  Assuming  that
        retobj contains the exit object returned by hndldial, the call: 

                             deselobj(tree, retobj);

        will do the trick.  


        RECAP

             The basic  dialog  handling method I have described contains
        three  steps:  initialization  (rsrcgaddr),  dialog  presentation
        (hndldial), and cleanup (deselobj).  

             As we  build  more  advanced dialogs, these same basic steps
        will   be  performed,  but  they  will  grow  more  complex.  The
        initialization  will  include  setting  up proper object text and
        states,  and  the  cleanup  phase will also interrogate the final
        states of objects to find out what the user did.  


        BUTTON BUTTON

             The simple   dialogs   described  above  contain  only  exit
        buttons  as  active  objects.  As such, they are little more than
        glorified alert boxes.  

             We will  now increase the complexity a little by considering
        non-exit   buttons.    These   are  constructed  by  setting  the


        


        Professional GEM            Part III                           21


        SELECTABLE  attribute  on  a button object.  At run-time, such an
        object  will  toggle its state between selected (highlighted) and
        non-selected   whenever  the user clicks on it.  (You can set the
        SELECTABLE  attribute   of  other  types  of objects and use them
        instead  of  actual  buttons,  but  be sure that the user will be
        able to figure out what you intend!)  

             Having non-exit  buttons  forces  us to consider the problem
        of  initializing  them  before  the dialog, and interrogating and
        resetting them afterward.  

             Since a  button is a toggle, it is usually associated with a
        flag  variable  in  the  program.  As part of the initialization,
        you should test the flag variable, and if true call: 

                              selobj(tree, BTNOBJ);

        which  will  cause  the  button  to  appear  highlighted when the
        dialog  is  first drawn.  Selobj() is in the download.  BTNOBJ is
        replaced  with the  name you gave your button when you defined it
        in  the  RCS.   Since the button starts out deselected, you don't
        have to do anything if your flag variable is false.  

             After the  dialog  has  completed,  you  need  to  check the
        object's  state.  The  selectp()  utility  does so by masking the
        OBSTATE  field.  You can simply assign the result of this test to
        your  flag  variable, but be sure that the dialog was exited with
        an  OK button, not with a CANCEL! Again, remember to clean up the
        button  with  deselobj().  (It's  often  easiest  to deselect all
        buttons  just  before you leave the dialog routine, regardless of
        the final dialog state.)  


        WHO'S GOT THE BUTTON ?

             Another common  use of buttons in a  dialog is to select one
        of  a  set  of possible options.  In GEM, such objects are called
        radio  buttons.   This term recalls automobile radio tuners where
        pushing  in  one  button  pops  out any others.  In like fashion,
        selecting  any  one  of  a  set  of  radio  buttons automatically
        deselects all of the others.  

             To use  the  radio  button feature, you must do some careful
        work with the Resource Construction Set.  

             First, each  member  of  a  set  of   radio  buttons must be
        children  of  the  same parent object within the  object tree. To
        create  this  structure,  put  a  hollow  box  type object in the
        dialog,  make  it big enough to hold all of the buttons, and then
        put the buttons into the box one at a time.  




        


        Professional GEM            Part III                           22



             By nesting  the  buttons  within  the  box object, you force
        them  to be its children.  Each of the buttons must have both the
        SELECTABLE  and  RADIO BUTTON attributes set.  When you are done,
        you  may  make the containing box invisible by setting its border
        to zero, but do not FLATTEN it! 

             Since each  radio  button represents a different option, you
        must  usually  assign  a  name to each object.  When initializing
        the  dialog,  you  must  check which option is currently set, and
        turn  on  the corresponding button only.  A chain of if-then-else
        structures assures that only one button will be selected.  

             At the  conclusion of the dialog, you must check each button
        with  selectp()  and make the appropriate adjustments to internal
        variables.  Again,  an  if-then-else  chain  is appropriate since
        only  one  button  may  be  selected.  Either deselect the chosen
        button within this chain or do them all at the end.  

             There is  one  common  use of radio buttons in which you may
        short-cut  this  procedure.   If  the  buttons each represent one
        possible  value  of  a  numeric  variable, for instance, a set of
        selector  buttons  representing   colors from zero to seven, then
        you can compute the initial object directly.  

             In order  for this technique to work, you must use a special
        capability  of  the  RCS.   Insert  the object corresponding to a
        zero  value  at  the top (or left) of your array of buttons, then
        put the "one" button below (or right) of it, and so on.  

             When the  buttons  are complete,  the SORT operation is used
        to  guarantee that the top/left object is in fact the first child
        of  the  parent  box  with the others following in order.  Due to
        the  details  of  object  tree  structure (to be discussed in the
        next   column),  this  will  guarantee  that  these  objects  are
        contiguous in the resource.  

             If you  assign  a  name  (say  BUTTON1) to the first button,
        then you can initialize the correct button with the call: 

                         selobj(tree, BUTTON1 + field);

        where field is the variable of interest.  

             When the  dialog is complete, you can scan the radio buttons
        to  compute  the  new  value  for  the  underlying variable.  The
        encode()  procedure  in  the  download  will do this.  As always,
        remember to deselect the buttons at the end.  

             You can  use  offsets  or  multipliers  if  your  variable's
        values  don't start with zero or increment by one.  If the values
        are  irregular you may be able to use a lookup table, at the cost


        


        Professional GEM            Part III                           23


        of additional code.  


        COMING UP NEXT

             In the  next  column,  I will discuss the internal structure
        of  object trees.  Then we'll use that knowledge to build a piece
        of  code which will "walk" an entire tree and apply a function to
        each  object.  We'll  apply  this  code  to  do all of the button
        deselects   with  a  single  call!  I'll  also  look  at handling
        editable  text  fields  and discuss some ways to alter a dialog's
        appearance at run-time.  


        DISPEL GREMLINS

             An editing   error   caused   an   omission   in  the  first
        installment  of  ST  PRO  GEM.  The window components RTARROW and
        DNARROW  should  have  been  listed  along  with  HSLIDE  as  the
        horizontal  equivalents  of  the vertical slider components which
        were discussed.  

































        

Back to Professional_GEM