[comp.sys.atari.st] PROGEM 4

exodus@uop.UUCP (Freddy Kreuger) (10/06/87)

                       **Professional GEM**
                           by Tim Oren

                             11/7/85


   Welcome to the fourth installment of ST PRO GEM.  We are about to
delve into the mysteries of GEM resource structure, and then use this
knowledge to create some useful utilities for handling dialogs.  As
with the past columns, there is once again a download file.  You will
find it under the name GEMCL4.C in the ATARI 16-bit Forum (GO PCS-58).

   The first and largest part of the download contains a C image of a
sample resource file.  To create this listing, I used the GEM
Resource  Construction Set to create a dummy resource with three
dialogs including examples of all object types, then enabled the C
output option and saved  the resource.  If you have access to a copy
of RCS, I suggest that you  create your own listing in order to get a
feel for the results.  Then, using  either listing as a roadmap to
the resource, you can follow along as  we enter...

   A MAZE OF TWISTY LITTLE PASSAGES.  While a GEM resource is loaded
as a block of binary information, it is actually composed of a number
of different data structures.  These structures are linked together
in a rather tangled hierarchy.  Our first job is to map this linkage
system.

   The topmost structure in a resource file is the resource header.
This is an array of words containing the size and offset within the
resource of the other structures which follow.  This information is
used by  GEM during the resource load process, and you should never
need to access it. (The resource header does not appear in the C
output file; it is generated by the RSCREATE utility if the C file is
used to recreate the resource.)

   The next structure of interest is the tree index.  This is an
array of long pointers, each of which addresses the beginning of an
object tree. Again, you wouldn't normally access this structure
directly.  The GEM  rsrc_gaddr call uses it when finding trees'
addresses.  This structure is called "rs_trindex" in the C output.

   If you look at the contents of rs_trindex you will notice that the
values are integers, instead of the pointers I described.  What has
happened is that RCS has converted the pointers to indices into the
object array. (If you actually used the C file to recreate the
resource file, then the  pointers would be regenerated by RSCREATE.)


   Now you can follow the link from rs_trindex to the objects stored
in rs_object.  Take (for instance) the second entry in rs_trindex and
count down that many lines in rs_object.  The following line (object)
should start with a -1.  This indicates that it is the root object of
a tree.  The following objects down to the next root belong to that
tree. We'll pass over the details of inter-object linkage for now,
leaving it for a later column.

   There are a number of different fields in an object, but right now
we'll concentrate on two of them: OB_TYPE and OB_SPEC.  The OB_TYPE
is the field which contains mnemonics like G_STRING and G_BOX
indicating the type of the object.  The OB_SPEC is the only field in
each object which  is a LONG - you can tell it by the L after the
number.

   What's in OB_SPEC depends on the object type, so we need to talk
about what kinds of objects are available, what you might use them
for, and finally how they use the OB_SPEC field.

   The box type objects are G_BOX, G_IBOX, and G_BOXCHAR.  A G_BOX is
an opaque rectangle, with an optional border.  It's used to create a
solid patch of color or pattern on which to place other objects.  For
instance, the background of a dialog is a G_BOX.

   A G_IBOX is a hollow box which has only a border.  (If the border
has no thickness, then the box is "invisible", hence the name.)  The
favorite use for IBOXes is to hold radio buttons.  There is also one
neat trick you can play with an IBOX.  If you have more than one
object (say an image and a string) which you would like to have
selected all at once, you can insert them in a dialog, then cover
them with an IBOX. Since the box is transparent, they will show
through.  If you now make the box selectable, clicking on it will
highlight the whole area at once!

   The G_BOXCHAR is just like a G_BOX, except that a single character
is drawn in its center.  They are mostly used as "control points":
the FULLER, CLOSER, SIZER, and arrows in GEM windows are BOXCHARs, as
are  the components of the color selection gadgets in the RCS.

   The OB_SPEC for box type objects is a packed bit array.  Its
various fields contain the background color and pattern, the border
thickness and color, and the optional character and its color.

   The string type objects are G_STRING, G_BUTTON, and G_TITLE.
G_STRINGs (in addition to being a bad pun) are for setting up static
explanatory text within dialogs.  The characters are always written
in the "system font": full size, black, with no special effects.

   We have already discussed many of the uses of G_BUTTONs.  They add
a border around the text.  The thickness of a G_BUTTON's border is
determined by what flags are set for the object.  All buttons start
out with a border thickness of one pixel.  One pixel is added if the
EXIT attribute is set, and one more is added if the DEFAULT attribute
is set.

   The G_TITLE type is a specially formatted text string used only in
the title bar of menus.  This type is needed to make sure that the
menus redraw correctly.  The Resource Construction Set automatically
handles inserting G_TITLEs, so you will seldom use them directly.

   In a resource, the OB_SPEC for all string objects is a long
pointer to a null terminated ASCII string.  The string data in the C
file is shown in the BYTE array rs_strings.  Again you will notice
that  the OB_SPECs in the C file have been converted to indices into
rs_string. To find the string which matches the object, take the
value of OB_SPEC and count down that many lines in rs_strings.  The
next line is the correct string.

   The formatted text object types are G_TEXT, G_BOXTEXT, G_FTEXT,
and G_FBOXTEXT.  G_TEXTs are a lot like strings, except that you can
specify a color, different sizes, and a positioning rule for the
text.   Since they require more memory than G_STRINGs, G_TEXTs should
be used  sparingly to draw attention to important information within
a dialog.   G_TEXTs are also useful for automatic centering of dialog
text which is  changed at run-time.  I will describe this technique
in detail later on.

   The G_BOXTEXT type adds a solid background and border to the
G_TEXT type.  These objects are occasionally used in place of
G_BUTTONs when their color will draw attention to an important
object.

   The G_FTEXT object is an editable text field.  You are able to
specify a constant "template" of characters, a validation field for
those characters which are to be typed in, and an initial value for
the input characters.  You may also select color, size, and
positioning rule for G_FTEXTs.  We'll discuss text editing at length
below.

   The G_FBOXTEXT object, as you might suspect, is the same as
G_FTEXT with the addition of background and border.  This type is
seldom used: the extra appearance details distract attention from the
text being edited.

   The OB_SPEC for a formatted text object is a pointer to yet
another type of structure: a TEDINFO.  In the C file, you will find
these in rs_tedinfo.  Take the OB_SPEC value from each text type
object and count down that many entries in rs_tedinfo, finding the
matching TEDINFO on the next line.  Each contains pointers to ASCII
strings for the template, validation, and initialization.  You can
find these strings in rs_strings, just as above.

   There are also fields for the optional background and border
details, and for the length of the template and text.  As we will see
when discussing  editing, the most important TEDINFO fields are the
TE_PTEXT pointer to  initialized text and the TE_TXTLEN field which
gives its length.

   The G_IMAGE object type is the only one of its kind.  A G_IMAGE is
a monochrome bit image.  For examples, see the images within the
various GEM alert boxes.  Note that monochrome does not necessarily
mean black.  The image may be any color, but all parts of it are the
SAME color.  G_IMAGEs are used as visual cues in dialogs.  They are
seldom used as selectable items because their entire rectangle is
inverted when they are clicked.  This effect is seldom visually
pleasing, particularly if the image is colored.

   G_IMAGE objects have an OB_SPEC which is a pointer to a further
structure type: the BITBLK.  By now, you should guess that you will
find it in the C file in the array rs_bitblk.  The BITBLK contains
fields describing the height and width of the image in pixels, its
color,nd it also contains a long pointer to the actual bits which
make up the image.  In the C file, the images are encoded as
hexadecimal words and stored in arrays named IMAG0, IMAG1, and so on.

   The last type of object is the G_ICON.  Like the G_IMAGE, the
G_ICON is a bit image, but it adds a mask array which selects what
portions of the image will be drawn, as well as an explanatory text
field.  A G_ICON may also specify different colors for its
"foreground" pixels (the ones that are normally black), and its
"background" pixels (which are normally white).

   The pictures which you see in Desktop windows are G_ICONs, and so
are the disks and trashcan on the desktop surface.  With the latter
you will notice the effects of the mask.  The desktop shows through
right up to the edge of the G_ICON, and only the icon itself (not a
rectangle) is inverted when a disk is selected.

   The OB_SPEC of an icon points to another structure called an
ICONBLK.  It is shown in the C file as rs_iconblk.  The ICONBLK
contains long pointers to its foreground bit array, to the mask bit
array, and to the ASCII string of explanatory text.  It also has the
foreground and background colors as well as the location of the text
area from the  upper left of the icon.  The most common use of
G_ICONs and ICONBLKs  is not in dialogs, instead they are used
frequently in trees which are build at run-time, such as Desktop
windows.  In a future article, we will return to a discussion of
building such "on-the-fly" trees with G_ICONs.

   Now, let's recap the hierarchy of resource structures:  The
highest level structures are the resource header, and then the tree
index.  The tree index points to the beginning of each object tree.
The objects making up the tree are of several types, and depending on
that type, they may contain pointers to ASCII strings, or to TEDINFO,
ICONBLK, or BITBLK structures.  TEDINFOs contain further pointers to
strings; BITBLKs have pointers to bit images; and ICONBLKs have both.

   PUTTING IT TO WORK.  The most common situations requiring you to
understand resource structures involve the use of text and editable
text objects in dialogs.  We'll look at two such techniques.

   Often an application requires two or more dialogs which are very
similar except for one or two title lines.  In this circumstance, you
can save a good deal of resource space by building only one dialog,
and changing the title at run time.

   It is easy to go wrong with this practice, however, because the
obvious tactic of using a G_STRING and writing over its text at run
time can go wrong.  The first problem is that you must know in
advance the longest title to be used, and put a string that long into
the resource. If you don't you will damage other objects in the
resource as you copy in characters.  The other problem is that a
G_STRING is always drawn at the same place in a dialog.  If the
length of the title changes from time to time, the dialog will have
an unbalanced and sloppy  appearance.

   A better way to do this is to exploit the G_TEXT object type, and
the TEDINFO structure.  The set_text() routine in the download shows
how.  The parameters provided are the tree address, the object
number, and the 32-bit address of the string to be substituted.  For
this to work, the object referenced should be defined as a G_TEXT
type object.   Additionally, the Centered text type should be chosen,
and the object should have been "stretched" so that it fills the
dialog box from side to side.

   In the code, the first action is to get the OB_SPEC from the
object which was referenced.  Since we know that the object is a
G_TEXT, the OB_SPEC must point to a TEDINFO.  We need to change two
fields in the TEDINFO.  The TE_PTEXT field is the pointer to the
actual string to be displayed; we replace it with the address of our
new string. The TE_TXTLEN field is loaded with the new string's
length.  Since the Centered attribute was specified for the object,
changing the TE_TXTLEN will cause the string to be correctly
positioned in the middle of the dialog!

   Editing text also requires working with the TEDINFO structure. One
way of doing this is shown in the download.  The object to be used
(EDITOBJ) is assumed to be a G_FTEXT or G_FBOXTEXT.  Since we will
replace the initialized text at run time, that field may be left
empty when building the object in the RCS.

   The basic trick of this code is to point the TEDINFO's TE_PTEXT at
a string which is defined in your code's local stack.  The advantages
of this technique are that you save resource space, save static data
by putting the string in reusable stack memory, and automatically
create a scratch string which may be discarded if the dialog is
cancelled.

   The text string shown is arbitrarily 41 characters long.  You
should give yours a length equal to the number of blanks in the
object's template field plus one.  Note that the code is shown as a
segment,  rather than a subroutine.  This is required because the
text string must be allocated within the context of dialog handling
routine itself,  rather than a routine which it calls!

   After the tree address is found, the code proceeds to find the
TEDINFO and modify its TE_PTEXT as described above.  However, the
length which is inserted into TE_TXTLEN must be the maximum string
length,  including the null!

   The final line of code inserts a null into the first character of
the uninitialized string.  This will produce an empty editing field
when the dialog is displayed.  If there is an existing value for  the
object, you should instead use strcpy() to move it into text[]. Once
the dialog is complete, you should check its final status as
described in the last article.  If an "OK" button was clicked, you
will then use strcpy() to move the value in text[] back to its static
location.

   Although I prefer this method of handling editable text, another
method deserves mention also.  This procedure allocates a full length
text string of blanks when creating the editable object in the RCS.
At  run-time, the TE_PTEXT link is followed to find this string's
location in  the resource, and any pre-existing value is copied in.
After the dialog is run, the resulting value is copied back out if
the dialog completed successfully.

   Note that in both editing techniques a copy of the current string
value is kept within the application's data area.  Threading the
resource whenever you need to check a string's value is extremely
wasteful.

   One final note on editable text objects:  GEM's editor uses the
commercial at sign '@' as a "meta-character".  If it is the first
byte of the initialized text, then the field is displayed blank no
matter what follows.  This can be useful, but is sometimes confusing
when a user in all innocence enters an @ and has his text disappear
the next time the dialog is drawn!

   LETTERS, WE GET LETTERS.  The Feedback section on ANTIC ST ONLINE
is now functional and is producing a gratifying volume of response. A
number of requests were made for topics such as ST hardware and ST
BASIC which are beyond the intended scope of this column.  These have
been referred to ANTIC's editorial staff for action.

   So many good GEM questions were received that I will devote part
of the next column to answering several of general interest.  Also,
your  requests have resulting in scheduling future columns on VDI
text output  and on the principles (or mythology) of designing GEM
application interfaces. Finally, a tip of the hat to the anonymous
reader who suggested including the actual definitions of all macro
symbols, so that those without the appropriate H files can follow
along.  As a result of this suggestion, the definitions for this
column and the previous three are included at the end of the
download.  Future articles will continue this practice.

   STRAW POLL!  I'd like to make a practice of using the Feedback to
get your opinions on the column's format.  As a first trial, I'd like
to know your feelings about my use of "portability macros" in the
sample code.  These macros, LLGET for example, are used for
compatibility between 68K GEM systems like the ST, and Intel based
systems like the IBM PC.  This may be important to many developers.
On the other hand, omitting them results in more natural looking C
code.  For instance, in the download you will find a second version
of set_text() as  described above, but without the portability
macros.  So, I would like to know if you think we should  (A) Keep
the macros - portability is important to serious developers, (B) Get
rid of them - who cares about Intel chips anyway, or (C) Who cares?
I'll tally the votes in two weeks and announce the results here.

   STAY TUNED!  As well as answers to feedback questions, the next
column will discuss how GEM objects are linked to form trees, and how
to use AES calls and your own code to manipulate them for fun and
profit.  In the following installment, we'll look at the VDI raster
operations (also known as "blit" functions).