
                       GEM PROGRAMMING by Jonathan White

                                   ARTICLE 10

     Hello again, and welcome to  the  second  half of our discussion about
     GEM resource files and dialog boxes.  In  this month's article we will
     look at displaying the various forms of  dialog box and how to use the
     information from last month's article to interrogate a resource to get
     the users response, and to alter dialog boxes, if necessary.

     The first (and most basic)  form  of  dialog  is  the alert box. These
     aren't really meant for input of  data,  although they can be used for
     that in a limited way; their  main  function is to present information
     to the user which can't be ignored. I have seen systems that put alert
     boxes into windows, allowing the user to pull another window over them
     or send them to the  back  of  the  window  pile.  In my opinion, that
     defeats the object of having an alert  box  in the first place. If you
     want user interaction, use a  dialog  box.  If you want to communicate
     something to the user immediately,  use  an  alert box. A good example
     might be if a program can't  locate  its  .RSC file. In that case, the
     program probably couldn't continue anyway, so  the best thing to do is
     put an alert box up telling the user  the problem (so it can be fixed)
     and then quit neatly.

     Alert boxes  are  generated  using  the  function  form_alert().  This
     function receives two parameters; an integer which tells it the number
     of buttons to show, and a string to tell it the appearance of the box.
     The number of buttons range  between  1  and  3,  and the text has the
     following format..

     "[icon no][text to show][button text]"

     Icon no is a pointer to a list of default images to show with the box,
     as follows :-

             0       No icon
             1       A diamond with an exclamation mark (Note only)
             2       Point down triangle with a question mark (warning)
             3       A Hexagon with 'STOP' inside it (are you sure???)

     The text to show is a set format string. Each line, up to a maximum of
     5, must have no more than 40 characters in it. Each 'line'  within the
     string is separated by a |  character.  Note  a  good  tip is to put a
     space on the end of the longest 'line'  as that helps the AES align it
     correctly within the box.

     The button text contains up to 10 characters per button, or at most 30
     characters in total,  and  each  buttons  text  is  separated from the
     others by a | symbol.

     For example, a dialog box with the  question mark, two buttons Yes and
     No and the text 'This program is low  on memory (new line) do you wish
     to continue' would have  a string as follows..

     "[2][This program is low on memory |Do you wish to continue?][Yes|No]"

     Note Icon 1 is usually  meant  for  note  messages  you simply have to
     acknowledge - this program won't run | in less than one meg {OK}

     whereas icon 2 is a choice operator
     - Do you wish to save this file {yes}{no}

     And three is a 'are you really sure you want to do this? I mean REALLY
     sure?' kind of thing.

     - This program will reformat your hard  drive, do you wish to continue
     {yes}{no}

     Note I tend  to  use  yes   /  no  rather  than  OK  /  cancel. That's
     technically against  Atari  user  interface  guidelines  (which  state
     buttons should go OK CANCEL OTHERS)  but  it's  a hangover from my DOS
     programmer days.

     The function returns a value which  tells you which button was pressed
     (between 1 and 3)

     Now we have to discuss  actual  dialog  boxes.  But  just before we do
     that, we will look at a  very  specific  sort  of dialog box, the file
     selector. The standard (pre  tos  1.04)  was  pretty appalling and the
     later ones not much better,  but  it  remains  the standard way to get
     file paths from the user. Besides,  most people use a replacement file
     selector program like UIS or Selectric.  Life's  too short not to. The
     file selector is called  with  one  of  two  calls depending upon what
     version of TOS the computer your program  is running on. Pre TOS 1.04,
     only the standard file selector is available. This is called with :-

             fsel_input(path, file, button)

     path is a pointer to the  file  path  i.e  the top line including wild
     cards. Even if the actual path given is shorter, this string should be
     at least 128 characters  long,  to  accommodate  any  changes the user
     might make. Note  that  using  fsel_input  DOES  ACTUALLY  CHANGE THIS
     STRING. On exit it contains  the  full  pathname of the file selected.
     You will have  to  splice  up  this  string  using  the  separator '\'
     characters to get at the file.

     File is a pointer to the file  name, the second editable line. On exit
     this contains the file  selected.  It  should  be  13 characters long,
     including the terminating character.

     button is a pointer to a  WORD  which  will  contain a 0 if cancel was
     pressed and 1 if OK was pressed.

     Note you can use the BIOS  calls  Dgetdrv()  and Dgetpath() to get the
     current path before you call this  the  first  time. It is best NOT to
     presume a certain path will exist  and especially bad practice to hard
     wire a path into your code.

     TOS 1.04 introduced a  new  variant  of  the function (fsel_input will
     still work) called fsel_exinput.

             fsel_exinput(path, file, button, title)

     The new thing was the 'title'  parameter, which contains a text string
     which is printed at the top of the box when it is shown on the screen.

     Both functions return a 1 if successful or a 0 if an error occurred

     Note that if you use the file  selector, it is almost certain you will
     have a wind_redraw message waiting for you after it's finished.


     DIALOG BOXES

     Although resource files are stored in memory as any other data is, you
     MUST use rsrc_load(). The reason for  this  is that dialogs are stored
     with coordinates based on characters rather  than pixels and they must
     have these coordinates converted to  screen  pixels before they can be
     used. Once you have done that you  can get the address of the specific
     resource 'tree' or dialog box you wish to draw using the rsrc_gaddr().

     rsrc_gaddr()  takes as its  parameters  firstly  a  flag which defines
     what exactly to look for..

             Flag value      Look for

             0 (R_TREE)      Object tree
             1 (R_OBJECT)    Individual object
             2 (R_TEDINFO)   TEDINFO structure
             3 (R_ICONBLK)   ICONBLK structure
             4 (R_BITBLK)    BITBLK structure
             5 (R_STRING)    String data
             6 (R_IMAGEDATA) Free Image data
             7 (R_OBSPEC)    ob_spec field within OBJECTs
             8 (R_TEPTEXT)   te_ptext field within a TEDINFO
             9 (R_TEPTMPLT)  te_pmtplt field within a TEDINFO
            10 (R_TEPVALID)  te_pvalid field within a TEDINFO
            11 (R_IBPMASK)   ib_pmask field within an ICONBLK
            12 (R_IBPDATA)   ib_pdata field within an ICONBLK
            13 (R_IBPTEXT)   ib_ptext field within an ICONBLK
            14 (R_BIPDATA)   bi_pdata within a BITBLK
            15 (R_FRSTR)     Free string
            16 (R_FRIMG)     Free image

     The other parameters are the index number  of the object and a pointer
     to put the address into.

     This is used mostly with a  0  parameter  to  get the root object to a
     tree, as that then can be used  as  an array of OBJECT structures. And
     since you have the header  file  which  defines objects by name rather
     than number, you can identify an object as, say, dialog[filestring].

     The next part of the process  is, of course, drawing the dialog box on
     the screen. There are actually several steps to this, which we will go
     through in order..

     The first step is to call form_center() to set the point on the screen
     you wish a particular dialog box  to appear. Then you call form_dial()
     with the FMD_START parameter to tell the system you are drawing on the
     screen and  not  to  do  anything  while  you  do  so.  Then  you  use
     objc_draw() to actually display the dialog on the screen. This doesn't
     allow the user  to  interact  with  it  though.  That's  done with the
     form_do() command. This will quit when the user clicks on an object in
     the tree with the EXIT or TOUCHEXIT property and returns the number of
     the object that caused it to  exit.  If  that  is 'cancel' you have to
     reset all the objects  (and  the  variables  associated  with them) to
     their previous state. If it is OK,  you interrogate the objects to see
     which have changed, and update your  program as appropriate or perform
     the process requested. If it's a  TOUCHEXIT  object, you have to amend
     it then re-draw the dialog again.

     The idea of variables associated  with  objects  in  a resource is the
     best way to organise things. If the  user  clicks on 'OK', you can set
     the variables to the  state  of  the  particular  object.  If the user
     clicks on cancel, you can set the  object state to match the variable,
     which still holds the previous 'correct' value.

     Once the dialog has been finished  with, you do form_dial() again with
     the FMD_FINISH mode to clear  the  dialog  off  the screen. It's usual
     that this will also cause a wind_redraw  to be waiting in your message
     queue.

     NOTE: If you want to  use  grow  and  shrink  effects with your dialog
     boxes (personally, I find them  irritating,  and  I have a fast Atari,
     but whatever turns you on..)  you  can  use these with the form_dial()
     function in FMD_SHOW and FMD_SHRINK modes to show these.

     Note  that  because   form_dial()   intercepts   all   inputs  via  an
     evnt_multi() inside it, while you  have  a  dialog  on the screen, the
     rest of  your  application,  and  any  other  applications  in  a non-
     preemptive multitasking environment (errr.. Geneva is the only one for
     the Atari, as far as I know) will be frozen. In MultiTOS, the programs
     continue running, but the screen is not updated outside your dialog. I
     have no idea of the situation  in  MagiC, as the current version won't
     run on the Falcon, and they won't fix  it as they are busy on the next
     version, which is super duper and  all  singing all dancing as well as
     being (at the mo)  six  months  late.  In  the  words of Paul Merton..
     'Bloody marvellous, innit?'


     ANALYSING DIALOG BOX CONTENTS

     I give up the number of  times  I have seen articles about programming
     the Atari that have presented this  in  a really complicated way, when
     it's actually quite simple. All  the  ones  I  have  seen talk about a
     process called 'walking the resource tree' where you start at the root
     object and go through it examining each  object in order. I think this
     is stupid for three reasons..

     a) It's bloody complicated.
     b) It's wasteful. In a large proportion of dialogs, only a few objects
     which are at the bottom of the tree have information in them.
     c) It's not necessary.

     Simple C / GEM tricks one. If  you  define a pointer to an OBJECT, say
     *dialog for argument, and point it  towards  the root object of a tree
     you can use that as an array of  all the objects in that tree. So what
     you do is this.

     First off, define your pointer as OBJECT *(something)
     Second, use rsrc_gaddr to point the pointer  at the root object of the
     tree by using rsrc_gaddr(R_TREE, NAME_OF_ROOT_OBJECT, &(something))

     Then you can look at any object in the tree by treating it as the n'th
     object in the  array. Even  better,  since  any decent resource editor
     will give you a header file to  include that contains #defines for the
     names you gave the various objects in  the tree, you can even refer to
     them BY NAME.  So, in our example,  to  get  at the ob_state part of a
     resource  we  named  'name_string'   in   the   tree   pointed  to  by
     *(something), we simply have to use..

             something[name_string].ob_state

     Note you can use this to  set  values in resources, to examine strings
     the user has typed in (if you  use  the correct cast operators to make
     sure you keep  your  pointers  in  order)  or  to manipulate resources
     (including menus) in all sorts of  ways.  It's  a lot easier than some
     authors had led me to believe when I first tried it.

     NOTE: the first thing you must  always  do  is set the ob_state of the
     object that was passed as the  exit  object  to 'not selected'. If you
     forget to do that, the  dialog  will  quit  immediately next time it's
     drawn on the screen.

     The easiest way to deal with  all  this  is to encapsulate it. Put the
     process of drawing the dialog, examining all the relevant  objects and
     setting variables as necessary all in  one function, that you can call
     when the dialog needs  to  be  used.  That's basically what fsel_input
     is..


     And, believe it or not, that's where it ends. By now, all the standard
     GEM aspects have been covered. As  I  said many months ago (it seems),
     if you can handle what I have  explained  to you above, you can handle
     anything (more or less). Of course,  GEM programming is like any other
     form of programming. The more you  wish  your  program to do, the more
     complicated it gets. There are more things  that GEM can do, like GDOS
     printing, windowed dialog boxes, plus many  other things. But most are
     only variants of what has come before,  and  if you know this lot, you
     should be able to handle anything you need to do in a GEM program with
     a little patience and effort.

     Before I go, I  should  recommend  some  'further reading', I suppose.
     Firstly I must recommend both  'The  Atari Compendium' and the 'Modern
     Atari Systems Software' books  available  from  Hisoft.  The A.C. is a
     full and detailed guide to the whole Atari range, including some stuff
     it appears will never see the light  of  day, and M.A.S.S. is the best
     guide around to programming multitasking friendly applications and for
     using new features such as 3D dialog boxes and colour icons. As to GEM
     programming itself, books are  thinner  on  the  ground. You obviously
     have these articles, but other sources  you  might  be able to get are
     the Compute! Guides by Sheldon Leeman , which are good if woefully out
     of date and now out print, although still in a lot of public libraries
     or you might be able to get a copy secondhand. Also there is Pearson &
     Hodgson's Introducing Atari ST  Machine  Code  book,  which  is a good
     guide to  the  processes  of  programming  GEM  even  if  you  are not
     programming in assembly language (I  don't  and  it  helped me quite a
     lot). Or there is C-Manship  Complete  by Clayton Walnum which teaches
     GEM and C at the same time. That WAS on sale from Hisoft, and the late
     (very) lamented ST Review was selling it. As far as I know, it's still
     available, and is a good place  to  start.  Also, there are Tim Oren's
     GEM tutorials in the public  domain.  Tim  is  one of the original GEM
     programmers, and although again  his  work  only deals with 'standard'
     GEM it's a great, and very cheap resource. The best place I know of to
     try and get these books  is  probably  the  ST club in Nottingham, who
     seem to be able to get these for you, even if only secondhand.

     And with that, I finish.  Thanks  for  reading these articles over the
     months, and I hope it  has  helped  some,  and  piqued the interest of
     others. In any case, I wish you luck in programming, and may your bugs
     always be simple ones..
