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 # 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.