Managers for Sys 6
Volume Number: 4
Issue Number: 10
Column Tag: Forth Forum
The Manager
By Jörg Langowski, MacTutor Editorial Staff
Notification Manager, List Manager and UserItems
System updates are almost too frequent to keep track of. It has been only a couple
of months since I installed release 6.0; already there are a couple of bug fixes out. I
couldn’t get them as of this writing, but System 6.0 has behaved stable so far,
examples of ill-behaved applications are only very rare.
The scene keeps moving. As I write this, System 6.0.1 is almost released and 7.0
is being talked about; true to the old wisdom “if one version of the operating system
finally behaves, go install a new one”. Anyway, the example I am giving today explores
a new service added in System 6.0 under MultiFinder; the notification manager.
This is one of the little things - one might say - added on the way to ‘Real
Inter-Process Communication’, and it is responsible for the little printer icon you see
flashing with the Apple symbol in the menu bar when your background printer has a
problem, or the clock icon when your alarm clock goes off.
Actually, this month’s program deals with many different things, the notification
manager being only one of them. I wanted to create some useful utility that can keep
track of appointments and remind me if something’s coming up, just like the Reminder
DA in one of my recent columns. However, this time I wanted to be able to enter date
and time in a well-readable format, and also keep a file with the appointments that is
automatically read in on startup and updated on exit. It turned out that doing these
things in a nice way required some use of the list manager and a user item in a
modeless dialog; so the example will show you some of those things as well.
The Plan
The program consists of a modeless dialog window which displays a list of
appointments with their dates and corresponding messages. Selection, updating and
scrolling of the list is handled by the list manager. The dialog allows the user to edit a
list item, add and delete appointments, and exit the program.
As long as the dialog window is up, a background task (easily created under
Mach2) keeps doing the following things:
- Each time the list is changed (by adding/editing/ deleting things), it gets the time
and message of the next upcoming appointment from the list.
- When the system time exceeds the time of the upcoming appointment, a Notification
Manager (NM) request is set up. The NM then displays an alert message with the
appointment message. After the message, the program gets the next appointment
from the list.
- When the main dialog window is closed, the background task writes the
appointment list back to the file and terminates the program.
Figure 1. Main dialog window of this month’s example
Implementation
First of all, we need to create a modeless dialog window that contains a list. Since
that is not a standard dialog item, it has to be implemented as a user item. Just as a
reminder: a user item is anything in a dialog that has to be drawn using our own
procedure when the dialog is updated. A pointer to the drawing procedure is passed to
the dialog manager by calling SetDItem, as described in TN34:
First call:
GetDItem (DialogPtr, itemNo, type, item, box);
check if necessary whether type is really the type of a userItem, and then call
SetDItem (DialogPtr, itemNo, type, @drawProc, box);
where @drawProc is a pointer to the user-supplied draw procedure. Note that it is
really a pointer that is passed here, not a handle like in the case of other dialog items.
DialogPtr, of course, is the pointer to the dialog window, where the item itemNo has
been defined as a userItem, and box is the rectangle where the item will be drawn.
We can install a user item procedure from Mach2, but, if we just passed any
Mach2 routine’s pointer to the SetDItem trap, the machine would crash gloriously
when trying to draw the item, because the procedure is not called from the Mach2
context, but through the dialog manager. This means, glue code has to be
written-something you should be used to by now.
In the example, the routine gUser provides this service, setting up space on the
stack for the local Forth and DO...LOOP stacks, saving all the registers, and moving the
dialog pointer and item numbers on the Forth stack. It then calls the UserDraw
procedure and cleans up after it has returned.
List Manager
Our user item will contain a list. How do we handle it? IM Vol.IV describes the
List Manager routines, and using those the job becomes pretty easy. I am not going to
review the List Manager in detail; IM gives a pretty good description, and we also had
an article last year describing the list manager (V3#4).
For the drawing procedure, all we need is a call to LUpdate, which redraws the
list. This is implemented in UserDraw (see Listing). However, here we assume the list
has already been setup. This is done at the start of the program, calling
installUserDraw which calls MakeList. The latter routine (taken from Palo Alto
Shipping’s ListManager example) creates a list in a given rectangle and window with
vertical and horizontal cell size given in pixels. Looking at the example you will also
notice that our list has a vertical scroll bar; the list manager handles scrolling
automatically. Note that the list rectangle was inset by one pixel with respect to the
user item rectangle, except for the right side where the scroll bar appears, where it
had to be inset by 16 pixels (the scroll bar is drawn outside the list). After the list is
created, its handle is stored in a variable for further use.
We now need a handler for mouse down events in the user item; and also routines
that will allow us to edit, add and delete list items when the appropriate buttons in the
modeless dialog are pushed. The words userList- handler, userEdit, userAdd, and
userDelete are provided for this purpose.
Mouse down events in the user item are handled by userList- handler. It calls
LClick with the local mouse position, the modifiers word of the event record and the
list handle. (Note that event-record is a Mach2 system variable which points to the
latest event record). LClick handles selection of list elements, highlighting, scrolling,
and double clicks. If a list item is double-clicked, the userEdit procedure is called;
otherwise, the routine just returns, with the list selection changed.
userEdit and userAdd set up a modal dialog which allow a date and text to be
entered into a message field. While userAdd adds a new field at the end of the list,
userEdit takes the current selection, puts the date and message into the modal dialog,
and lets the user edit it. getMsg is the routine that parses the message string into its
date and message fields and displays the edit dialog. update-cell writes the current
date/message string into the selected cell. userDelete simply deletes the currently
selected cell.
A number of things could be improved here: mainly, I did not implement
automatic sorting by date when a list item is added or edited. This is straightforward
and would require one or two more pages of code; since it is not really essential for the
operation, I’m leaving it as an exercise for you.
The list is initially filled from a file called ‘dates’ which has to be in the same
folder as the application. If it doesn’t exists, it will be created. dates contains the
date/message strings, separated by carriage returns, and can be edited like any text
file. fill-list opens - or creates - the file, reads the strings and adds rows to the list.
At the end of the program, write-list writes the list back to the file. This way, by
making the program one of your Multifinder startup applications, you will be
automatically reminded of any upcoming appointment that was entered into the list.
Modeless Dialog Handling in Mach2
What has to be done to handle modeless dialogs correctly in a multitasking
environment like Mach2? Since events are handled for us by the I-O task, we need not
- and should not - call WaitNextEvent, IsDialogEvent, or DialogSelect from our
program. This has already been done for us. All we need to do to connect a modeless
dialog window with one particular task is install the task pointer in the dialog
window’s refCon field. The I-O task will look at the front window’s refCon and pass the
event information to the owning task. In the case of a dialog event it will put the dialog
pointer and the item number into the user variables DialogHandle and DialogData
(offsets 140 and 136). When data is stored there, the owning task will run the
modeless dialog handling routine whose pointer is stored in the user variable
modelessVector (offset 132). This routine gets passed the item number and the dialog
pointer. Our program uses the word dialog- handler to call the handlers for the