Professional GEM 1
P�PA�AR�RT�T -�- I�I
W�Wi�in�nd�do�ow�ws�s
I�IN�N T�TH�HE�E B�BE�EG�GI�IN�NN�NI�IN�NG�G
In GEM, creating a window and displaying it are two
different functions. The creation function is called
windcreate, and its calling sequence is:
handle = windcreate(parts, xfull, yfull, wfull, hfull);
This function asks GEM to reserve space in its memory for a new
window description, and to return a code or "handle" which you
can use to refer to the window in the future. Valid window
handles are positive integers; they are not memory pointers.
GEM can run out of window handles. If it does so, the
value returned is negative. Your code should always check for
this situation and ask the program's user to close some windows
and retry if possible. Handle zero is special. It refers to
the "desktop", which is predefined as light green (or gray) on
the ST. Window zero is always present and may be used, but
never deleted, by the programmer.
The xfull, yfull, wfull, and hfull parameters are integers
which determine the maximum size of the window. Xfull and yfull
define the upper left corner of the window, and wfull and hfull
specify its width and height. (Note that all of the window
coordinates which we use are in pixel units.)
GEM saves these values so that the program can get them
later when processing FULL requests. Usually the best maximum
size for a window is the entire desktop area, excepting the menu
bar. You can find this by asking windget for the working area
of the desktop (handle zero, remember):
windget(0, WFWXYWH, &xfull, &yfull, &wfull, &hfull);
Note that WFWXYWH, and all of the other mnemonics used in this
article, are defined in the GEMDEFS.H file in the ST Toolkit.
The parts parameter of windcreate defines what features
will be included in the window when it is drawn. It is a word
of single bit flags which indicate the presence/absence of each
feature. To request multiple features, the flags are "or-ed"
together. The flags' mnemonics and meanings are:
NAME - A one character high title bar at the top of the
window.
�
Professional GEM Part I 2
INFO - A second character line below the NAME.
MOVER - This lets the user move the window around by
"dragging" in the NAME area. NAME also needs to be
defined.
CLOSER - A square box at the upper left. Clicking this
control point asks that the window be removed from the
screen.
FULLER - A diamond at upper right. Clicking this control
point requests that the window grow to its maximum size, or
shrink back down if it is already big.
SIZER - An arrow at bottom right. Dragging the SIZER lets
the user choose a new size for the window.
VSLIDE - defines a right-hand scroll box and bar for the
window. By dragging the scroll bar, the user requests that
the window's "viewport" into the information be moved.
Clicking on the gray box above the bar requests that the
window be moved up one "page". Clicking below the bar
requests a down page movement. You have to define what
constitutes a page or line in the context of your
application.
UPARROW - An arrow above the right scroll bar. Clicking
here requests that the window be moved up one "line".
Sliders and arrows almost always appear together.
DNARROW - An arrow below the right scroll bar. Requests
that window be moved down a line.
HSLIDE - These features are the horizontal equivalent of the
RTARROW above. They appear at the bottom of the window.
Arrows LFARROW usually indicate "character" sized movement
left and right. "Page" sized movement has to be defined by
each application.
It is important to understand the correspondence between
window features and event messages which are sent to the
application by the GEM window manager. If a feature is not
included in a window's creation, the user cannot perform the
corresponding action, and your application will never receive
the matching message type. For example, a window without a
MOVER may not be dragged by the user, and your app will never
get a WMMOVED message for that window.
Another important principle is that the application itself
is responsible for implementing the user's window action request
when a message is received. This gives the application a chance
to accept, modify, or reject the user's request.
�
Professional GEM Part I 3
As an example, if a WMMOVED message is received, it
indicates that the user has dragged the window. You might want
to byte or word align the requested position before proceeding
to move the window. The windset calls used to perform the actual
movements will be described in the next article.
O�OP�PE�EN�N S�SE�ES�SA�AM�ME�E!�!
The windopen call is used to actually make the window
appear on the screen. It animates a "zoom box" on the screen
and then draws in the window's frame. The calling sequence is:
windopen(handle, x, y, w, h);
The handle is the one returned by windcreate. Parameters x, y,
w, and h define the initial location and size of the window.
Note that these measurements INCLUDE all of the window frame
parts which you have requested. To find out the size of the area
inside the frame, you can use
windget(handle, WFWXYWH, &innerx, &innery, &innerw, &innerh);
Whatever size you choose for the window display, it cannot be
any larger than the full size declared in windcreate.
Here is a good place to take note of a useful utility for
calculating window sizes. If you know the "parts list" for a
window, and its inner or outer size, you can find the other size
with the windcalc call:
windcalc(parts, kind, inputx, inputy, inputw, inputh, &outputx,
&outputy, &outputw, &outputh);
Kind is set to zero if the input coordinates are the inner area,
and you are calculating the outer size. Kind is one if the
inputs are the outer size and you want the equivalent inner
size. Parts are just the same as in windcreate.
There is one common bug in using windopen. If the NAME
feature is specified, then the window title must be initialized
BEFORE opening the window:
windset(handle, WFNAME, ADDR(title), 0, 0);
If you don't do this, you may get gibberish in the NAME area or
the system may crash. Likewise, if you have specified the INFO
feature, you must make a windset call for WFINFO before opening
the window.
�
Professional GEM Part I 4
Note that ADDR() specifies the 32-bit address of title.
This expression is portable to other (Intel-based) GEM systems.
If you don't care about portability, then &title[0], or just
title alone will work fine on the ST.
C�CL�LE�EA�AN�NI�IN�NG�G U�UP�P
When you are done with a window, it should be closed and
deleted. The call
windclose(handle);
takes the window off the screen, redraws the desktop underneath
it, and animates a "zoom down" box. It doesn't delete the
window's definition, so you can reopen it later.
Deleting the window removes its definition from the system,
and makes that handle available for reuse. Always close windows
before deleting, or you may leave a "dead" picture on the
screen. Also be sure to delete all of your windows before
ending the program, or your app may "eat" window handles. The
syntax for deleting a window is:
winddelete(handle);
T�TH�HO�OS�SE�E F�FA�AT�T S�SL�LI�ID�DE�ER�RS�S
One of ST GEM's unique features is the proportional slider
bar. Unlike other windowing systems, this type of bar gives
visual feedback on the fraction of a document which is being
viewed, as well as the position within the document. The catch,
of course, is that you have two variables to maintain for each
scroll bar: size and position.
Both bar size and position range from 1 to 1000. A bar
size of 1000 fills the slide box, and a value of one gets the
minimum bar size. To compute the proper size, you can use the
formula:
size = min(1000, 1000 * seendoc / totaldoc)
Seendoc and totaldoc are the visible and total size of the
document respectively, in whatever units are appropriate. As an
example, if your window could show 20 lines of a 100 line text
file, you should set a slider size of 200. Since the window
might be bigger than the total document at some points, you
need the maximum function. If the document size is zero, force
the slider size to 1000. (Note: You will probably need to do
�
Professional GEM Part I 5
the computation above with 32-bit arithmetic to avoid overflow
problems).
Once you have computed the size, use the windset function
to configure the scroll bar:
windset(handle, WFVSLSIZE, size, 0, 0, 0);
This call sets the vertical (right hand) scroll bar. Use
WFHSLSIZE for the horizontal scroller. All of these examples
are done for the vertical dimension, but the principles are
identical in the other direction.
Bar positioning is a little tougher. The most confusing
aspect is that the 1-1000 range does not set an absolute
position of the bar within the scroll box. Instead, it
positions the TOP of the bar within its possible range of
variation.
Let's look at our text file example again to make this
clearer. If there are always 20 lines of a 100 line file
visible, then the top of the window must be always be somewhere
between line 1 and line 81. This 80 line range is the actual
freedom of movement of the window. So, if the window were
actually positioned with its top at line 61, it would be at the
three-quarter position within the range, and we should set a
scroll bar position of 750. The actual formula for computing
the position is:
pos = 1000 * (topwind - topdoc) / (totaldoc - seendoc)
Topwind and topdoc are the top line in the current window and
the whole document, respectively. Obviously, if seendoc is
greater or equal to totaldoc, you need to force a zero value for
pos. This calculation may seem rather convoluted the first time
through, but is easy once you have done it. When you have
computed the position, windset configures the scroll bar:
windset(handle, WFVSLIDE, pos, 0, 0, 0);
WFHSLIDE is the equivalent for horizontal scrolling.
It is a good practice to avoid setting the slider size or
position if they are already at the value which you need. This
avoids an annoying redraw flash on the screen when it is not
necessary. You can check on the current value of a slider
parameter with windget:
windget(handle, WFVSLIDE, &currvalue, &foo, &foo, &foo);
Foo is a dummy variable which needs to be there, but is not
�
Professional GEM Part I 6
used. Substitute WFVSLIDE with whatever parameter you are
checking.
One philosophical note on the use of sliders: It is
probably best to avoid the use of both sliders at once unless it
is clearly appropriate to the type of data which is being
viewed.
Since Write and Paint programs make use of the
sheet-of-paper metaphor, moving the window around in both
dimensions is reasonable. However, if the data is more randomly
organized, such as a tableau of icons, then it is probably
better to only scroll in the vertical dimension and "reshuffle"
if the window's width is changed. Then the user only needs to
manipulate one control to find information which is off-screen.
Anyone who has had trouble finding a file or folder within a
Desktop window will recognize this problem.
C�CO�OM�MI�IN�NG�G U�UP�P N�NE�EX�XT�T
In my next column in Antic Online, we'll conclude the tour
of the ST's windowing system. I'll discuss the correct way to
redraw a window's contents, and how to handle the various
messages which an application receives from the window manager.
Finally, we'll look at a way to redesign the desktop background
to your own specifications.
F�FE�EE�ED�DB�BA�AC�CK�K
One of the beauties of an on-line column is that you can
make your comments known immediately. To register your
opinions, select ST FEEDBACK, enter your message, leave your
name, and enter a blank line to exit.
I am interested in hearing proposals for topics, feedback
on the technical level of the column, and reports on bugs and
other "features" in both the column and the ST itself. Your
comments will be read by the ANTIC staff and myself and, though
we might not answer individual questions, they will be used to
steer the course of future columns.