TransSkel 2.0 Manual

TransSkel
 A Transportable Application Skeleton 

Version 2.0
9 February 1989

Introduction

This document describes TransSkel, a transportable Macintosh application 
skeleton. Several skeleton programs for the Macintosh already exist, 
e.g., the original Skel for Lisa Pascal (Steve Maker), and translations 
of the same for other compilers, such as TML Pascal (Vic Abell), MegaMax 
C (Bill Jefferys), and LightspeedC (Michael Peirce). These take the form 
of application frameworks that support a window and two menus, and 
handle a number of standard operations such as window dragging and menu 
item selection. Each provides a straightforward example of Macintosh 
programming and serves as a valuable learning tool. However, to use any 
of them for different applications, the skeleton must be modified and 
recompiled.

The philosophy of TransSkel is somewhat different: to isolate into a 
single place code that remains relatively stereotypical from application 
to application, while requiring the host program to supply application-
specific code. Although this concept is entirely unoriginal, it allows 
TransSkel to take the form of a library program that may be compiled 
once and left alone--in essence, a modular, plug-in skeleton. TransSkel 
is sufficiently functional to be transportable without modification 
across many different applications. For instance, TransSkel allows an 
arbitrary number of windows or menus--it doesn't care whether you have 
none, one, or a hundred. It recognizes and initializes blank disks--no 
more dead drives. If the host wishes, TransSkel takes care of the Apple 
menu automatically.


Distribution Information

TransSkel is public domain, so distribution is unrestricted. The 
software is free and is provided "as is," without any warranty express 
or implied. Paul DuBois is the original author of TransSkel for 
LightspeedC. Owen Hartnett did the port from LightspeedC to Lightspeed 
Pascal and is responsible for most of the improvements between versions 
1.02 and 2.0. Additions or corrections may be sent to one of the 
addresses below (questions about the structure, content and philosophy 
of TransSkel should be sent to Paul DuBois, questions specific to the 
Pascal version should be sent to Owen Hartnett).

U.S. Mail:	Paul DuBois
	Wisconsin Regional Primate Research Center
	1220 Capitol Court
	Madison, WI 53715-1299 USA

Electronic mail:	UUCP:	{allegra, uunet}!uwvax!rhesus!dubois
	Internet:	dubois@primate.wisc.edu

U.S. Mail:	Owen Hartnett
	OHM Software Company
	163 Richard Drive
	Tiverton, RI 02878 USA

Electronic mail:	Brown University Computer Science
	UUCP:	{allegra, uunet}!brunix!omh
	Internet:	omh@cs.brown.edu
	CSNET:	omh@cs.brown.edu.CSNET

Support for zoom boxes and modeless dialogs was provided by David W. 
Berry (well!dwb@lll-lcc.arpa).

The version of TransSkel described in this document is written for 
LightspeedC. LightspeedC is a trademark of:

THINK Technologies, Inc.
420 Bedford Street Suite 350
Lexington, MA 02173 USA


The full TransSkel distribution consists of:

The document "TransSkel--A Transportable Application Skeleton"
The document "TransSkel 2.0 Quick Reference"
TransSkel.c - TransSkel source code
TSHistory.c - change history
Demonstration programs
	MiniSkel: minimal application
		MiniSkel.c - source
	Skel: simulation of traditional Skel
		Skel.c - source
		Skel.proj.rsrc - resources
	MultiSkel: multiple-window application--fixed number of windows
		MultiSkel.h,
		MultiSkel.c,
		MSkelEdit.c,
		MSkelHelp.c,
		MSkelRgn.c,
		MSkelZoom.c - sources
		MultiSkel.proj.rsrc - resources
	ManyWind: application with variable number of menus and windows
		ManyWind.c - source
	DialogSkel: application using modeless dialogs
		DialogSkel.c - source
		DialogSkel.proj.rsrc - resources


Version Notes

Applications created with previous versions of TransSkel are not 
compatible with version 2.0, which differs from earlier versions in the 
following respects.

*	Better handling of out of memory conditions. All TransSkel calls 
	which allocate space on the heap now return a value to indicate 
	success or failure. Desk accessories now make sure they have at 
	least the minimal amount of memory before opening.
*	There is an explicit model of port-setting behavior, discussed in 
	more detail under the description of the SkelWindow function. Many 
	extraneous SetPort calls have been deleted.
*	SkelInit has become much smarter now. It will call MoreMasters for 
	you a specified number of times, and optionally install a user-
	supplied GrowZone function.
*	SkelMenu now has a parameter which allows drawing of the menu bar 
	to be delayed. This eliminates the annoying redrawing of the bar 
	after each menu is added.
*	The mechanism that determines the window to which events should be 
	routed has been much improved. If you had many windows, for each 
	event, TransSkel would do an exhaustive search of its internal 
	window list. Windows at the end of the list required a search of 
	the entire list. There is now a cache, based on the presumption 
	that the last window to receive an event is the window most likely 
	to get the next event. This window is checked first before 
	searching the list. The overhead for this is more than compensated 
	by the savings in list traversal.

The remainder of this document describes the demonstration programs 
included in the distribution and provides a detailed specification of 
the TransSkel interface.


Demonstration Programs 

The demonstrations are an introduction to the kinds of applications for 
which TransSkel can be used. The programs differ in emphasis and 
complexity:
 (a) a trivial application that does nothing but support desk 
     accessories
 (b) a simulation of the traditional Skel
 (c) a simple multiple window application for a fixed number of windows
 (d) an application with either 2 or 4 menus and with anywhere from 0 to 
     20 windows
 (e) an application demonstrating use of modeless dialogs.


MiniSkel

This demonstration puts up an Apple menu with desk acessories in it, and 
a File menu with a Quit item. Desk accessories may be run as usual. 
Terminate the application by selecting Quit from the File menu, or by 
typing command-Q. The source illustrates in distilled form the general 
way in which a host uses TransSkel.

Skel

This demonstration mimics the traditional Skel program: one sizable, 
draggable, non-closable dark gray window, Apple and File menus, two 
dialog boxes, support for desk accessories. The simulation is inexact, 
though close enough to be recognizable.

MultiSkel

This demonstration is a simple multiple-window application. There are 
two text windows (one manipulable, the other not), and two graphics 
windows (one manipulable, the other not). MultiSkel shows how to use 
TransSkel to support some of the basic Macintosh programming techniques: 
graphics, scroll bar use, region manipulation, shift-click and double-
click detection, text editing and use of the clipboard.

All four windows may be resized, dragged, closed and opened. If any 
window is resized, its display adjusts itself to the window's new shape. 
Use the close box in a window to close it, or select Close from the File 
menu. To make hidden windows visible, select Open from the File menu.

Help Window
This window describes MultiSkel via a scrolling text display.

Edit Window
This window allows text editing. The Edit menu is functional for this 
window, except Undo. The clipboard is supported. Scrolling is not.

Zoom Window
This window presents a series of randomly selected rectangles, each 
smoothly interpolated into the next. The display pauses if the mouse 
button is held down in the window.

Region Window
If the mouse is clicked and dragged in this window, a gray selection 
rectangle is drawn. Rectangles drawn this way are combined into a 
region, the outline of which is drawn as a marquee. Rectangles drawn 
with the shift key down are subtracted from the region. Clear the window 
by double-clicking.

ManyWind

This application demonstrates dynamic object handler creation and 
disposal. Initially there are two menus and no windows. The Apple menu 
operates as usual. The File menu allows new windows to be created with 
the New item. Up to 20 windows may exist at once; after that, the New 
item is dimmed until some window is destroyed. Whenever any windows are 
present, a Windows menu and a Color menu are also present. The Windows 
menu contains one item for each visible window. Selecting an item brings 
the corresponding window to the front. The Color menu may be used to 
change the background color of the frontmost window. Clicking the close 
box of a window destroys it and removes it from the Windows menu. If all 
the windows are closed, the Windows and Color menus are destroyed as 
well until another window is created.

Note
The limit of 20 windows is arbitrary--that's how many items fit into a 
standard (i.e., non-scrolling) menu on a Macintosh. TransSkel itself has 
no such limitation.

DialogSkel

This application supports two modeless dialogs, each of which is used to 
influence the appearance of the other.


When Quit is selected from the File menu of any of the demonstrations, 
menus and windows are disposed of automatically.


The TransSkel Interface--General Information

TransSkel.c contains the source of the TransSkel module. It can be made 
into a project for inclusion in the host application project, or the 
source can be included directly in the host project. TransSkel compiles 
to different amounts of code, depending on whether it is compiled to 
support modeless dialogs or not. Dialog support can be enabled or 
disabled through the use of #define or #undef in the source. (TransSkel 
may be compiled with dialog support turned off for all of the 
demonstrations except DialogSkel.) You may wish simply to compile two 
projects, one with dialog support turned on, the other with it turned 
off.

Host applications interact with TransSkel entirely through procedure and 
function calls. There are no extern variables or #include files. The 
available calls are:

SkelInit		Initialize TransSkel
SkelApple		Install Apple menu handler
SkelMenu		Install menu handler (returns success/failure)
SkelRmveMenu		Remove menu handler
SkelWindow		Install window handler (returns success/failure)
SkelRmveWind		Remove window handler
SkelDialog		Install modeless dialog handler (returns 
			success/failure)
SkelRmveDlog		Remove modeless dialog handler
SkelMain		Event processing loop
SkelWhoa		Terminate SkelMain
SkelClobber		Handler clean-up
SkelGrowBounds		Set sizing limits for window
SkelBackground		Install background procedure
SkelGetBackground	Retrieve background procedure
SkelEventHook		Install event-inspecting function
SkelGetEventHook	Retrieve event-inspecting function
SkelEventMask		Specify event types applicable to SkelMain
SkelGetEventMask	Return event types applicable to SkelMain
SkelDlogMask		Specify event types applicable to dialogs
SkelGetDlogMask		Specify event types applicable to dialogs

All other variables and procedures are declared static, precluding name 
conflicts with the host.

The general logic of host applications is:

(I)   Tell TransSkel to initialize itself.
(ii)  Create window and menu objects, tell TransSkel to install handlers 
      for them.
(iii) Execute TransSkel 's main loop, which routes events to the proper 
      object handlers automatically.
(iv)  When event processing terminates, tell TransSkel to dispose of 
      object handlers.

Here is a trivial application, supporting desk accessories and a File 
menu with a Quit item. There are no window handlers. (This is similar to 
the source for MiniSkel.)

# include  <MenuMgr.h>
# define  nil   (0L)

main ()
{
MenuHandle m;
Integer   DoFileMenu ();

  SkelInit (6, nil);              /* initialize */
  SkelApple (nil, nil);           /* handle desk accessories */
  m = NewMenu (2, "\pFile");      /* create menu and install */
  AppendMenu (m, "\pQuit/Q");     /* trivial handler */
  (void) SkelMenu (m, DoFileMenu, nil, true);
  SkelMain ();                    /* loop 'til Quit selected */
  SkelClobber ();                 /* clean up */
}

DoFileMenu (item)
int   item;
{
  SkelWhoa ();                    /* tell SkelMain to quit */
}

TransSkel 's SkelMain procedure causes DoFileMenu to be called when Quit 
is selected from the File menu. DoFileMenu calls SkelWhoa, which in turn 
causes SkelMain to terminate and return control to the host, which calls 
SkelClobber to clean up, and exits.

More complex applications are an elaboration of the same theme: the host 
creates windows and menus, and tells TransSkel which procedures should 
be called when certain things happen to them. The collection of 
procedures attached to an object constitutes an object handler. 
TransSkel manages the routing of events to the proper handler procedures 
automatically. The host often need not even know such things as event 
records exist.


The TransSkel Interface--Procedural Specification

Each of the TransSkel interface routines is described in more detail 
below.

Note
The value of nil is understood to be equal to 0L (long zero). The 
Integer and Longint types below are understood to be typedef'ed to the 
two- and four-byte integer types of the C compiler used to compile 
TransSkel. For LightspeedC, these are int and long.

Warning
Many of the routines below take procedure addresses as parameters. All 
procedures passed to TransSkel routines should be C procedures. Do not 
pass addresses of ToolBox routines unless you have a predilection for 
bomb boxes.


SkelInit (noMasters, myGrowZone)
Integer	noMasters;
ProcPtr	myGrowZone;

The host application should call SkelInit before it does anything else. 
This initializes the various Macintosh managers and some internal 
variables.

noMasters is the number of times MoreMasters should be called (Knaster 
recommends at least six times).

myGrowZone is either a ProcPtr to a user-written GrowZone routine or nil 
if no GrowZone function should be installed.


SkelMenu (theMenu, pSelect, pClobber, drawBar)
MenuHandle theMenu;
ProcPtr   pSelect;
ProcPtr   pClobber;
Boolean	 drawBar;

SkelMenu installs a menu handler for theMenu. The host should already 
have created theMenu via GetMenu or NewMenu/AppendMenu. pSelect is the 
address of the procedure to call when items are selected from theMenu. 
The item number of the selection is passed to the pSelect procedure, 
which should be declared to take one parameter.

	MyMenuProc (item)
	Integer item;

SkelMenu allocates memory for the handler and returns non-zero if it 
succeeded. If SkelMenu returns zero, it failed.

If pSelect is nil, theMenu is installed but selecting items from it has 
no effect. This is useful for initial testing.

pClobber is the address of the procedure to call to take care of 
disposing of the menu. This can be nil for any menu that persists 
throughout program execution. Handlers for temporary menus should 
include a disposal procedure. The pClobber procedure should be declared 
to take one parameter, a handle to the menu to be disposed of.

	MyMenuClobber (theMenu)
	MenuHandle theMenu;

Note
The host should never call the pClobber procedure itself. Let 
SkelClobber or SkelRmveMenu do it.

If the host calls SkelMenu to install a handler for a menu for which a 
handler already exists, the previous handler is removed (without 
disposing of the menu itself). This is functionally equivalent to 
modification of existing handlers.

drawBar indicates whether the menu bar should be drawn after the menu is 
installed. Typically the host installs all menus but the last with a 
drawBar value of false, to avoid unnecessary redrawing.


SkelRmveMenu (theMenu)
MenuHandle theMenu;

SkelRmveMenu removes the menu handler for theMenu. It removes the menu 
from the menu bar (which is redrawn) and calls the handler's disposal 
procedure, if one was specified when the handler was created.

SkelRmveMenu can be used to dynamically alter the set of menus appearing 
in the menu bar.

Note
It is permissible for menu handler selection procedures to call 
SkelRmveMenu. Handlers can remove themselves this way.

Menu handlers can also be removed by window handler procedures.


SkelApple (aboutTitle, aboutProc)
StringPtr  aboutTitle;
ProcPtr   aboutProc;

The Apple menu and the way it is processed are generally highly 
stereotypical: a first item that says something like "About", then a 
gray line, then all the desk accessories. Selecting the "About" item 
results in a display giving some information about the program. 
Selecting a desk accessory causes it to be opened.

If your application has an Apple menu that fits this description, call 
SkelApple to install one. The call should supply the title of the 
"About..." item as a Pascal string (e.g., "\pAbout MyProgram...") and 
the procedure to execute when the item is selected. If aboutTitle is 
nil, only desk accessories appear in the menu. If a title is supplied 
but aboutProc is nil, the "About..." item appears in the Apple menu but 
selecting it has no effect. The procedure passed as aboutProc should 
take no parameters.

SkelApple creates its own menu; you do not need to supply one. It uses a 
menu ID of 1, so the host should not use that ID for any of its other 
menus. If the Apple menu is to be handled in a non-standard way, use 
SkelMenu to install your own Apple menu.

SkelApple calls SkelMenu to install the Apple menu but returns no value. 
It is assumed that SkelApple will be called so early in the application 
that it is virtually certain to succeed, and that if it doesn't, there 
is no hope for the application anyway.

Note
SkelApple and SkelMenu install menus at the end of the menu bar; the 
host should not call InsertMenu. SkelRmveMenu deletes the menu from the 
menu bar; the host should not call DeleteMenu. SkelRmveMenu redraws the 
menu bar; the host does not need to. SkelMenu draws the menu bar as 
instructed. SkelApple does not draw the menu bar; if the Apple menu is 
the only one, then a call to DrawMenuBar is necessary to make the menu 
show up.


SkelWindow (theWind, pMouse, pKey, pUpdate, pActivate, pClose,
            pClobber, pIdle, frontOnly)
WindowPtr  theWind;
ProcPtr    pMouse, pKey, pUpdate, pActivate, pClose,
           pClobber, pIdle;
Boolean    frontOnly;

Zillions of parameters! Ugh!

SkelWindow installs a handler for theWind and makes its GrafPort the 
current port. The host should already have created the window via 
NewWindow or GetNewWindow. Most of the other parameters are addresses of 
procedures that are called to handle events in the window (pMouse, pKey, 
pUpdate, pActivate, pClose) or program execution conditions (pClobber, 
pIdle). If the handler doesn't need a particular procedure, pass nil for 
the corresponding parameter. For example, if the handler doesn't process 
key clicks, pass nil as the value of pKey.

SkelWindow allocates memory for the handler and returns non-zero if it 
succeeded. If SkelWindow returns zero, it failed, and the current port 
remains unchanged.


theWind	The window to be handled.

pMouse
	A pointer to the procedure to execute when theWind is the 
	active window and the mouse is clicked in its content 
	region. The procedure should be declared to take three 
	parameters.

		MyMouse (thePoint, theTime, theMods)
		Point  thePoint;
		Longint theTime;
		Integer theMods;

	The location of the mouse click is passed in thePoint, in 
	coordinates local to theWind. The time of the click is passed in 
	theTime, and the modifier flags word of the mouse click event 
	record is passed in theMods. The time of the click can be used for 
	double-click detection, while theMods can be used to detect shift-
	click, option-click, etc.

pKey
	A pointer to the procedure to execute to handle key clicks when 
	theWind is the active window. The procedure should be declared to 
	take two parameters.

		MyKey (theChar, theMods)
		char  theChar;
		Integer theMods;

	The character code is passed in theChar and the modifier flags
	word of the key event record is passed in theMods.

	Note
	Since key clicks are routed to the pKey procedure of the active 
	window, key clicks are thrown away if no windows are visible. If 
	this is a problem, install an event-inspecting hook with 
	SkelEventHook.

pUpdate
	A pointer to the procedure that draws the content region of 
	theWind. Calls to this procedure are bracketed by calls to 
	BeginUpdate and EndUpdate, so that drawing shows on the screen 
	only in the region that actually needs updating (see the Window 
	Manager Manual of Inside Macintosh). The procedure should be 
	declared to take one parameter.

		MyUpdate (resized)
		Boolean resized;

	The resized parameter of the update procedure indicates to the 
	host program whether theWind has been resized. The convention for 
	such notification is this: if the mouse is clicked in the grow box 
	or zoom box of the active window, TransSkel resizes it 
	automatically and invalidates the entire window port to cause an 
	update event to be generated. When that event occurs, TransSkel 
	passes a value of true to the update procedure. The host may take 
	whatever action is appropriate to respond to a resizing. Since the 
	entire port is invalidated, drawing shows up in the whole window.

	Note
	If the host fiddles with the clipping region, it should take care 
	to adjust it properly whenever the window is resized.

pActivate
	A pointer to the procedure to execute when the window is activated 
	or deactivated. The procedure should be declared to take one 
	parameter.

		MyActivate (active)
		Boolean active;

	The parameter has a value of true if the window is coming active, 
	false if it is going inactive. The host program should draw the 
	grow box if the window has one and take whatever other steps are 
	appropriate, such as enabling or disabling of menus or menu items, 
	control or text highlighting.

pClose
	A pointer to the procedure to execute when the mouse is clicked in 
	the window's close box. If the window has a close box but the 
	handler has a nil pClose procedure, the window is simply hidden 
	(not disposed of). In that case, the host should probably provide 
	a method for reopening the window (such as a menu selection).

	pClose is useful for writing handlers for temporary windows that 
	are thrown away when the window is closed. The pClose procedure 
	can call SkelRmveWind to cause the window to be disposed of and 
	its handler removed.

	The pClose procedure should take no parameters.

pClobber
	A pointer to the procedure to execute to dispose of theWind and 
	any associated data structures that may have been created at the 
	same time as the window (e.g., controls, TextEdit records). The 
	procedure should take no parameters.

	Note
	The host should never call the pClobber procedure itself. Let 
	SkelClobber or SkelRmveWind do it.

pIdle 
	A pointer to the procedure to execute when there are no 
	events pending. Essentially, this is the "no-event" handler. For 
	example, pIdle procedures for TextEdit window handlers usually 
	call TEIdle. Window handlers that change the cursor shape 
	depending on the current mouse location would do so here.

	The pIdle procedure should take no parameters.

frontOnly 
	If this parameter is true, the handler's pIdle procedure is called 
	only when theWind is active (frontmost). Otherwise it is called 
	whenever there are no events pending, whether it is active or not 
	(and whether it is visible or not; pIdle procedures that should 
	execute only when the window is visible must themselves check 
	whether that is so).


The Port-Setting Model

Window-handlers in versions of TransSkel prior to version 2.0 acted to 
ensure that the port would always be set to the window for which an 
event occurred by setting the port to that window before calling any of 
the handler procedures. This resulted in an incongruence with the user's 
perception of the way an application works: most actions are performed 
in the active (frontmost) window, and those that aren't are usually such 
as to bring another window forward. From the user's point of view, the 
port is changed by clicking in a window to bring it to the front. This 
implies that persistent changes to the current port are made only when a 
different window becomes active, and that all other port changes take 
place locally (the current port is saved before and restored after the 
local port change). TransSkel 2.0 now has an explicit port-setting 
model, described below. Thanks go to Duane Williams for pointing out 
deficiencies of previous versions of TransSkel in this regard.

The current port is set by TransSkel to the last window activated or 
created. This often means the programmer need do no SetPort calls at 
all, since most events are directed toward the active window.

Key clicks are directed by TransSkel to the frontmost window anyway. 
Mouse clicks in the content region of a window are passed to the click 
handler only if the window is frontmost; otherwise the window is brought 
to the front (resulting in an activate for that window) and the click is 
discarded. Clicks in the close box, the grow region or the zoom box of a 
window can only occur when that window is frontmost (active), thus the 
port will already have been set to that window.

Update events can occur for any window. Thus, TransSkel saves the port, 
sets the port to the window needing updating, calls the update handler, 
and restores the port. Clobber and idle procedures may also be executed 
for any window and the port is saved, set and restored similarly.

Handling deactivate events is problematic. In the normal case, a 
deactivate on a window will occur only after the window has been 
activated, and the port will have already been set to it. This would 
seem to imply that the port
need not be set for deactivates. But consider the situation where an 
application puts up window A, then window B in front of window A, then 
starts processing events. The first event will be a deactivate for A, 
and there will have been no corresponding activate to set the port to A. 
One could presumably require the programmer to set the port to A before 
starting to process events, but it appears more convenient simply to set 
the port before deactivates as well. Perhaps this is a barbarism. At any 
rate, setting the port apparently has no undue effects. If the 
deactivated window is not the last window, an activate will be pending 
and will cause the port to be set to the newly active window. If there's 
no pending activate, the deactivated window is the last and it doesn't 
matter where the port is set.

It is conceded that setting the port to any newly-created window does 
not strictly follow from either the user's perception of port-setting, 
or from the local-changes-only convention. However, our experience shows 
that typically when a window is created and its handler installed, other 
initialization is performed on the window (setting font size or face, 
installing controls, etc.) and it is convenient to find the port already 
set. In any case, should the host application wish to override this 
behavior, the port can be saved before and restored after the call to 
SkelWindow.

The model will fail under the following circumstances:

*	Window A is created and activated (port becomes A)
*	Window B is created but is not visible, i.e., it will be used 
	later (port becomes B)
*	User draws into A, the active window. The port, however, 
	is set to B.

Under conditions such as these, the creation of B and its initialization 
should be bracketed by a pair of GetPort/SetPort calls. It is felt that 
these circumstances are sufficiently unlikely to occur that the burden 
on the programmer is less when the port is set by SkelWindow than when 
it is not.

Treatment of modeless dialogs is similar; when a dialog becomes active, 
the port is set to it. Currently this is handled differently in the 
Pascal version of TransSkel.

Note
If the host calls SkelWindow to install a handler for a window for which 
a handler already exists, the previous handler is removed (without 
disposing of the window itself). This is functionally equivalent to 
modification of existing handlers.


SkelRmveWind (theWind)
WindowPtr  theWind;

SkelRmveWind removes the window handler for theWind. It calls the 
handler's pClobber procedure, if one was specified when the handler was 
created.

SkelRmveWind can be used to dynamically alter the set of available 
windows.

Note
It is dangerous for pIdle procedures to call SkelRmveWind to remove 
handlers for windows other than the one for which they are called. 
Otherwise, it is permissible for any window handler procedure except 
pClobber to call SkelRmveWind. Handlers can remove themselves this way. 
(pClobber is called by SkelRmveWind; it does not make sense for the 
latter to be called by the former.)

Window handlers can also be removed by menu handler procedures.


SkelDialog  (theDialog, pEvent, pClose, pClobber)
DialogPtr   theDialog;
ProcPtr     pEvent, pClose, pClobber;

SkelDialog installs a handler for theDialog and makes its GrafPort the 
current port. The host should already have created the dialog via 
NewDialog or GetNewDialog. The other parameters are addresses of 
procedures that are called to handle events in the dialog (pEvent, 
pClose) or program execution conditions (pClobber). If the handler 
doesn't need a particular procedure, pass nil for the corresponding 
parameter.

SkelDialog allocates memory for the handler and returns non-zero if it 
succeeded. If SkelDialog returns zero, it failed, and the current port 
remains unchaned.

theDialog
	The dialog to be handled.

pEvent
	A pointer to the procedure to execute to handle events in the 
	dialog. This will be called after the usual 
	IsDialogEvent/DialogSelect sequence is performed. The procedure 
	should be declared to take two parameters.

			MyEvent (theItem, theEvent)
			Integer   theItem;
			EventRecord *theEvent;

	The event to be handled is passed in theEvent, and theItem is the 
	item to which the event applies.

	Note
	IsDialogEvent considers any event at all to be a dialog event when 
	a modeless dialog is active. Events such as disk-inserts, however, 
	are not usually related to the dialog. Because of this, TransSkel 
	only processes update, activate, key, mouse and null events for 
	dialogs. The set of events that should be passed to dialogs may be 
	changed with SkelDlogMask.

	Command-key equivalents are handled properly when a modeless 
	dialog is active.

pClose
	A pointer to the procedure to execute when the mouse is clicked in 
	the dialog's close box. If the dialog has a close box but the 
	handler has a nil pClose procedure, the dialog is simply hidden 
	(not disposed of). In that case, the host should probably provide 
	a method for reopening the dialog.

	pClose is useful for writing handlers for temporary dialogs that 
	are thrown away when the dialog is closed. The pClose procedure 
	can call SkelRmveDlog to cause the dialog to be disposed of and 
	its handler to be removed.

	The pClose procedure should take no parameters.

pClobber
	A pointer to the procedure to execute to dispose of theDialog and 
	any associated data structures that may have been created at the 
	same time as the dialog. The procedure should take no parameters.

	Note
	The host should never call the pClobber procedure itself. Let 
	SkelClobber or SkelRmveDlog do it.

	If the host calls SkelDialog to install a handler for a dialog for 
	which a handler already exists, the previous handler is removed 
	(without disposing of the dialog itself). This is functionally 
	equivalent to modification of existing handlers.


SkelRmveDlog  (theDialog)
DialogPtr     theDialog;

SkelRmveDlog removes the dialog handler for theDialog. It calls the 
handler's pClobber procedure, if one was specified when the handler was 
created.

SkelRmveDialog can be used to dynamically alter the set of available 
dialogs.


SkelMain ()

The host calls this procedure to process events, after installing 
appropriate handlers. SkelMain either takes care of events itself, or 
routes them to the appropriate handlers. When there are no events 
pending, SkelMain polls window handler pIdle procedures as appropriate. 
When the host wants SkelMain to quit, it calls SkelWhoa.

SkelMain performs the following actions itself, without routing them to 
host-installed handlers:

*	Window dragging, if the window can be dragged.
*	Window sizing, if the window has a grow box or zoom box. The host 
	program detects when the window has been resized according to the 
	convention described above under discussion of SkelWindow's 
	pUpdate parameter.
*	If the mouse is clicked in the content region of an inactive 
	window, that window is brought to the front. (Window-activating 
	clicks are not passed to mouse-click handler procedures.)
*	Command-key equivalents for menu items are processed.
*	Disk-insert events for uninitialized disks are handled by allowing 
	the user to eject or initialize the disk. You don't end up with a 
	dead drive due to a program throwing such an event away.

If the host wishes to inspect events before TransSkel processes them, a 
hook function should be installed with SkelEventHook.


SkelWhoa ()

The host calls this procedure to tell SkelMain to terminate, generally 
from within a handler procedure. The application may then call 
SkelClobber and exit.


SkelClobber ()

When SkelMain returns, the host should call SkelClobber to shut down all 
window, dialog and menu handlers. The host generally exits when 
SkelClobber returns.


SkelGrowBounds (theWind, hLo, vLo, hHi, vHi)
WindowPtr  theWind;
Integer   hLo, vLo, hHi, vHi;

SkelGrowBounds tells the handler for theWind what the lower and upper 
limits on window sizing should be. The lower limits horizonally and 
vertically are given by hLo and vLo. The upper limits are given by hHi 
and vHi.

Grow limits for a window are initialized by SkelWindow when it creates 
the window's handler. To reset these general defaults (as opposed to the 
defaults for a particular window), pass nil as the value of theWind. The 
initial defaults are 80 pixels in either direction for the lower limits. 
The default upper limits are the dimensions of the screen (minus the 
menu bar), and are therefore machine dependent.


SkelEventMask (mask)
Integer mask;

SkelEventMask is used to specify the types of events that are requested 
for processing in SkelMain. The mask is constructed by or-ing together 
the event masks used to specify individual event types.


SkelGetEventMask (mask)
Integer *mask;

SkelGetEventMask returns the current event mask.


SkelBackground (p)
ProcPtr p;

SkelBackground installs a background procedure to be run by SkelMain 
every time it goes through its event processing loop. It is called 
before an event is requested, and is called whether or not an event is 
available. To turn the background procedure off, pass a value of nil. 
There is no background procedure initially.


SkelGetBackground (p)
ProcPtr *p;

SkelGetBackground returns the current background procedure, or nil if 
there isn't one.


SkelEventHook (p)
Boolean (*p)();

SkelEventHook installs a function to be called in SkelMain every time a 
non-null event occurs. A pointer to the event record is passed to the 
function, which can inspect the event and take whatever action it deems 
necessary. The function should be declared as follows:

	Boolean EventHook (theEvent)
	EventRecord *theEvent;

If the hook returns true, SkelMain assumes that the event was handled 
and ignores it, otherwise TransSkel handles the event as it would 
otherwise. To turn event inspection off, pass a value of nil to 
SkelEventHook. There is no event inspection initially.

Note
Not every event that occurs will pass through the hook. For example, 
many desk accessory events are processed by the system; modal dialog 
events likewise; mouse up events following clicks in the menu bar and 
window drag regions are eaten by the ToolBox. Notwithstanding this 
caveat, the events seen by the hook are generally the only ones the host 
really cares about, anyway.


SkelGetEventHook (p)
Boolean (**p)();

SkelGetEventHook returns the current event-inspecting hook, or nil if 
there isn't one.


SkelDlogMask (mask)
Integer mask;

SkelDlogMask is used to specify the types of events that should be 
allowed to pass through to modeless dialogs. The mask is constructed by 
or-ing together the event masks used to specify individual event types. 
The actual mask used within TransSkel will also have bit 0 turned on, 
even if not turned on in the mask parameter. This forces null events to 
be passed to modeless dialogs; otherwise the caret in TextEdit items 
would not blink.


SkelGetDlogMask (mask)
Integer *mask;

SkelGetDlogMask returns the current dialog event mask.


Miscellaneous Notes

The typical application sets up, calls SkelMain, then calls SkelClobber 
and exits when SkelMain returns. This is not strictly necessary. The 
only rule is that every invocation of SkelMain must be terminated by a 
call to SkelWhoa. SkelMain may, if desired, be executed again.

It is also possible to invoke SkelClobber and install a new set of 
handlers between calls to SkelMain.

More generally, one may call SkelMain recursively, or call SkelClobber 
while SkelMain is executing. The only thing to watch out for is that 
this sort of thing should not be done from within window handler pIdle 
procedures. One way in which this property may be used is to force all 
update events to be processed (so that all windows are completely 
redrawn) before any other events are handled. This is done by retrieving 
the current event mask and background procedure. Then set the event mask 
so that only update events are requested, and install a background 
procedure that calls EventAvail (with the same mask). Then call 
SkelMain. The background procedure does nothing as long as there are 
update events pending, and calls SkelWhoa as soon as there aren't any 
more. When SkelMain returns, restore the previous event mask and 
background procedure. The original call to SkelMain resumes operation as 
before.


Converting Pre-Version 2.0 TransSkel Programs to Version 2.0

Since the TransSkel calls are all fairly easy to find, especially given 
THINK's fast searching capabilities, conversion is quite simple. The 
following illustrates how to quickly convert TransSkel calls for 
applications based on versions of TransSkel older than version 2.0.

*	SkelInit
	This will be called once near the beginning of the application.

	Change from:	SkelInit ();
	to:	SkelInit (6, nil);

*	SkelWindow
	Calls to SkelWindow may appear anywhere in an application. 
	Strictly speaking, you do not need to change these, if you wish to 
	ignore the return value. However, if you wish to explicitly 
	discard the return value or assign it for testing, do the 
	following:

	Change from:	SkelWindow (args...);
	to:	(void) SkelWindow (args...);
	or to:	result = SkelWindow (args...);

*	SkelMenu
	Calls to SkelMenu may appear anywhere in an application. You must 
	change these to supply an extra parameter. You can ignore the 
	return value or assign it for testing, as follows:

	Change from:	SkelMenu (args...);
	to:	(void) SkelMenu (args..., true);
	or to:	result = SkelMenu (args..., true);

	Note that this emulates pre-version 2.0 behavior, i.e., the menu 
	bar is redrawn for every menu installed. It is better to pass 
	false for the last argument for all but the last SkelMenu call.

*	If you find that you're drawing things in the wrong windows, you 
	probably relied on the old TransSkel to set the port for you. 
	You'll have to explicitly set the port.


Related Efforts

The philosophy underlying TransSkel is that it should serve as a module 
that can be plugged into a large number of applications without change. 
Two other packages sharing the same philosophy are available. Both aim 
at the goal of "compile it and plug it in." TransDisplay provides an 
arbitrary number of generic display windows. These can be used for 
displaying debugging output or on-line documentation, for example. The 
module automatically handles scrolling, autoflushing, reformatting when 
the window is resized, etc. TransEdit is similar, providing generic text 
edit windows. Keyboard input, text selection with the mouse, scrolling, 
file I/O, and so forth are handled with little or no host intervention 
required.

Both are public domain, free and come in source form, with documentation 
and example programs.

