Doodats
Volume Number: 5
Issue Number: 9
Column Tag: C Workshop
Three Doodats 
By Lee A. Neuse, Manassas, VA
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
[Lee Neuse started in the days you built them before programmed them. He was
lured away from IBM compatibles to the Mac in 1982 and been there ever since.
Currently, he is working for Computer Science Corporation designing and
implementing Mac software as part of CSC re search. His Mousehole handle is "Noisy".]
Three Doodats
Doodat -- a trivial problem guaranteed to make a programmer’s life miserable;
from the expression “How did he do dat?”.
It seems like every programmer, at one time or another, is faced with the same
dilemma: wanting to use a feature in a program, but not wanting to spend the time to
figure out the technique. For example, we all know that a Balanced B-Tree index is
wonderfully efficient, but how many of us really want to sit down and write the code?
One solution is to start reading back issues of MacTutor, hoping that some kind soul has
already published useable source code. Another is to scour public domain software for
an author willing to impart knowledge in exchange for a shareware donation. Either
way, it’s usually faster (and easier) to find and adapt someone else’s code instead of
doing it yourself.
The purpose of ‘Doodats’ is to provide generic solutions to the little problems, so
the programmer can spend his or her time working on the big problems (like what the
program does).
Doodat #1
Problem: how to determine if the user has tried to abort something by pressing
the Cmd-’.’ keys. EventAvail() returns only the first keystroke event in the queue,
and the user may have pressed some other keys since the last keystroke event was read
by the application. Either GetNextEvent() or WaitNextEvent() return all keystroke
events, forcing the application to store them or ignore them (a Bad Thing). Moreover,
if MultiFinder is running, calling any of these event-related traps may cause the
application to be switched out, which could cause problems.
Solution: Doodat #1 is a routine called check_abort(), which directly scans the
ToolBox’s internal event queue for Cmd-’.’ abort events.
The routine starts by setting a pointer to head of the event queue (stored in the
system global EventQueue), then examining the next event in the queue. If the event is
a Cmd-’.’ event, it is removed by calling Dequeue(), and the ‘found’ flag set. If the
pointer does not match the event queue tail, it is advanced to the next event, otherwise,
the function returns. This technique solves all of the problems posed by normal event
processing:
1. It doesn’t affect any other keystroke events in the queue.
2. If the user has generated multiple aborts (by holding the keys down), it removes
all of them.
3. It does not call EventAvail() or WaitNextEvent(), so no chance of being switched
in or out under MultiFinder.
To use check_abort(), call it from within any place where the user might want to
abort. For example:
/* 1 */
for (page = 1; page < last_page; page++)
{
/* print a page */
if (check_abort())
break;
}
/*************************************************
**
** check_abort() - Lightspeed C Version
**
** This routine returns scans the low-level
** event queue in search of a Cmd-’.’ key event
** Each one found is removed from the event
** queue.
**
** In: N/A
**
** Out: TRUE if Cmd-’.’ found, FALSE otherwise.
**
*/
check_abort()
{
EvQElPtr eq_p;
Boolean f_found = false;
/* start at head of internal queue */
eq_p = (EvQElPtr)(EventQueue.qHead);
while (true)
{
if ((eq_p->evtQWhat == keyDown ||
(eq_p->evtQWhat == autoKey) &&
(eq_p->evtQModifiers & cmdKey) &&
(eq_p->evtQMessage & charCodeMask) == ‘.’)
{
/* remove the event from the queue */
Dequeue((QElemPtr)eq_p, &EventQueue);
f_found = true;
}
/* test for end of queue */
if (eq_p == (EvQElPtr)EventQueue.qTail)
break;
/* continue with next queue entry */
eq_p = (EvQEl *)(eq_p->qLink);
}
return(f_found);
} /* end of check_abort() */
Doodat #2
Problem: Many windows contain items such as lines, icons, pictures, or text
strings that are there for decoration; these items don’t change in appearance, and
clicking on them has no effect. While the code to draw these items is relatively simple,
it is also very boring to write, and frequently quite lengthy. In addition, trying to
figure out exactly which horizontal and vertical co-ordinates to use can be
time-consuming and frustrating.
Solution: Doodat #2 is an attempt to save time (and code!) by making ResEdit and
the Resource Manager do most of the work. This involves a two-step approach:
Step 1 is to use ResEdit (or any other Resource tool) to create a ‘DITL’ resource
(Dialog Item List) that contains all of the items desired. ResEdit is particularly
handy, as the items can be arranged visually within the window. The DITL resource
may be created and edited normally, with all items being set to disabled. No Control or
EditText items should be included (they will be ignored if you do), and the items may
be in any order. The finished DITL resource is then included in the application’s
resources for later use.
UserItems in the item list are handled in one of two ways: either as a dividing line
or as frame. If the horizontal or vertical co-ordinates are equal, i.e. the boundary
rectangle is empty, it is drawn as a 50% gray line. If the rectangle is not empty, it is
drawn as a black frame.
Text items are drawn (left-justified) in whatever font the window’s port is
using when draw_ditl() is called. Different text characteristics could be supplied by
reserving the first few characters of the text stringbut that’s for another time.
Step 2 is to use the routine below called draw_ditl() to actually read in and draw
the items within the window. This routine accepts the ID number of a DITL resource,
reads the resource into memory, then traverses the item list, drawing each item
accordingly. For draw_ditl() to work properly, the DITL resource must be present
in the application’s resource fork (or in an open resource file), and the port set to the
desired window.
draw_ditl() uses the following definitions and structure:
/* 2 */
#define NIL 0L
/* Test a pointer for validity */
#define VALID_PTR(p) ((long)(p) &&
((long)(p) & 1L) == NIL)
/* Test a handle for validity */
#define VALID_HNDL(h) (VALID_PTR(h) &&
VALID_PTR((long)(*(h)) & 0x00FFFFFF))
typedef struct
{
Handle hndl;
Rect frame;
unsigned char type;
unsigned char length;
short data[];
} ditl_list;
/*************************************************
**
** draw_ditl() - Lightspeed C Version
**
** This routine reads in and draws the items in a
** Dialog Item List (DITL) resource.
**
** In: resource id of DITL
**
** Out: N/A
**
*/
void
draw_ditl(ditl_id)
short ditl_id;
{
Handle ditl_h;
ditl_list *ditl_p;
PenState save_pen;
short d_type, offset, num_ items;
ditl_h = GetResource(‘DITL’, ditl_id);
if (ResError() != noErr || !VALID_HNDL(ditl_h))
return;
HLock(ditl_h);
/* don’t change pen behind the window’s back */
GetPenState(&save_pen);
PenNormal();
/* get the number of items in list */
num_items = *((short *)(*ditl_h)) + 1;
/* set pointer to first item in list */
ditl_p = (ditl_list *)((*ditl_h) + sizeof(short));
while (num_ items--)
{
/* mask out the item disabled bit */
switch (ditl_p->type & 0x7F)
{
case statText:
TextBox((char *)ditl_p->data,
ditl_p->length, &(ditl_p->frame),
teJustLeft);
break;
case iconItem:
ditl_p->hndl =
GetIcon(ditl_p->data[0]);
if (VALID_HNDL(ditl_p->hndl))
{
PlotIcon(&(ditl_p->frame),
ditl_p->hndl);
ReleaseResource(ditl_p->hndl);
}
break;