Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

A Programmer's Database for the Macintosh


JAN88: A PROGRAMMER'S DATABASE FOR THE MACINTOSH

Abdullah Al-Dhelaan and Ted Lewis work in the Computer Science Dept. Oregon State University, Corvallis, OR. 97331


One challenge to writing application software for Apple's Macintosh is the complex environment. Programming the Mac requires the use of an extensive set of ROM routines known as the Toolbox. These routines, although largely responsible for the machine's radical ease of use, add appreciably to the Mac programmer's work load. The Toolbox contains more than 600 routines, and many of them are required in writing even the smallest application. Most programmers find themselves continually referencing Apple's massive documentation, Inside Macintosh, for technical details on the numerous procedures, functions, and data structures of the Toolbox.

At first, using Inside Macintosh as a handy reference manual might seem a minor chore. It has a good index and is divided into five logical volumes. Unfortunately, information retrieval time becomes a dominant factor in the development process, and it quickly becomes obvious that an on-line, electronic means of retrieval is needed.

Our program, MacMan, is an online programmers database that contains much of the key information found in Inside Macintosh. Programmers can use MacMan to fetch the name, parameters, comments, and often-needed descriptions of the Toolbox routines. This information can be accessed from within an editor as an aid to documentation or can be used simply as a way to acquire a better understanding of the inner workings of the Mac. Currently, the database contains more than 500K of text describing the Toolbox routines as well as many other useful facts about the Macintosh.

The design goals for MacMan were simple: the program should be convenient to use, and it should contain useful information. Above all else, we knew that it had to be convenient to be compelling. Convenient meant both simple and fast. We knew that programmers wouldn't accept MacMan if they had to learn a new language or come to grips with a complex database system.

We also knew that the information contained had to be accurate and useful. We could have written the information in a more useful form than that of Inside Macintosh, and we could have selected information from various other sources. We rejected these alternatives, however, because we didn't want to risk introducing errors or to accidentally include ambiguous passages.

We therefore elected to copy carefully selected passages directly from Inside Macintosh. We worked hard to convince Apple Computer to let us use the copyrighted contents of Inside Macintosh specifically to avoid errors or misrepresentations of fact.

MacMan is not a generalized programmers' database program. We willingly sacrificed generality for user convenience. As a result, MacMan is both fast and extremely easy to use. The description of any Toolbox routine can be retrieved from the 500K database file and displayed in a window-all in less than a second.

Using MacMan

There are a two ways to use the database: an abbreviated version of MacMan can be installed in the system file as a desk accessory (DA). or a full-fledged application version of the program can be launched from the desktop.

A DA is a special program that can run concurrently in memory with another application. Most DDJ readers are familiar with the concept of DAs from TSR (terminate-and-stay-resident) programs such a SideKick, but these products illustrate an ad hoc solution to the problem of writing a DA. In contrast to the PC, the Mac allows DAs to be integrated into the Macintosh operating environment. It's this integration that we'll be emphasizing in this article. We'll describe the MacMan desk accessory and then show how it was implemented using the Macintosh Toolbox functions.

Two simple access methods are provided through the menu: Find by Name and View by Category (see Figure 1, page 25). Find by Name, as you might expect, retrieves the desired Toolbox routine by its name. If you don't know the routine's name, you can select the View by Category menu item (see Figure 2, page 25). View by Category lets you select one of the 28 managers as a category and then browse through it. When you browse through a category, the Toolbox routine names are displayed in alphabetical order--selecting one of them results in a full display of the routine's description (see Figure 3, below). If MacMan is unable to find a Toolbox routine, it reports the error and asks if it should browse all the routines that begin with the same letter (see Figure 4, page 26).

Figure 1: A toolbox routine is retrieved by giving either its name or its manager category.

     MacMan    Edit
          Find by name
          View by category
          Quit

Figure 2: The MacMan categories cover the ROM routines for both the user interface Toolbox and operating system.

Select any Category Please:

     All                           Macintosh Packages
     Resource Manager              Memory Manager
     Quick Draw                    Segment Loader
     Font Manager                  O.S. Event Manager
     ToolBox Event Manager         File Manager
     Window Manager                Printing Manager
     Control Manager               Device Manager
     Menu Manager                  Disk Driver
     TextEdit                      Sound Driver
     Dialog Manager                Serial Drivers
     Desk Manager                  AppleTalk Manager
     Scrap Manager                 Vertical Retrace Manager
     ToolBox Utilities             Operating System Utilities

                                             CANCEL

Figure 3: An example of a displayed Toolbox routine

dragcontrol

PROCEDURE DragControl (theControl: ControlHandle; startPt: Point;
limitRect,slopRect: Rect; axis: INTEGER);

Called with the mouse button down inside theControl, DragControl pulls a
gray outline of the control around the screen, following the movements
of the mouse until the button is released. When the mouse button is
released, DragControl calls MoveControl to move the control to the
location to which it was dragged.

(note)
Before beginning to follow the mouse, DragControl calls

Figure 4: MacMan reverts to browsing if the named Toolbox routine can't be found.

Sorry, "setwtitlee" is not found

Should I Give a list of all that start with "s"

          CANCEL         OK

The subject of this article, DAMacMan, is a version of the database that runs as a desk accessory. Before delving into the inner structure of DAMacMan, however, we'll first review the structure of the typical Macintosh application and how it relates to calling DAs.

The Structure of Mac Applications

Every Macintosh application consists of at least one event loop that determines what operations the application's user is allowed to perform. The event loop must handle all user interactions such as mouse clicks, menu selections, and icon manipulations. The existence of an event loop makes Macintosh applications resemble realtime control programs more than traditional interactive sequential programs.

Every well-behaved application must include a Toolbox call to SystemTask so that periodic actions, such as updating the system clock, can be performed by the Macintosh operating system. In Example 1, page 26, we show only one SystemTask call for each pass through the main event loop, but in general SystemTask should be called at least once every 16 clock ticks (a tick is defined as a 60th of a second). If the application is doing a lot of work on each pass of the event loop, then the SystemTask call should be made more often.

Example 1: A simple event loop

PROCEDURE SimpleEventLoop ( Var Event: EventRecord );
     Var UserAction : Boolean; {Has the user done something??}
          Finished       : Boolean; {Exit When user Quits}

Begin
     Repeat
     SystemTask;         {To support periodic events}
     UserAction := GetNextEvent ( AllEvents, Event);
                                   {To invoke SystemEvent}
     If UserAction then  {Handle the event...}
     Case Event.what Of
            mouseDown    : DoMouseDown (Event, Finished );
            KeyDown      : DoKeyDown (Event, Finished );
            ActivateEvt  : DoActivate (Event, Finished );
            UpDateEvt    : DoUpdate (Event, Finished );

     End;   {case}
     Until Finished;     {terminate the program?}
End;                     {SimpleEventLoop}

The application calls GetNextEvent each time through the event loop in order to find out what events have taken place since the previous pass. GetNextEvent calls the SystemEvent routine, which simply intercepts the stream of events, and if an event belongs to the DA, that event is shuttled to the DA rather than to the application.

Suppose the user selects the DA menu and presses the mouse button; this will cause a mousedown event (mouseDown) to be generated by calling the DoMouseDown routine. The application programmer must write the DoMouseDown routine in such a way as to call the appropriate DA. The code necessary to do this is shown in Example 2, below.

Example 2: The DoMouseDown routine

PROCEDURE DoMouseDown (   Var Event : EventRecord;
                         Var Finished: Boolean );
Var
     whichWindow : WindowPtr;{Window that mouse was pressed in}
     whereIs    : INTEGER;   {Part of screen where mouse was pressed}

Begin {DoMouseDown}
     {Where on the screen was mouse pressed?}
     whereIs := Findwindow ( Event.where, whichWindow);
     Case whereIs Of
          InDesk:          {In Empty Space...}
               {Do nothing};
          InMenuBar:       {Menu Selection...}
               DoMenuClick;
          InSysWindow:     {Aha! In a DA...}
               SystemClick  ( Event, whichWindow);
          InContent:       {In Application's window...}
               DoContent    (whichWindow);
          InDrag:          {Drag Application's window...}
               DoDrag       (whichWindow);
          InGrow:          {Resize Application's window...}
               DoGrow       (whichWindow);
     InGoAway:        {Close Application's window...}
               DoGoAway     (whichWindow)
     End--{case}
End; {DoMouseDown}

The events that are diverted from the application to the DA are channeled into the DA processing code in two ways, as shown in the DoMouseDown procedure. The first way is through the System Click Toolbox routine, as shown in case InSysWindow of DoMouseDown, and the second way is through a menu selection.

When a mouse-down event occurs in a system window, the application code should call SystemClick. If the mouse-down event is in a DA window, Systemclick takes care of processing the event instead of the application. This case will be discussed later as it is what happens when the DA is already on the screen.

The mouse-down event could also occur in the DA menu (under the apple), which would mean that the DA is to be activated (this is called opening the DA). This is the case we are most interested in for the time being. If the user has selected the MacMan DA, for instance, then the application program must handle the opening of the DA from the DoMenuClick routine, shown in Example 3, page 30.

Example 3: The DoMenuClick routine

PROCEDURE DoMenuClick;   { Handle mouse-down event in menu bar. }

Var
     menuChoice     : LONGINT;     {Menu ID and item number}
     theMenu   : INTEGER;     {Menu ID of selected menu}
     theItem   : INTEGER;     {Item number of selected item}

Begin {DoMenuClick}

menuChoice := MenuSelect (TheEvent.where);
     if menuChoice <> 0  {Application, or DA?}
     Then begin          {Application...}
          theMenu := HiWord   (menuChoice);{Get menu ID}
          theItem := LoWord   (menuChoice);{Get item number}
     Case theMenu of
          AppleID:            {Make selection from Apple menu}
               DoAppleChoice       (GetMenu ( AppleID ), theItem);
          FileID:             {Make selection from File menu}
               DoFileChoice        (GetMenu ( FileID ), theItem);
     EditID:             {Make selection from Edit menu}
               DoEditChoice        (GetMenu ( EditID ), theItem)
     End; {case}
     End; {if}
End; {DoMenuClick}

As shown in DoMenuClick, when an application calls the MenuSelect (or MenuKey) Toolbox routine, a call is made to SystemMenu, which passes the event to the DA (if the event is a mouse-down in the DA menu). The DA must then handle the event and return a zero to the application. Otherwise, MenuSelect returns a long integer containing the menu number in its HiWord and the item number in its LoWord. In this case, when the user selects the Apple menu, and within this menu, the MacMan DA, the DoAppleChoice routine (see Example 4, page 30) is called to activate the DA.

Example 4: The DoAppleChoice routine

PROCEDURE DoAppleChoice (Var AppleMenu: MenuHandle;
                          theItem  : INTEGER);
Var
     accName   : Str255;   {Name of desk accessory}
     accNumber : INTEGER   {Reference number of desk accessory}

Begin          {DoAppleChoice}
Case theItem of
     AboutItem:     {Application's About... Item}
          DoAbout;
     otherwise      {Must be a DA...}
          Begin
          GetItem       {AppleMenu, theItem, accName)
                                   {Get accessory name}
          accNumber := OpenDeskAcc (accName) {Open desk accessory}
          End           {otherwise}
     End           {case}
End;          {DoAppleChoice}

This sequence of actions opens the DA and prepares it for use alongside the currently running application. In summary, the sequence is:

  • A mouse-down event occurs and is handled by the event loop.
  • The DoMouseDown routine decides the event is InMenuBar.
  • The DoMenuClick routine decides the event is an AppleMenu event.
  • The DoAppleChoice routine decides the event is a DA selection.
  • The name of the DA (accName) is obtained and the DA opened.

The Structure of a Mac DA

A DA is a "mini-application" that can be run concurrently with a Macintosh application. A DA cannot exceed 32K of executable machine code and data and is installed in the start-up disk system file using a Macintosh utility program called the Font/DA Mover. Once a DA is installed, it no longer has a file or an icon visible from the desktop. Instead, the user opens a DA by selecting it from the standard Apple menu, which by convention is the first in the menu bar.

After a DA has been opened, its window, if any, is displayed on the desktop and it becomes the active window. To close a DA, the user can click the DA's close box (in its own title bar), or another program can call the function CloseDeskAcc to close it. The DA will then disappear and the frontmost window will become the active window.

A desk accessory may have a menu of its own, which will be added to the menu bar when it is active and deleted when it is not. The Cut, Copy, and Paste commands in a standard Edit menu can be used by an active DA. They are very useful for copying and pasting between the DA and the application or another DA.

Writing a DA

Writing a DA is a lot more difficult than writing a "plain vanilla" application because a DA has no main procedure, no main event loop to obtain events, and no global variables.

Technically, a DA is known as a Macintosh device driver, and each DA is required to have three special procedures: Open, Close, and Ctl (for control). These procedures are called directly by the operating system through a special table called the DA Header.

Each of these procedures requires two formal parameters of type Device Control Record and Parameter Block Record. A Device Control Record is created when a DA is opened and destroyed when it is closed. A Parameter Block Record is created by the operating system each time any of the three routines is called. It is used to inform the DA about the purpose of the call.

The Macintosh operating system calls Open whenever a DA is selected from the Apple menu and calls Close whenever the close box on the title bar of the DA's window is clicked or CloseDeskAcc is called by some other program. Between these two calls, the system will call Ctl.

What DA Procedures Do

When the Open procedure is called to open the DA, it will:

    1. Create the DA window if there is one

    2. Set the WindowKind field of the window's WindowRecord to the DA's driver reference number. (This field is set so the operating system can call the correct DA when an event occurs in a DA window.)

    3. Set the DctlWindow field of the DA's Device Control Entry Record to the window pointer.

    4. Allocate space for global data in the field dctlstorage of the Device Control Entry Record.

    5. Initialize the global data.

Close is called to close the DA. It first disposes of its window and stores nil in the DctlWindow field of the Device Control Entry Record, then it disposes of any global data it might have allocated in the dctlstorage field of the Device Control Entry Record.

Ctl is called to enable the DA to handle the action indicated by the csCode field of the Parameter Block Record. There are nine such actions, as shown in Table 1.

Table 1: DA actions allowed by Ctl

1. AccEvent   An event (update, activate, keyboard...)
2. AccRun     Do a periodic action
3. AccCursor  Change cursor shape
4. AccMenu    Menu selection
5. AccUndo    The Undo editing command
6. AccCut     The Cut editing command
7. AccCopy    The Copy editing command
8. AccPaste   The Paste editing command
9. AccClear   The Clear editing command

Passing Text

An application must have the standard Edit menu if it wits to support passing text to and from DAs. The order of items in this menu is important, but the menu can be made longer by adding items at the end.

The standard Edit menu contains Undo, Cut, Copy, Paste, and Clear. When a user chooses one of these commands, the application must call Toolbox routine SystemEdit from within DoEditChoice (see Example 5, page 30). The menu items are numbered 0 through 5 internally, which is why we subtract 1 from the item number (theItem-1) in the routine shown in Example 5. If the active window is a system window (that is, a DA window), then SystemEdit will return false and the application will process the command as usual. Otherwise, SystemEdit will shuttle the event on to the DA for command processing and return true.

Example 5: The DoEditChoice routine

ProcDoEditChoice (......, theitem : Integer);
     { Handle choice from Edit Menu }
     Begin       {DoEditChoice}
               if Not SystemEdit (theItem-1)
          Then
               Case theItem of
                    cutitem  : DoCut;
                    copyitem : DoCopy;
                    Pasteitem: DoPaste;
          end; {Case}
End; {DoEditChoice}

Resources for DAs

The code for the DA is not a CODE resource, as it is for applications, but is a DRVR because a DA is actually a device driver. The Macintosh resource compiler RMaker can be used to create a DRVR resource by reading the CODE resource created MAC DATA BASE by the linker with ID = 1 and converting it to a DRVR resource. This is done by including the following lines in the resource file prior to compiling it with RMaker:

Type DRVR = PROC
      Deskacc,16
DeskaccFile

The name DeskaccFile is the linker's output file for the DA file, and Deskacc will be used as the DA name to appear under the Apple menu once the DA is installed in the system file. The resource ID then becomes the DA's driver reference number and is used by the operating system to implement the DA and is also used for owned resources.

Owned Resources

A range of 32 resource IDs has been reserved for each DA DRVR resource ID, so they are called owned resources. A special numbering convention is used to associate owned system resources with the resources they belong to. For any particular DA, this range is computed by adding $C000 and 32 times the driver reference number--for example, if the driver reference number is 16, then the range is -15,872 through -15,857.

When the DA is moved from its own file or a system file into a system file, all its resources for windows, menus, and so on must be transferred along with the code for the DA into the destination system file. If the destination system file already has a DA with the same DRVR resource ID, the Font/DA Mover will renumber it and all of its owned resources. Part of the DAMacMan resource file is shown in Listing One, beginning on page 48.

In summary, a DA program consists of at least three special procedures called Open, Close, and Ctl. The DA program may have other procedures as well, but it has no main body. You might think of the DA program as a module consisting of its own constants,g types, variables (Listing Two, page 48), and procedures (Listing Three, page 50).

Open is called by OpenDeskAcc (from the running application); Close is called by Ctl (when the DA terminates itself) or ExitToShell (when the application terminates); and Ctl is called each time the application calls SystemEvent.

Listing Three shows these three procedures for DAMacMan, but keep in mind that these procedures must always exist for every DA even if they are tailored for some other purpose. In addition, because DAs are limited to 32K in size, the sophistication of a DA is restricted to miniature utility functions such as displaying the keyboard and so on. Implementing the MacMan database retrieval code was quite a challenge because of this limitation.

The Structure of DAMacMan

When DAMacMan is opened from the Apple menu, it looks in the system disk to see if the files it requires are present. If a file named Manual and another file named MMIndex are not present, DAMacMan will display an error dialog.

DAMacMan's related files are Manual, text from Inside Macintosh (the database); MMIndex, the index into the database; MMSize.int, a temporary file used to generate a version of DAMacMan; MMConfig, a DAMacMan software tool for generating versions and MMLock, which locks and unlocks files.

Manual, the database, consists of two parts: the MacMan distribution text and the MacMan information that contains all the procedures and functions defined in Inside Macintosh, organized as follows:

\name
     category#
     body

where name is the name of the procedure or function, category is the section of Inside Macintosh in which it is defined, and body is the information about the procedure or function.

The index file, MMIndex, contains records sorted with respect to name, each record having the following form:

Record
     name : String[25];
     start : longint;
     length : Integer;
     man : Integer;
end;

where name is the name of the procedure or function, start is the starting position relative to the beginning of the manual, length is the length of the text that belongs to this procedure or function, and man is an integer representing the section or manager of Inside Macintosh where this function or procedure is defined.

The MMSize.int file contains the statement:

Const MaxRec = ;

The blank will be filled in by the MacMan tool MMConfig, prior to compiling MacMan. The value of MaxRec is equal to the number of entries in Manual. The DAMacman main program is shown in Example 6, page 41.

Example 6: The DAMacMan main program

PROGRAM DAMacMan;
{DAMacMan is an online inside macintosh, running as a desk accessory

     Pascal source: DAMacMan.pas  Main Program
     Uses :

               MMSize.Int    The Manual Size, Generated by MMConfig Tool
               DAMacMan.int  The Interface include file
               DAOthers.imp  Our own procedures and functions
               DAThree.mp    The open, Ctl, Close procedures
               DAIndex       Indes To Database Enntries
          Manual              Database from Inside Macintosh
     Resources :   DAMacMan.R

     Creation Date: July 1st, 1986
     Author :      Abdullah Al-Dhelaan
                    (TGL Software Development Group)
                    Oregon State University
}
{ The next four include files below are Interface to Toolbox....}
{$I MemTypes.ipas }
{$I QuickDraw.ipas }
{$I OSIntf.ipas }
{$I ToolIntf.ipas }

{$I MMSize,Int }   {The Manual Size, Generated by MMConfig Tool}
{$I CAMacMan.int } {The Interface include file }
{$I DAOthers.imp } {Our own procedures and functions}
{$I DAThree.imp }  {The open, Ctl, Close procedures}

{-----------------------Main Program--------------------------}
BEGIN
{Desk Accessory, There should be no main program}
END.

MacMan Tools

DAMacMan is configured and maintained through the use of a set of tools that must be applied each time the database is changed. MMConfig is a tool for constructing the index file for the database and the Pascal compiler include file that contains the size of the database. In addition, the integrity of the database is maintained by locking the database files using the MMLock program, described later.

MMConfig reads the file Manual and writes the ordered file MMIndex with records of the form:

Record
     name : String [251;
     start : Longint;
     length : Integer;
     man : Integer;
end;

MMConfig performs the following steps:

    1. It forms a record for each procedure or function in Manual.

    2. It store a pointer to each of the records in an array of pointers.

    3. It sorts the array with respect to name.

    4. It creates the file MMIndex, to be read whenever MacMan is started up, and writes the sorted records to it.

    5. It creates the file MMSize.int, which will be included during compilation of DAMacMan.

    6. It locks the files Manual and MMSize.int so they cannot be opened by a user who is not supposed to have access to these files. 7. It stamps these files with the appropriate icons.

MMConfig is run to create MMIndex and MMSize.int after Manual has been edited the first time and whenever Manual is updated.

Finally, MMLock is a MacMan tool whose job is to lock and unlock the files that are generated by MMConfig.

Data Structures

As mentioned, after Manual has been constructed by entering all desired text into the database, MMConfig is run to generate the files MMIndex and MMSize.int. Then DAMacMan can be compiled and run. When DAMacMan is run, it loads the file MMIndex into a list of

type table: table : array [1. .MaxRec] of ptr1;

where ptr1 is a pointer to a record similar to those stored in MMIndex and MaxRec is the number of functions or procedures in Manual. MaxRec is set by including the file MMSize .int.

Installing DAMacMan

DAMacMan is compiled into the resource file MacManFile. The Font/ DA Mover utility tool is used to copy this file into a system file. After the DA has been installed in the system file of the start-up volume, it can be selected from the Apple menu.

Bibliography

Apple Computer Inc. Inside Macintosh. 4 vols. Reading, Mass.: Addison-Wesley, 1985-1986.

Chernicoff, Stephen. Macintosh Revealed, Volumes I and II. Hasbrouck Heights, NJ.: Hayden/Apple Press, 1985.

Reingold, E.; and Wilfred, H. Data Structures. Boston: Little, Brown, 1983.

Takatsuka, J.; Huxham, F; and Burnard, D. Using the Macintosh Toolbox with C. Berkley, Calif: Syvex, 1986

TML Systems. MacLanguage Series Pascal User's Guide and Reference Manual, Jacksonville, Fla. TML Systems, 1985

Wirth, Niklaus, Algorithms + Data Structures = Programs. Englewood Cliffs, N.J.: Prentice-Hall, 1976

Wootton, Alan. "Resource Utility DA with TML." MacTutor (November 1985).

[LISTING ONE]

<a name="0028_001c">

*            DAMacMan.r                 Resource
*            July 3, 1986               Last modified
*
* Resource file for "DAMacMan.pas" for use with MacLanguage
* series Pascal Compiler (TML Pascal)
*
*
* all resources must be between -15872 and -15841 inclusive
* if they are to travel  with DRVR number 16

MacManFile
DFILDMOV

TYPE DRVR = PROC
 MacMan,16
DAMacMan

* this DLOG is the main window.
* the StatText items are used only for their rectangles.

Type WIND
 ,-15872
Untitled
50 40 300 510
InVisible GoAway
0
0

* menu used by the DA

type MENU
  ,-15872
Macman     ;; menu title
about Macman
(-
Find By Name
View By Category

* This second dialog is for about menu item
* the button and icon do nothing. Just decoration


Type ALRT
 ,-15872
50 50 330 430
-15872
4444

 ,-15871
50 50 300 480
-15871
4444

 ,-15870
60 80 126 430
-15870
4444

--------------------------------------------------------------------------


<a name="0028_001d"><a name="0028_001d">
<a name="0028_001e">
[LISTING TWO]
<a name="0028_001e">


PROGRAM  DAMacMan;
{ DAMacMan is an online inside macintoch, running as a desk accessory

  Pascal source:  DAMacMan.pas     Main Program
  Uses  :

            MMSize.Int     The Manual Size, Generated by MMConfig Tool
            DAMacMan.int   The Interface include file
            DAOthers.imp   Our own procedures and functions
            DAThree.imp    The open, Ctl, Close procedures
            DAIndex        Index To Database Entries
            Manual         Database from Inside Macintosh

  Resources    :   DAMacMan.R

  Creation Date:  July 1St, 1986
  Author       :  Abdullah Al-Dhelaan
                  (TGL Software Development Group)
                  Oregon State University
}
{ The next four include files below are Interface to Toolbox ...}
{$I MemTypes.ipas    }
{$I QuickDraw.ipas   }
{$I OSIntf.ipas      }
{$I ToolIntf.ipas    }

{$I MMSize.Int       }   { The Manual Size, Generated by MMConfig Tool }
{$I DAMacMan.int     }   { The Interface include file }
{$I DAOthers.imp     }   { Our own procedures and functions }
{$I DAThree.imp      }   { The open, Ctl, Close procedures }

{------------------------------Main Program----------------------------}
BEGIN
    { Desk Accessory, There should be no main program }
END.


The DAMacMan.int file in shown below :
{
             DAMacMan.int                 Interface
             July 3, 1986                 Last modified

 An interface file that Contains all the needed types and vars

}

Const

  accEvent   = 64;
  accRun     = 65;
  accCursor  = 66;
  accMenu    = 67;
  accUndo    = 68;
  accCut     = 70;
  accCopy    = 71;
  accPaste   = 72;
  accClear   = 73;

  ManFile    = 'Manual'; {Name of Database File}
  Vnum       = 0;        {default drive}

 Type
   Str25  = String [25];
   Lptr   = ^LongInt;
   Item   =  Record                           { An entity record}
                   name        : Str25;       { Proc/Func. Name }
                   start       : LongInt;     { Starting pos. on the Manual }
                   length      : Integer;     { Length of Proc./Func. Text }
                   man         : Integer;     { Category # }
          END;

   ptr1      =  ^ Item;
   myarray   = ARRAY [1..MaxRec] of Ptr1;  { The main array }

{This is the data. A handle to it will be stored in the dctl storage field
 of the DCltEntry Record  }

  GlobalsH   = ^GlobalsP;
  GlobalsP   = ^GlobalsRec;
  GlobalsRec = Record { DAMacMan Database Info }
                 hScroll      : ControlHandle;{Horizontal scroll for window.}
                 vScroll      : ControlHandle;{Vertical scroll for window}
                 pRect        : Rect;         {Rectangle within window to see}
                 tRect        : Rect;
                 hTE          : TEHandle;     {Text is here...}
                 table        : myarray;      {Index to entries}
                 noIndex      : boolean;      {Error if no Index on Disk}
                 noman        : boolean;      {Error if no Database on Disk}
              END;

  { This is used to store system info about the state of the driver. It will
   be passed to us on all calls from system. This Record is definded in the
   interface (TML files) above (as DCtlEntry) . it is not strictly necessary
   to redefine it here. But doing so,  will enable me to use dctlStorage^^ to
   refer to GlobalsRec without coercing Types }

  MyDeviceEntry = Record
             DctlDriver       : HAndle;       { Pointer to driver }
             DctlFlags        : Integer;      { Flags }
             DctlQueue        : Integer;      { Low-order byte, drivers version
number }
             DctlQhead        : Lptr;         { Pointer to first entry in
drivers I/O queue }
             DctlQTail        : Lptr;         { pointer to last entry in
drivers I/O queue }
             DctlPosition     : LongInt;      { Byte position }
             DctlStorage      : GlobalsH;     { Handle to RAM driver's private
storage }
             DctlRefNum       : Integer;      { Driver's reference number }
             DctlCurTicks     : LongInt;      { Used internally by Device
Manager }
             DctlWindow       : Grafptr;      { Pointer to driver's window
Record }
             DctlDelay        : Integer;      { number of ticks between
periodic actions }
             DctlEmask        : Integer;      { Desk accessory event mask }
             DctlMenu         : Integer;      { Menu ID of menu associated with
driver }
          END;

{ No VARiables for main program are allowed }


The file DAThree.imp is shown below :

{
            DAThree.imp               Implementation
            July 3, 1986              Last modified


<a name="0028_001f"><a name="0028_001f">
<a name="0028_0020">
[LISTING THREE]
<a name="0028_0020">

     THE FOLLOWING PROCEDURES SHOULD BE IN EVERY DESK ACCESSORY
     AND THEY ARE CALLED BY THE SYSTEM

}


 PROCEDURE open   ( VAR Device : MyDeviceEntry;
                    VAR block  :  ParamblockRec);
 { Open Makes a window and sets-up our private storage.
   we may get an open call even after we are already open }

CONST
    InfoLength = 700;   {The number of chars. fro the distribution text}
  VAR
     Wpk      : WindowPeek;
     Dtyp     : Integer;
     ID       : Integer;
     Dhan     : Handle;
     tmpPtr   : ptr;
     dummy    : GlobalsRec;
     Watch    : CursHandle;  {Handle to wristwatch cursor}

  BEGIN

  {Use ID for all Resource access}
   ID := $C000 - 32 * (1 + Device.dctlRefNum);
   Device.dctlMenu := ID;
   With Device do
   if DctlWindow = nil
   THEN BEGIN
     { Create a hole in the heap.  It is good practice to keep
       Window records off of the bottom of the Application Heap. }
     DctlStorage := pointer(NewHandle(SizeOf(dummy)));
     TmpPtr := NewPtr($1000);
     Hlock(Handle(DctlStorage));
     With DctlStorage^^ do
       BEGIN {initialize our storage }
       noindex := false;    {Index found}
       noman   := false;    {Manual found}
       Watch := GetCursor (WatchCursor);
       SetCursor (Watch^^); {Indicate delay }
       CreateWindow (Device,ID);
       { post information }
       DoOpen (Device,'MacMan Distribution',0,InfoLength);
       If not noman
          Then LoadData(device); {Load the array}
       initcursor;
     END;  { of with storage }
     {Deallocate our temporary pointer }
     Hunlock(Handle(DctlStorage));
     DisposPtr(TmpPtr);
    END; { of if }
 END; { of open }

{---------------------------------------------------------------------}

PROCEDURE close  (   VAR Device : MyDeviceEntry;
                  VAR block   : paramBlockRec);
 BEGIN
    deactivate(Device);    { remove menu }
    with Device do
    BEGIN
      disposHandle(Handle(DctlStorage));      { kill data }
      disposeWindow(DctlWindow);               { erase window }
      DctlWindow := nil;

   END; {of with }
 END; { of close }

{----------------------------------------------------------------------}

 PROCEDURE ctl (   VAR Device : MyDeviceEntry;
               VAR block : ParamBlockRec) ;
{ Here is the main entry point for system calls. The permanent bolck tell us
  what the nature of the call is. }
  VAR
      mousept       : point;
      wpnt          : Grafptr;
      item          : Integer;
      ibeam         : cursHandle;
      ignore        : Integer;
      longignore    : LongInt;

 BEGIN
    setport(Device.DctlWindow);
    Hlock(Handle(Device.DctlStorage));
    with Device, DctlStorage^^,block do
     BEGIN
        TEidle(hTE);
        CASE csCode of
            accevent   :  Event(Device, block);
            accCursor :
              BEGIN
                 ibeam := GetCursor(ibeamcursor);
                 GetMouse(mousePt);
                 If (PtInRect(mousePt,pRect))
                   THEN SetCursor(iBeam^^)
                   ELSE InitCursor;
              END;
           accmenu   :    { CASE out menu item number }
             BEGIN
                Initcursor;
                CASE csParam[1] of
                        1 : Doabout (Device.dctlmenu);   {about... }
                        3 : IF (not noindex) THEN  DoFind  (Device,'');
                        4 : IF (not noindex) THEN  DoView  (Device)  ;
                    END; { of CASE menu }
                HiliteMenu(0);
            END; { of menu CASE }
         accCopy :
           BEGIN
                   longignore := ZeroScrap;  {Init. Scrap}
              TECopy(hTE);      {Copy text from hte to TextEdit scrap}

              ignore := TEToScrap;  {Copy  TextEdit scrap to desk scrap }
              ignore := UnloadScrap; {Copy desk scrap to file scrap}
          END;
       Otherwise ;
     END; { CASE of ... }
    END; { of with block }
  Hunlock(Handle(Device.DctlStorage));
 END; { of control  PROCEDURE }









Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.