New Toolbox
Volume Number: 6
Issue Number: 10
Column Tag: Assembly Lab
New Toolbox Routines 
By Hugues A. Oliver, Perpignan, France
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
[Hugues A. Oliver is a 23 year old student at Ecole Supérieure d’Ingénieur de
Marseille. He started programming Macintosh computers in 1986 and now works on a
Mac II.]
Every Macintosh programmer knows that the foundation of a Macintosh program
is the event loop, and the main component of a Macintosh application is windows. When
an event occurs, the Mac OS signals the application and sends the event’s type and some
other information in a structure called EventRecord to your program. Then, the
program takes whatever action is appropriate for the event’s type but, before, your
program must find for which opened window the event is related to.
My idea was to add for each opened windows an associated procedure which I’ve
called : Window Procedure. When an event occurs, the EventRecord is sent to the
window procedure which responds by taking action for each type of event. A pointer to
this procedure should be placed somewhere in the Window record (I will explain
where below) .Once you’ve intercept the event record a special procedure should get
our procedure pointer and call the window procedure. You must be aware that the
address that will be stored in your window record must always be a pointer to an
executable procedure. If the segment containing the procedure moves, you know what
will happen...
In every application, the window is used to contain a document. A document can be
a set of texts, lists (from the list manager) and graphics. Whenever you want to use in
your window a text, picture or list, the common technique is to declare in your source
code globals variables to keep track of the new added structure. To use the new
“object” , you also need to rewrite some parts of your source code.
If you want to write an application which could open a unlimited number of
windows with TEdit in it, for example, you can’t use this technique.
Some programmers use the WRefCon of the window to hold the TEHandle of the
text displayed in the window, but there is only one WrefCon field in the window
record...
A good way to use the WRefCon to hold as many variables as we want is to use
linked list and a new data structure, which will contain linked lists of
TEHandle,ListHandle or Picture Handle, the window procedure and some other
information.
I’ve called this new structure the Window AuxRecord.
The Window AuxRecord
The Window AuxRecord will be used to keep all the information the window
requires to operate on texts, lists or pictures. A handle to this AuxRecord will be
placed in the RefCon field of each new opened windows. The fields of the Aux
WindowRecord will be explained in the new toolbox calls section.
In order to use this new structure described above, I wrote some new routines.
The NewToolBox Library
Each of the new toolbox calls have been written in assembly language, using the
Consulair 68000 assembler.
The Think C text editor was used to write the source code. To build the
newToolBox library, you need to compile each source file then to convert them to the
Think C library format using the RelConv utility. Once you’ve compiled and converted
the object file, you create a new Think C project, you can name it ‘NewToolBox,’ and
then you add the libraries you’ve just created to your project.
Now, you compile your project using the Build Library option. The header file
associated with the library contains the prototype of each new added toolbox routines
To use this library: add it in your project and #include the header file where
needed.
The New ToolBox Calls
InstalWindow():
This procedure will open a new window on the Macintosh desktop. It first creates
space for a window record and then calls the GetNewWindow() function. WindID is the
ID of the Window template on the resource file. If the window template can’t be read,
InstalWindow() aborts and returns an error code.
Then, the function creates space for the Auxiliary Window record and replaces
the original WrefCon of the new created window with a handle to the AuxWindowRecord.
The content of WrefCon is saved and will be placed in the WauxRefCon of the
AuxWindowRecord.
All the fields of the new structure are initialized.
The clip region origin is set to (0,0). You can use this field to add scrolling
functionalities to your window.
The current active text and list are initialized with NIL, as well as the window
THPrint. If your application prints the window’s content, you will allocate space for a
TPrint record and place a handle to this record in the WPrintRec of the
AuxWindRecord.
The WvRefNum is cleared, if texts or lists use data from disk, the volume
reference number of the file containing the data will be placed in the WvRefNum field.
The WContRect is set with the window’s portRect minus the scroll bar size. If
your window has a size box, you will place true in the WhasGrow field, which will
allow the event loop to draw the size box if needed when an update event occurs.
The address of the Window procedure is placed in WindProc. Remember: your
window procedure should never move.
Space is also allocated for the text, list and picture linked list in the
WText,WList and WPic fields.
As a matter of fact,the linked list of text is a list of auxiliary text record :
The list auxiliary record is a bit more complicated, as you can see below :
Two fields are used to keep the font information needed to draw the content of each
list’s cells.
The picture auxiliary record holds the transfer mode for QuickDraw drawing
operation and the destination rectangle for the picture.
The InstalWindow() function will return the windowPtr in the variable
‘theWindow’.
If a memory allocation has failed, an error code is returned and no window is
opened.
ExecWindow():
Each time an event occurs concerning the window, you will call this procedure.
The procedure looks for the WindProc fields of the AuxWindowRecord and then
executes the procedure at the given address (assuming you’re never unloading and/or
re-loading the segment containing the Window procedure). Before, ExecWindow(),
places the EventRecord address on the stack.
GetWindowAuxRec():
Will return a handle to the windowAuxRecord.
GetWindowClip() and SetWindowClip():
Will return or set the WcontRect and Worigin of the window AuxRecord.
SetCurrents() and GetCurrents():
These procedures allows you to save and restore the values of the current active
text or list in your window.
DeInstalWindow():
The whole space allocated for text(s), list(s), pict(s) and TPrint for the given
window is released, and the window is closed.
I had new toolbox routines to provide the TextEdit and List Manager more
functionality and to make life easier for the programmer who wants to open, handle and
close texts and lists.
I have not forgotten Pictures and Controls. Each new PicHandle created when
reading a picture from resource file, will be placed in a linked list.
A procedure, which I call ControlProcedure, will be executed whenever the user
clicks in an active control. The procedure’s address is placed in the contrlRefCon.
InstalText():
Assuming a window has already been setup, this function will create a new text in
your window.
textId is the ID of a template which contains two rectangles: the viewRect and the
destination rectangle of the text.
Figure 5.
The inPort field of the TERecord is set with the whichWindow parameter.
Then, the new TEHandle is placed at the end of the WText list.
The function returns an error code before aborting if memory allocation fails or
if the template can’t be found or read.
FindText():
Mouse down events are sent to the Window Procedure by the event loop of your
application and by the ExecWindow procedure.
You’ll call the FindText function with the local coordinates of the mouse to know
if the mouse down occurs in a text (the function returns the TEHandle of the text) or
not (the function returns NIL).
This function simply calls PtInRect() for each texts of the WText list with the
text’s viewRect and the local coordinates of the mouse as parameters.
DrawTexts():
Redraws the text(s) belonging to whichWindow.
TEUpdate is called for every text of the window.
ActivateText():
Calls TEActivate for the current active text . The TEhandle of the active text is
store in the WactivTxt of the windowAuxRec.
If currentText is NIL, ActivateText does nothing.
DeactivateText():
Does the same as ActivateText() but TEDeactivate() is called instead of
TEActivate()
InstalList():
Creates a new list in whichWindow, the routine first looks for a template on the
resource file.
the template contains all the parameters needed by the LNew() routine. listID is
the resource number of the list template.
Space is also allocated for the ListAuxRec and all fields initialized.
fontList and sizList are the font and size used by the ListDefProcedure to draw the
cells content. These values are read from the List template.
If the list have controls (scrolls bar) , they are hidden (contrlVis field set to
zero) because the list is supposed to be inactive when the window is opened.
FindList():
The routine works exactly in the same way the Findtext() routine does. But,
before calling PtInRect(), if the list have scroll bar, 16 pixels are added to the list
vRect bottom or right point.
DrawLists():
Will redraw the list(s) of whichWindow.
If your list has never been drawn before, a call to LUpdate will always draw the
list’s scroll bars. (LUpdate sets the contrlVis of each scroll bars with 255). This will
be a little annoying if your window displays more than one list because every list of
the window seems to be active when your window will appear for the first time on the
desktop. In order to avoid this, the DrawLists() routine will hide the scroll bars
handle before calling LUpdate(). The scroll bars are drawn separately by calling the
Draw1Control() routine which draws the control only if necessary (if contrlVis is
255).
ActivateList():
the list manager routine LActivate() is called for the current active list with
TRUE as first parameter .
DeactivateLists():
LActivate() called with FALSE for the current active list (if any).
ListKey():
You will call this procedure whenever the event loop will report a keyDown event
for an active list.
Each character typed will be added to the active cell data and the cell redrawn.
The routine tests also for characters like Return, Enter or the arrow keys and
instead of adding the value to the cell data, changes the current active cell.
The Control Manager have also been enhanced with new routines:
InstalControl():
Will be used to create a new control from a template read from a resource file.
The control reference value is replaced with a procedure pointer . This procedure
will be executed each time you click in the control and, of course, will never remove
the segment containing the control procedure.
After the TrackControl function, you will call ExecControl() which call your
contrlProc.
HiliteAllControls():
Calls HiliteControl() for each control of the window control list, except for those
with the contrlRef field set to -1. This, to avoid the scroll bars of a list to be drawn if
the list is not active.
Drawing the list’s scrollbars is the DrawList() or ActivateList() job only.
Pictures can be added to every windows, using the InstalPic() function, which
adds picHandle to the list of pictures that belong to the window.An auxiliary picture
record is also added. It will contain the ID of the picture resource, the pen transfer
mode used to draw the picture and the destination rectangle (in local coordinates).
As DrawList and DrawTexts, DrawPicts() will draw every pictures of
whichWindow.
FindPict() will allow the user to know if he ‘clicks’ in the picture frame.
The Demo Project
The demo project, written in Think C v 4.00, demonstrates the use of the new
toolbox routines and shows a simple Window procedure.
Five different windows displaying texts, textual lists and pictures can be opened
together.
The Window procedure is in the “CExecWindow.c” file.This segment should never
be unloaded .
Conclusion
The window procedure presented here is implemented in a very simple way.A
more sophisticated approach would be to store the window procedure as a resource in a
resource file.
The window auxiliary record can also easily be modified to hold more ‘objects’,
not only texts, lists and pictures.
Continued in next frame