TweetFollow Us on Twitter

Tear-off, Float menus

Volume Number: 4
Issue Number: 4
Column Tag: C Workshop

Tear-off Menus & Floating Palettes

By Don Melton and Mike Ritter, Impulse Technologies, Inc., Santa Clara, California

Don Melton is the Computer Graphics Specialist for the San Jose Mercury News and does technical support for the Knight-Ridder Graphics Network. His partner, Mike Ritter, is a Research Scientist at Lockheed's Palo Alto Research Laboratories in the Electro-optics Department. In their spare time they do MacHacking for their own company, Impulse Technologies, Inc.

Introduction

Why TearOffPalette? Well, ever since we saw HyperCard we've been wondering, just like everyone else, how Bill Atkinson did that impressive tear-off menu effect. Of course, the wizardry Andy Hertzfield performed with Radius's new monitors caught our eye, too. If you haven't seen Andy's ROM-defying performance, take a look -- he makes any menu tear off!

And now everyone wants to get into the act.

Daryl Lovato gave many of us our first clues toward making menus leave their home in his article titled "Tear-Off Menus Explained" in December's MacTutor. But he still left many questions unanswered, like how do you get the darn palettes to 'float' above other application windows (which we call documents to differentiate them from the floating palettes). And he didn't use those stylish HyperCard title bars in his palettes either.

We also noticed that all of the applications we saw which used tear-off menus had some rather strange 'features' in them. Our first attempt to do the tear-off trick simply mimicked HyperCard. The constant flashing of menu items soon drove us batty. Then there's Bill's highlighting (or lack thereof) of his main window when a menu is torn off.

Then we saw the new MacPaint. Sure, the highlighting is better but the palettes are in a fixed order! Even HyperCard doesn't do that. Two steps forward and one step back. That's progress.

If only Apple would just torture Andy Hertzfield until he lets them release his code as system software. Alas, Radius has to maintain its market advantage. Maybe if everybody buys enough Radius monitors Burrell Smith may let the cat out of the bag.


Fig. 1 Tear off palette menus that float on top!

Until then we decided to show you how it can be done. And how to make it nice and modular too -- with one application, an MDEF and a WDEF. Using our code any developer can implement tear-off menus and floating palettes in their application. Really. As a bonus, our source code will also demonstrate how to:

  • Work with MultiFinder using WaitNextEvent()
  • Use Color QuickDraw to implement a full-color palette
  • Respond to suspend/resume events
  • Write a fully-working, albeit simple, grow zone function

And it has extensive error checking built right in. Enough to function correctly with strict trap discipline and heap check, scramble and purge, all set in TMON, while it runs concurrently under MultiFinder with ResEdit and any Microsoft application. Just about the nastiest environment a Mac developer could imagine.

It also (ta da!) works with any Macintosh. That's right, even those lonely ones in the corner with 64K ROMS.

Before we get to the code though, we'd like to cover some user interface considerations, the proper use of menu and window definitions, some guidelines for using our special MDEF, and a general explanation of how the whole thing works.

User-Interface Thought Police on Patrol

We're going to pretend we're Apple Computer and tell you how tear-off menus and floating palettes should behave. After all, we are writing this with plural pronouns.

Before a menu is torn off it must act like a normal menu. The currently selected item must be highlighted when the menu is drawn. The current item must be unhighlighted and another item must be highlighted as the mouse is tracked through the menu. When the mouse button is released within the menu, the highlighted item must flash and become the currently selected item.

When the mouse is more than 15 pixels outside the menu, a gray outline of the palette must track the mouse until the button is released or the mouse moves back within the menu or menu bar. The mouse must be 'stuck' to the top center of the gray outline, just as if it were dragging a window by its title bar. If the mouse button is released outside the 15 pixel boundary, a floating palette must appear within the gray outline. See Figure 1.

If a non-application window is the front window when the menu is torn off, the top-most document and any other floating palettes must be brought in front of that non-application window. The torn-off menu must then become the top-most palette. The top-most document or other floating palettes must never be unhighlighted when a menu is torn off. See Figure 2.

Floating palettes must always remain above documents in their own layer and they must have a user-selectable order in that layer. If the only visible document is closed, all of the palettes must remain visible.

In a non-multitasking environment, whenever a non-application window is brought to the front, the top-most document and all floating palettes must be unhighlighted. See Figure 3. Under MultiFinder, whenever another application is brought to the front, the top-most document must be unhighlighted and all of the visible palettes must be hidden. When the application is brought back to the front, all hidden palettes must be made visible and the top-most document must be highlighted.

Whew! And you thought Apple was stuffy in their "Human Interface Guidelines: The Apple Desktop Interface," a recent must-read publication from Addison-Wesley. If you've got a copy handy, turn to pages 90 through 92 and see how close our definitions match.

Hmmm. We seem to be much more specific about certain details. Why? Well, after using HyperCard and the new MacPaint we began to notice some of the quirks we mentioned earlier. Some behavior just didn't seem to conform to the standard Macintosh user interface. Like not unhighlighting floating palettes when a desk accessory is brought to the front. Which then is active, the desk accessory or the application? If the windows don't overlap, the user can easily become confused.

Tear-off menus and floating palettes shouldn't break any of the other normal interface guidelines. Think of them as an extension or super-set of the regular Mac interface. Also, always remember Scott Knaster's rule, "Apple is not God." That's why we don't require the user to hold down the infamous command key to tear off a menu. That rule reeks of modality. Why make the convenience of tearing off a menu with the mouse more inconvenient by having to use the keyboard? Obviously Bill Atkinson feels the same way. And remember, HyperCard is system software.

Of course, if you hold down the command key, our menus will still tear off.

And don't be confused by Apple's insistence that "if a single application can have more than one menu torn off at a time, the application must determine their order of precedence." Of course, floating palettes must always be in some order. But the application should let the user determine that order by clicking the mouse in the title bars of the palettes. It's still a determined order of precedence, just not fixed.

We did add one extension to our application's user interface and it has nothing to do with tear-off menus or floating palettes. It's something that we think the Mac has needed for quite awhile. In fact, Mike Schuster performed a similar extension with Adobe Illustrator. This is simply to allow the user to send a window to the back by holding down the option key and clicking on the window's title bar. And we extended this extension to include the palettes. A floating palette can be sent underneath all of the other visible palettes in the same manner.

Also, just like the Finder, if the option key is held down while closing one of our application windows, all open windows are closed.


Fig. 2 Our palette floats above the document window


Fig. 3 But it correctly allows DA windows on top!

Writing the Darn Thing

So now that we've decided what we want to do, how do we go about doing it?

Well, we could use Andy Hertzfield's method but it's very complex. Of course, since Andy's a genius you'd expect that. Andy decided to patch several Window Manager traps and lie to the ROM about what's actually going on in the Window Manager port. He also plays hide-and-go-seek with the Menu Manager and a lot of other system software. We decided not to get that low-level with our code since we're only planning to tear off a few of our application's menus.

First, to create a menu that will tear off, a menu definition must be written. If that torn-off menu will display a palette that looks different from a standard window, then a window definition must be written. Daryl Lovato showed us this much. But to make those palettes float above application documents, without patching any traps, substitutes for certain Window Manager routines must be implemented within the application code.

Of course, we could just rewrite the entire Window Manager as a library and link it with our code. Anyone have a lot of time on their hands? Just kidding, we'll get to this later anyway.

Since the first job seems to be writing menu and window definitions (MDEFs and WDEFs), we can just jump right in. Right? Well ... there's a little disagreement, and even more confusion, about how these MDEFs and WDEFs should be put together.

MDEFs and WDEFs are called by the Macintosh ROM to do things like draw a menu or highlight a window. The System file and newer versions of the ROM contain the standard definitions as separate resources. Apple recommends that whenever you have to display non-standard menus, windows, etc., you should write and compile these routines as separate resources and move them into your application.

In reality almost nobody, including Apple, does it this way.

Most folks create the definitions as a part of their normal application code and at runtime make the ROM call their code as if it existed in a separate resource. However, there is a right way and a wrong way to do this. Let us explain MDEFs first.

Using a Menu Definition

One of the fields of a MENU resource is the procID. This is a short integer containing the resource ID of the MDEF to be used by the Menu Manager to draw, highlight, etc., the menu. When the menu is inserted into the menu list, the Menu Manager replaces the procID and the following filler field, also a short integer, with a handle to the MDEF. Thus one long, the handle, replaces two shorts, the procID and filler, in the menuProc field of a MenuInfo record.

Have you noticed that the resource seems to be arranged a bit differently than the MenuInfo record? Relax, it works fine because the whole thing is really the same size.

Normally the procID contains 0 to refer to the default MDEF in the system. Programmers who create their own separate MDEF code resources place the ID of their MDEF in a MENU resource's procID. This is the Apple approved method. It works too, but it's more difficult for non-standard MDEFs to be completely separate since they usually contain information specific to the application which must be drawn or highlighted. Some method of communicating with the application must exist. That's why so many programmers leave the menu definition in their application code, so it can access their own global variables and such.

How is that done? One method is too leave your MENU resource with a 0 in its procID, insert the menu into the menu list, and then place a handle to a routine in your application code in the menuProc field that will behave like a normal MDEF. The tricky part is creating a handle to your own code. This is where some programmers try to be clever and wind up doing something very dangerous with Macintosh memory management. Here's the dangerous evil mutant method:

DangerousMethod() {
 MenuHandle myMenuHdl;

 /* Get a handle to the menu
 and insert it into the menu list. */
 myMenuHdl = GetMenu(MENU_ID);
 InsertMenu(myMenuHdl, 0);

 /* Point menuProc at empty block on the heap. */
 (*myMenuHdl)->menuProc = NewHandle(0);

 /* Arrggh!
 Aim the block's master pointer at your code!? */
 *(*myMenuHdl)->menuProc
 = (Ptr) MenuDefintionFunction;

 /* Force the menu size to be recalculated
 after it's inserted. */
 CalcMenuSize(myMenuHdl);

 DrawMenuBar();
}

Never, never, never change the contents of a master pointer this way! As soon as you do, the block to where it formerly pointed no longer exists as far as the memory manager is concerned. And worse still, the block to where it now points is inside another block. Strange and evil things can begin happening to your heap. For more information on this phenomenon, consult Tech Note #117, pages 17-19. Apple has some very strict precautions against using this technique.


Fig. 4 The right and wrong of definition routines

So how do you get a handle to your code? It's so simple it's scary. Put a handle to a routine that will jump to your application code in the menu's menuProc field. What? Another routine? Aren't we going in circles here? Nope, just create it at runtime. Let's define a new type of structure and a new constant:

typedef struct JumpRecord {
 short instruction;
 void (*function)();
} JumpRecord, **JumpHandle;

#define JMP_INSTRUCTION 0x4ef9

Figured it out? Right! All we have to do is allocate one of these records as a relocatable block on the heap, put the value of a 68000 jump instruction in the first field and the address of the menu definition in the second field. Voila! When the ROM looks at this it will see:

 JMP MenuDefinitionFunction

Sneaky? Yeah, but it's one safe and sane method that's also perfectly legal. Here's how you set it up:

SafeAndSaneMethod() {
 MenuHandle myMenuHdl;
 JumpHandle menuDefJump;

 /* Get a handle to the menu
 and insert it into the menu list. */
 myMenuHdl = GetMenu(MENU_ID);
 InsertMenu(myMenuHdl, 0);

 /* Allocate a JumpRecord on the heap. */
 menuDefJump
 = (JumpHandle)NewHandle(sizeof(JumpRecord));

 /* Init JumpRecord to point to your code. */
 (*menuDefJump)->instruction = JMP_INSTRUCTION;
 (*menuDefJump)->function = MenuDefinitionFunction;
 
 /* Point menuProc at JumpRecord on the heap. */
 (*myMenuHdl)->menuProc = (Handle) menuDefJump;

 /* Force the menu size to be recalculated
 after it's inserted. */
 CalcMenuSize(myMenuHdl);

 DrawMenuBar();
}

Now some might argue that this forces the application to become a compiler. In a sense that's true, but the same argument could be made for any variable initialization done by an application. Hey, it works. And it works well.

There's one important thing you need to remember when you're going to have the ROM call your application code. Never let your code move! If you give the ROM a pointer, it better point to the real thing. This means that the menu definition better reside in a code segment that's always available and always locked. Place it in your main segment and you shouldn't have to worry.

There's a variation on the safe and sane method that's definitely worth mentioning. You don't have to create the JumpRecord at runtime. Instead, you can create an MDEF resource that's the same size as the record, have the ROM read it in, and initialize it the same way. And it even saves a few lines of code. Other than that, there's no functional difference at all. Here's an example:

OtherMethod() {
 JumpHandle menuDefJump;
 MenuHandle myMenuHdl;

 /* Get a handle to the MDEF. */
 menuDefJump
 = (JumpHandle)GetResource('MDEF', MDEF_ID);

 /* If the first word of the MDEF resource 
 contains 0x4ef9 then there's no need to 
 initialize the jump instruction here. */
 
 /* Initialize the MDEF to point to your code. */
 (*menuDefJump)->function = MenuDefinitionFunction;

 /* Get a handle to the menu
 and insert it into the menu list. */
 myMenuHdl = GetMenu(MENU_ID);
 InsertMenu(myMenuHdl, 0);

 /* There's no need to force the menu size
 to be recalculated. */

 DrawMenuBar();
}

By the way, if you place a different number in the procID of a MENU resource, a corresponding MDEF must be available at runtime or the Menu Manager is particularly unforgiving; i.e., a system error will occur.

Using a Window Definition

WDEFs usually don't need to communicate with an application because they just draw the window frame. The contents of a window are (and should) always be handled by the application itself in its update function. Of course that doesn't mean you can't include a window definition in your application code. It's just not as necessary.

If you're going to write a separate WDEF, like we did, you still have one thing you must do: pass the correct procID to NewWindow(). "Inside Macintosh Volume I," page 298, explains this whole process in detail. Since the standard WDEF's resource ID is 0, most programmers don't know they're calculating the procID like this when they create a window:

 procID = (16 * resource ID) + variation code

Obviously (16 * 0) + noGrowDocProc would still equal noGrowDocProc, so most of us never notice this. Here's an example in context:

UseRealWDEF() {
 WindowPtr myWindow;

 myWindow = NewWindow(p, bounds, title, visible,
 (16 * WDEF_ID) + noGrowDocProc, behind,
 goAwayFlag, refCon);
}

However, if your window definition is going to reside in your application code, you'll have to do things a bit differently. If you use the six byte jump table WDEF in a resource (like we did with the MDEF), you're going to have to initialize it first before you pass its ID to NewWindow(). Why? Because the WDEF code is executed when the window is created. If you don't have the address to the function stored in the jump table, the ROM will jump to the flaming pits of Hell. Not a nice thing to happen. So here's how it can be done correctly:

UseJumpTableWDEF() {
 JumpHandle windowDefJump;
 WindowPtr myWindow;

 /* Get a handle to the WDEF. */
 windowDefJump
 = (JumpHandle)GetResource('WDEF', WDEF_ID);

 /* If the first word of the WDEF resource 
 contains 0x4ef9 then there's no need to 
 initialize the jump instruction here. */
 
 /* Initialize the WDEF to point to your code. */
 (*windowDefJump)->function
 = WindowDefinitionFunction;

 /* Now it's safe to create the window. */
 myWindow = NewWindow(p, bounds, title, visible,
 (16 * WDEF_ID) + noGrowDocProc, behind,
 goAwayFlag, refCon);
}

If you'd rather create the jump table at runtime (like we also did with the menu), there is one limitation: your window definition won't be able to respond to a wNew message (See "Inside Macintosh Volume I," pages 298-302). This is because there isn't any real resource to pass to NewWindow() in the procID, and a wNew message is executed only when the window is created. A real chicken-before-the-egg problem. But, most of the time this is no big deal, so here's how it's done:

UseOnlyAJumpTable() {
 WindowPtr myWindow;
 JumpHandle windowDefJump;

 /* Create window, but don't make it visible. */
 myWindow = NewWindow(p, bounds, title, FALSE,
 noGrowDocProc, behind, goAwayFlag, refCon);

 /* Allocate a JumpRecord on the heap. */
 windowDefJump
 = (JumpHandle)NewHandle(sizeof(JumpRecord));

 /* Init JumpRecord to point to your code. */
 (*windowDefJump)->instruction = JMP_INSTRUCTION;
 (*windowDefJump)->function = WindowDefintionFunction;
 
 /* Point windowDefProc at JumpRecord on heap. */
 ((WindowPeek) myWindow)->windowDefProc
 = (Handle) windowDefJump;
 
 /* Now you can make the window visible. */
 ShowWindow(myWindow);
}

Whatever you do, don't try the evil mutant method again! Here's a real dangerous way to get your code executed:

DangerousMethodForWindows() {
 WindowPtr myWindow;

 /* Create window, but don't make it visible. */
 myWindow = NewWindow(p, bounds, title, FALSE,
 noGrowDocProc, behind, goAwayFlag, refCon);

 /* Point windowDefProc at empty block on heap. */
 ((WindowPeek) myWindow)->windowDefProc
 = NewHandle(0);

 /* Arrggh!
 Aim the block's master pointer at your code!? */
 *((WindowPeek) myWindow)->windowDefProc
 = (Ptr) WindowDefintionFunction;

 /* Make the window visible. */
 ShowWindow(myWindow);
}

Never fornicate master pointers like this! Enough said. Figure 4 diagrams the right and wrong way to point to your code.

Our Solution

So, there are many methods to connect an application to a definition function. Certain methods to avoid entirely. But which is best? There is no best way; it depends entirely upon what you're trying to do. Still, Apple suggests applications that ship should contain these definitions as separate resources. The key word there is ship.

Also remember that Apple doesn't always follow their own advice. And it's advice, not the law. The menu and window definitions used in HyperCard and the new MacPaint (which was developed at Apple before Claris even existed) are not contained in separate MDEF and WDEF resources. However, Bill Atkinson and other folks in Cupertino know better than to use the dangerous methods described above.

The reality of it is that during development it's much easier to keep the definition in your application code, especially if you use LightspeedC. Moving a separately compiled project in and out of another resource file is a real pain. If you write the definitions correctly, it's not that much trouble to move them into a separate code resource at the end of your development cycle, providing you're not doing the last compilation the day before you shrink wrap your product.

When we told a few folks in Apple Tech Support that we were going to write this article, they encouraged us to code our example with a separate MDEF and WDEF. Everyone could use them then. Not only would our source code be handy, but folks could just use our xDEFs like libraries. Universal code. What a great idea!

That was easy with the WDEF, but how do you share global data with a separate MDEF? Well, we had to get just a little bit sneaky ...

Communicating with TearOffMDEF

So you've probably skipped ahead and looked at the code by now. Confused? Serves you right for skipping ahead. It's really simple. All we do is provide the basic tear-off mechanism and the logic to highlight and unhighlight menu items. You write all the hard stuff in your application.

What? Isn't the MDEF separate? Sure, but it's not clairvoyant. Somebody has to draw the menu. TearOffMDEF passes application specific requests made by the ROM back to the application. The application must first create the palette window to be torn off, provide three functions for the MDEF to use, and create and initialize a structure that will be used to contain global information.

Our TearOffMDEF communicates with an application via a TearOffMenuGlobals structure in a TOMG resource. What's a TOMG resource? Well, it's a 28-byte block with nothing but zeroes in it. But we'll fill it up quickly. At runtime, TearOffMDEF uses the menuID of the MenuInfo record passed to it by the Macintosh ROM to find a TOMG resource of the same ID.

Okay, let's make sure we're clear on this. A MENU resource has an ID. One of its fields also contains an ID. They're usually the same, too. Obviously this sub-ID can be found with a handle to the menuInfo record, because the menuInfo is basically a duplicate of the original MENU resource. TearOffMDEF uses this sub-ID to find a TOMG resource. Why? This way the application and the MDEF can share global information without ever referencing a compiled offset of address register 5. They don't have to share any of the same source code files. They just need to define the structures the same way.

Yes, we thought it was pretty clever.

Here's the way the global structure should be declared by the application:

typedef struct TearOffMenuGlobals {
 void (*drawMenuProc)();
 short (*findItemProc)();
 void (*hiliteItemProc)();
 SysEnvRec *environment;
 WindowPtr paletteWindow;
 Point position;
 short currentItem;
 Boolean itemHilited;
} TearOffMenuGlobals, *TearOffMGlobalsPtr, **TearOffMGlobalsHdl;

There must be one of these structures for each of the menus that will be torn off. The TearOffMenuGlobals structure contains eight elements:

drawMenuProc:
A function pointer to a Pascal-type procedure which draws the contents of the menu in global coordinates within the WMgrPort.
findItemProc:
A function pointer to a Pascal-type procedure which returns the number of the menu item where the mouse is currently located.
hiliteItemProc:
A function pointer to a Pascal-type procedure which highlights or unhighlights a given menu item.
environment:
A pointer to a SysEnvRec used to test if the 64K ROMs are present.
paletteWindow:
A WindowPtr used to calculate menuWidth, menuHeight, and the structure boundary from the window's portRect.
currentItem:
A short int containing the number of the currently highlighted menu item.
position:
A Point passed to the application from TearOffMDEF indicating the top, left point, in global coordinates, of a torn-off menu.
itemHilited:
A Boolean used internally by TearOffMDEF.

All of these elements, except position and itemHilited, must be initialized by the application before a tear-off menu is inserted into the menu list.

Remember, the application is completely responsible for the appearance of the menu. A set of the three following procedures must be declared in the application for each tear-off menu:

pascal void DrawMenuProc(destRect)
Rect *destRect;  /* The menu rectangle in the current
 grafPort where the application should
 draw the menu. */

pascal short FindItemProc(mousePt)
Point mousePt; /* The point in which the mouse is
 currently located, relative to the
 top, left of the menu rectangle. */

 /* This routine must return the 
 number of the menu item where the 
 mouse is currently located. */

pascal void HiliteItemProc(destRect, item, hilite)
Rect *destRect;   /* The menu rectangle in the current
 grafPort. */
short item; /* The number of the item to be
 highlighted or unhighlighted. */
Boolean hilite;  /* A flag that determines the state
 of the highlighting. True means
 highlight the item, false means
 unhighlight. */

Inside TearOffMDEF

If you want to take a closer look at our TearOffMDEF code, pay particular attention to a few points.

We must refer to QuickDraw's globals in a peculiar fashion. Why? Since we're not an application, we can't call InitGraf() to initialize any compiler offsets to address register 5. We have to set up a pointer to these globals via the low-memory location CurrentA5 and refer to them by explicit dereference of that pointer. A bit inconvenient, but it means that when you want to draw in white it doesn't come out looking like chicken scratches.

When we track the mouse to correctly highlight items, we make sure the ROM will correctly flash the item when the mouse is released. Getting the menu to flash is more than just making the algorithm in your code function in a certain way. Specifically, you must insert enough fake items in the MENU resource so that the Menu Manager thinks the item you chose exists when it calls CountMItems(), otherwise it won't flash it.

Furthermore, our code expects the Menu Manager to tell us the mouse is in position (0, 0) when it's trying to flash the item. An undocumented feature, true, but it hasn't changed yet. This doesn't happen if the menu is called via PopUpMenuSelect() (and no, we don't know why), but since we don't think pop-up menus should be torn off, we don't even worry about it.

And how does the menu tear off? If the Menu Manager calls TearOffMDEF and the mouse is outside the menu rectangle by more than 15 pixels, our DragMenu() routine is called. Simple. DragMenu() follows the mouse around (just like DragGrayRgn() in the ROM) with a gray outline of the menu. Notice that the outline doesn't flash like HyperCard or the new MacPaint. DragMenu() retains control until the mouse button is released or the mouse returns to the menu rectangle or menu bar. If the mouse returns the code returns. If the mouse button is released outside the menu rectangle, we set the item selected to -1 and place the top, left corner of where the palette should be drawn in the position field of the appropriate TearOffMenuGlobals structure.

Using TearOffMDEF in Your Application

If you intend to use TearOffMDEF, you're going to have to move the MDEF code resource file into your application. If you're using LightspeedC, just paste it into the common resource file with ResEdit. If you're translating this to MPW C, then you can use Rez to put it all together at once.

By the way, there is one very important thing to remember when you create an MDEF (or another kind of xDEF), always make sure it's resource flags are set to non-purgeable. Why? The ROM doesn't like it when a code resource gets purged at runtime. Any heap compaction can force this to happen.

Also, there's another REAL thrill you need to work around if you compile this MDEF. It's a conflict with LightspeedC and our favorite ROM. As we all know, the Menu Manager passes the menu item to be manipulated to an MDEF by address. Usually the item is located on the stack. But when the Menu Manager repeatedly calls your code to cause a menu item to flash, it passes ToolScratch as the address of the item. Why? Who knows. The conflict occurs because LightspeedC stores the address of the separate code resource (in this case, the MDEF) in ToolScratch as soon as that code is called by the ROM. Why? This allows a programmer (who doesn't want to use in-line assembly) to find out exactly where the code exists in memory by simply looking in ToolScratch. A perfectly legal and very clever use of that low-memory global.

Believe it or not, the ROM's use of ToolScratch is definitely Apple's problem. When we told Paul Mercer in Apple Tech Support about this, he even filed an official bug report. Is that service or what?

To work around the problem, Michael Kahl, the author of LightspeedC, says he may change his glue for code resources in the soon-to-be-released version 3.0 of his compiler. Until then, both Apple and Mike suggest patching the MDEF after it's compiled. A real pain we know, but use ResEdit or FEdit to change hex string '21c809ce' at hex offset 14 in the MDEF resource to '4e714e71.' This substitutes two 'nop' instructions for a 'move.l a0,ToolScratch.'

Mike Kahl says you can also modify your copy of the compiler so it never generates these instructions when the code resource is compiled. The glue in versions 2.13 and 2.15 is located in code resource number 11, also at hex offset 14. The patch is identical. Remember, do it to a copy.

Let us repeat that the segment in which your application's procedures are declared must remain locked at runtime, the ROM will not chase your procedures around. Place it in your main segment and you shouldn't have to worry.

Remember the application must move the palette window and make it visible when MenuSelect() returns -1 from a tear-off menu. We'll explain how we keep the little beggars floating in just a bit.

Also, when the user moves the palette or clicks the mouse inside it, some response is necessary. If you want to emulate the Menu Manager and its drawing, finding, and highlighting of menus, your application must do this itself for floating palettes. We went the easy route and just used the same three routines again that are called by the MDEF. They're already in the application code, right? We're lazy and we highly recommend this lack of effort.

Inside PaletteWDEF

Our PaletteWDEF is pretty straightforward. It creates windows almost identical to those in HyperCard or the new MacPaint. The main difference being PaletteWDEF will respond to a Window Manager request to unhighlight the title bar. Also, the drop shadow created by PaletteWDEF more closely matches a menu's drop shadow than an ordinary window.

The only real tricky thing in the code is where we create the pattern to fill the title bar. Why? Windows are drawn into the Window Manager port. The origin is always in the top, left corner of the screen. Patterns are always aligned to the origin. Depending on where the top, left corner of the window will be, the pattern may not align when the window is drawn.

Apple's standard WDEF, originally written by Andy Hertzfield, uses a rather undocumented QuickDraw global called patAlign to make sure everything looks correct. If you look at the interface files to most high-level language compilers available for the Macintosh, you won't even find this field defined. It is in the MPW assembler equates though.

Even though we defined our own QuickDraw structure, we decided not to use a global not even discussed in any Apple documentation that we could find. Instead, we create the pattern on the fly. It's a very regular pattern, each horizontal line contains either 55, 55, 55 etc. or AA, AA, AA etc. After we wrote this 'hack' we decided to disassemble HyperCard and see how Bill Atkinson did it. You guessed it, the same way.

Keeping Palettes Above Documents

The first thing you need to know about palettes to keep them floating is where they are and where the top-most document is. Specifically, where in the window list. Window list? Yes, the Window Manager keeps a linked list of all windows beginning in a low-memory global called WindowList. It points to the first window and it's very handy. In fact FrontWindow(), in the Mac ROM, steps through this list to find the top-most visible window and then returns a pointer to it.

We wrote our own version of FrontWindow() called LocateWindows(). It doesn't return a pointer -- instead it initializes four global variables called TopWindow, TopPalette, BottomPalette and TopDocument. It's called once during the beginning of our event loop. But why do we need to know all this?

If your application is only going to use one document window, then perhaps you can keep a pointer to it in a global variable and test to see if it's visible or not. And if you only have one palette, or a fixed number of palettes, you can maintain a series of pointers to them. You can even create all of these windows at once so you know what order they're in. Right?

Well, what if somebody uses QuicKeys or the public domain Windows DA to change the order of the windows? And if you want your palettes to have a dynamic order or you need more than one document then you're going to have to keep track of a few things. These four globals are the minimum you need to know about the window list.

When a document window is created it can appear behind the bottom palette only if you know which window is the bottom palette. Look at our example code carefully and notice the care and feeding of these globals. Every operation to keep palettes floating above documents depends on LocateWindows().

And initializing these four globals isn't the only thing LocateWindows() does. If it finds a non-application window between the palettes and top-most document, it moves that window. If it finds a document above one of the palettes, it moves it below the bottom palette.

Usually FrontWindow() is also called by an application to tell if that application is active. We wrote a small function called ActiveWindow(), to which we pass a window pointer and test it to make sure it is either the TopDocument or one of the palettes.

Now the hard part.

Normally if you click in a window, it's brought in front of all other windows. SelectWindow() is always used to do this. But you can't call SelectWindow() for a document if a palette is visible. How do you keep it from being brought to the front? Use SendBehind() instead. Right. Just send the document behind the BottomPalette.

Look carefully at our code to see how we did this in a function called DoSelectWindow(). You'll also notice that we use two low-level Window Manager routines called CalcVisBehind() and PaintOne() after we move the document. Why? If a window is sent behind a window that is above it, the Window Manager must redraw parts of the affected windows. We speed up this process by calculating the minimum area to be redrawn. See "Inside Macintosh Volume I," page 286, for details on this process.

Another ROM call to avoid is DragWindow(). Annoyingly, DragWindow() will always bring a window to the front if the window pointer passed to it is not already in front. You can hold the command key down while you drag a window to keep this from happening, but there's no clean way to 'depress' this key from within your code. Instead, look at DoDragWindow() and Drag() in our code. We had to completely rewrite this ROM routine. DoDragWindow() handles all of the logic of when and where to drag, and Drag() actually moves an outline of the window around the screen.

Are you seeing a pattern yet? Right. To suspend palettes above documents, never call a function which will directly move a document to the front if a palette is visible.

Still, problems with window highlighting, the appearance of controls, and the grow icon can occur while doing our juggling act with the Window Manager. Basically, you can't depend on the ROM to always activate or deactivate a document -- it's not really the front window. "Inside Macintosh Volume I," page 280 contains a discussion of two low-memory variables called CurActivate and CurDeactive. Clever manipulation of these globals can direct the Event and Window Managers to activate or deactivate something other than the front window. It's all legal and it works just fine.

But Apple may change these low-memory globals one of these days. With the advent of MultiFinder and the Layer Manager, the Window Manager has been changing. Just to be safe, we've developed a different technique to do this that doesn't write to these locations. It's a bit of a pain, but it works. Essentially, we must explicitly highlight windows, activate controls, and redraw grow icons when documents are moved within the window list. Take a look at two of our routines called ActivateDocument() and HiliteUserWindows() -- they're pretty straightforward.

Remember, the whole process of floating a palette is a charade. First you fool the ROM, and then you can fool the user.

How to Read the Code

First we'd like to acknowledged our debt to the folks in Apple Tech Support for providing us with some helpful clues. Also, many thanks to Mike Kahl and the rest of the gang on Compuserve's LVTForum, home to many LightspeedC and Pascal nerds (Are you a THINKer? Just type GO THINK when you're on CIS). And finally, thanks to David Smith for believing we could pull it off.

We've tried to give as complete an example as possible. Although our code can be used almost like libraries, the whole idea is to share needed information with the rest of the Mac community. And to show off, of course. If you read this and come up with some better solutions or (horror!) bugs, please let us know.

You might have already noticed that we wrote all of our code in C. Why? Sometimes we want to send the wrong type somewhere. Of course, we don't recommend this, we just do it. At least we didn't use any in-line assembly.

Actually, we designed the code to be easily translated into Pascal. Too bad if you're using COBOL though. We didn't use too many special C 'tricks' that aren't obvious anyway. If you look at the application code, you may notice that it's rather repetitive in sections that deal with updating or drawing into palette windows. In our own project we're developing we use arrays of function pointers indexed with a palette's ID number (stored in the window refCon field) to make everything much shorter and more elegant. We thought this would probably confuse Pascal programmers though.

Although we wrote this in LightspeedC, the resources are listed in MPW Rez format. Sorry, but we never use RMaker. It's just not powerful enough.

This whole thing is a monster. Our advice is: buy the source code disk from David. It's actually contained in three LightspeedC project files. Here's a breakdown:

TearOffMDEF.project

TearOffMDEF.c The menu definition.

PaletteWDEF.project

(No need to link with MacTraps)

PaletteWDEF.c The window definition.

TearOffPalette.project

(All of these go in the main segment)

TearOffPalette.c The main program.

Interface.c Adjusts the use of color at runtime.

Error.c Error and grow zone function.

Window.c Creation and updating of windows.

Dialog.c About dialog and utilities.

Menu.c The routines called by the MDEF.

Palette.c The code that floats the palettes.

(Place this in a separate segment)

Initialize.c Initialization and creation of palettes.

(The include files are used by all of the sources)

Constants.h include files and our definitions.

Variables.h Global variables.

(MPW Rez format resource file)

TearOffPalette.project.r

(Move the MDEF and WDEF into this file with ResEdit)

TearOffPalette.project.rsrc

Finally, if you program in C, you had better try to maintain a style of some kind. If you don't, nobody will ever be able to make heads or tails of your code, including you. We've tried to use classic indentation, but we'd like to clarify our nomenclature.

FunctionName()
Always an initial capital.
GlobalVariable
Just like the functions.
localVariable
The first letter is always lower case.
A_CONSTANT
All capitals, underscores dividing words.

Of course many of Apple's globals and constants don't follow our rules. Oh well. It might not make sense to you, but at least we're consistent. On to the code!

/*
----------T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright, 1987, 1988 by Impulse Technologies,
 Inc., all rights reserved. 
 
 Filename:Constants.h
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD */

/*
----------------------------------------
INCLUDE DEFINITIONS */

#include "Color.h"
#include "ColorToolbox.h"
#include "ControlMgr.h"
#include "DeskMgr.h"
#include "DialogMgr.h"
#include "EventMgr.h"
#include "FileMgr.h"
#include "FontMgr.h"
#include "MacTypes.h"
#include "MemoryMgr.h"
#include "MenuMgr.h"
#include "OSUtil.h"
#include "PackageMgr.h"
#include "Quickdraw.h"
#include "ResourceMgr.h"
#include "SegmentLdr.h"
#include "StdFilePkg.h"
#include "TextEdit.h"
#include "ToolboxUtil.h"
#include "WindowMgr.h"

/*
--------------------------------------------------------
SYSTEM CONSTANTS */

#define nil 0
#define NULL 0
#define zoomDocProc 8
#define zoomNoGrow 12

/*
--------------------------------------------------------
ENVIRONMENT */

#define VERSION_REQUESTED 1

#define WAIT_NEXT_EVENT_TRAP_NUMBER 0x60
#define UNIMPLEMENTED_TRAP_NUMBER 0x9f

/*
--------------------------------------------------------
MEMORY */

#define MEMORY_BUFFER_SIZE 0x8000 /* 32K. */

/*
--------------------------------------------------------
EVENTS */

#define SLEEP_DURATION 50

/*
--------------------------------------------------------
WINDOWS */

#define VISIBLE true
#define NOT_VISIBLE false
#define BRING_TO_FRONT (WindowPtr) -1
#define SEND_BEHIND nil
#define GO_AWAY_BOX true
#define NO_GO_AWAY_BOX false
#define NO_REFCON nil

/*
--------------------------------------------------------
PALETTES */

enum {
 TOOL_PALETTE,
 PATTERN_PALETTE,
 COLOR_PALETTE,
 DOCUMENT_WINDOW
};

#define PALETTE_COUNT 3

#define PALETTE_WDEF_ID 128
#define TEAROFF_MDEF_ID 128

/*
--------------------------------------------------------
TEAROFF MENU GLOBALS */

typedef struct TearOffMenuGlobals {
 void (*drawMenuProc)();
 short (*findItemProc)();
 void (*hiliteItemProc)();
 SysEnvRec *environment;
 WindowPtr paletteWindow;
 Point position;
 short currentItem;
 Boolean itemHilited;
} TearOffMenuGlobals, *TearOffMGlobalsPtr, **TearOffMGlobalsHdl;

#define TEAR_OFF_MENU_GLOBALS_TYPE 'TOMG'

/*
--------------------------------------------------------
TOOL PALETTE */

#define TOOLS_ACROSS 4
#define TOOLS_DOWN 4
#define TOOL_COUNT ((TOOLS_ACROSS * TOOLS_DOWN) + 1)
#define DEFAULT_TOOL 4
#define TOOL_PICT_ID 128

/*
--------------------------------------------------------
PATTERN PALETTE */

#define PATTERNS_ACROSS 8
#define PATTERNS_DOWN 8

#define PATTERN_COUNT ((PATTERNS_ACROSS 
 * PATTERNS_DOWN) + 1)

#define PATTERN_ITEM_WIDTH 16
#define PATTERN_ITEM_HEIGHT 16

#define DEFAULT_PATTERN 1
#define PATTERNS_ID 128

/*
--------------------------------------------------------
COLOR PALETTE */

#define COLORS_ACROSS 16
#define COLORS_DOWN 16
#define COLOR_COUNT ((COLORS_ACROSS * COLORS_DOWN) + 1)
#define COLOR_ITEM_WIDTH 8
#define COLOR_ITEM_HEIGHT 8
#define DEFAULT_COLOR 1

/*
--------------------------------------------------------
DOCUMENTS */


#define TITLE_BAR_HEIGHT 18
#define SCROLL_BAR_SIZE 16
#define SCREEN_MARGIN 4
#define MIN_WINDOW_WIDTH 80
#define MIN_WINDOW_HEIGHT 80
#define HORIZONTAL_WINDOW_OFFSET 8
#define VERTICAL_WINDOW_OFFSET 8
#define DOCUMENT_COUNT 8
#define DOCUMENT_TITLE_STRING_ID 128

/*
--------------------------------------------------------
MENUS */

enum {
 APPLE_MENU_INDEX,
 FILE_MENU_INDEX,
 EDIT_MENU_INDEX,
 TOOL_MENU_INDEX,
 PATTERN_MENU_INDEX,
 COLOR_MENU_INDEX
};

enum {
 APPLE_MENU_ID = 128,
 FILE_MENU_ID,
 EDIT_MENU_ID,
 TOOL_MENU_ID,
 PATTERN_MENU_ID,
 COLOR_MENU_ID
};

#define MENU_COUNT 6
#define MENU_ID_OFFSET APPLE_MENU_ID

#define TEAR_OFF_MARGIN 15
#define MOVE_PALETTE_ITEM (-1)

/*
--------------------------------------------------------
APPLE MENU ITEMS */

#define ABOUT_ITEM 1

/*
--------------------------------------------------------
EDIT MENU ITEMS */

enum {
 UNDO_ITEM = 1,
 CUT_ITEM = 3,
 COPY_ITEM,
 PASTE_ITEM,
 CLEAR_ITEM
};

/*
--------------------------------------------------------
FILE MENU ITEMS */

enum {
 NEW_ITEM = 1,
 CLOSE_ITEM = 3,
 QUIT_ITEM = 5
};

/*
--------------------------------------------------------
ABOUT DIALOG */

#define ABOUT_DIALOG_ID 128



/*
--------------------------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies,
 Inc., all rights reserved. 
 
 Filename:Variables.h
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD */



/*
--------------------------------------------------------
GLOBAL VARIABLE DECLARATIONS */

#ifdef GLOBAL_VARIABLES

SysEnvRec Environment;
EventRecord Event;

GDHandle MaxDevice;

MenuHandle Menus[MENU_COUNT];

TearOffMGlobalsHdl TearOffs[PALETTE_COUNT];
WindowPtr Palettes[PALETTE_COUNT];

WindowPtr TopWindow;
WindowPtr TopPalette;
WindowPtr BottomPalette;
WindowPtr TopDocument;

Rect ToolRects[TOOL_COUNT];
Rect PatternRects[PATTERN_COUNT];
Rect ColorRects[COLOR_COUNT];

short PositionCounter;

long Sleep;

Boolean ClosingAll;
Boolean Quitting;
Boolean Finished;

Boolean OutOfMemory;
Boolean WNEIsImplemented;
Boolean MouseInMenu;

Boolean ColorMenuVisible;
Boolean PalettesVisible[PALETTE_COUNT];

Handle MemoryBuffer;

#else

extern SysEnvRec Environment;
extern EventRecord Event;

extern GDHandle MaxDevice;

extern MenuHandle Menus[MENU_COUNT];

extern TearOffMGlobalsHdl TearOffs[PALETTE_COUNT];
extern WindowPtr Palettes[PALETTE_COUNT];

extern WindowPtr TopWindow;
extern WindowPtr TopPalette;
extern WindowPtr BottomPalette;
extern WindowPtr TopDocument;

extern Rect ToolRects[TOOL_COUNT];
extern Rect PatternRects[PATTERN_COUNT];
extern Rect ColorRects[COLOR_COUNT];

extern short PositionCounter;

extern long Sleep;

extern Boolean ClosingAll;
extern Boolean Quitting;
extern Boolean Finished;

extern Boolean OutOfMemory;

extern Boolean OutOfMemory;
extern Boolean WNEIsImplemented;
extern Boolean MouseInMenu;

extern Boolean ColorMenuVisible;
extern Boolean PalettesVisible[PALETTE_COUNT];

extern Handle MemoryBuffer;

#endif



/*
--------------------------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:TearOffPalette.c
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD
 Segment: Main

--------------------------------------------------------
DESCRIPTION

 TearOffPalette is a demonstration of a very portable method of implementing 
tear-off menus and floating palettes. This technique uses external MDEF 
and WDEF code resources. The MDEF communicates with the application via 
a TearOffMenuGlobals structure in a TOMG resource.
 
 See TearOffMDEF.c for an explanation of the TearOffMenuGlobals structure 
and the routines an application must contain to use TearOffMDEF.
 
 See PaletteWDEF.c for an explanation of the behavior and use of PaletteWDEF.
 
 TearOffPalette is designed to keep palettes floating above documents 
without directly writing to low-memory Window Manager globals.
 
 A Color menu and palette will be displayed when Color QuickDraw is present 
and the depth of the screen allows 256 colors.
 
 Under MultiFinder and at any Suspend or Resume event, TearOffPalette 
will hide or show all visible palettes.
 
 IMPORTANT! TearOffPalette.project.rsrc must contain the TearOffMDEF 
and PaletteWDEF resources created by compiling their respective source 
files. */

/*
--------------------------------------------------------
GLOBAL CONSTANT 
DEFINITIONS AND VARIABLE DECLARATIONS */

#define GLOBAL_VARIABLES

#include "Constants.h"
#include "Variables.h"

/*
--------------------------------------------------------
EXTERNAL FUNCTION 
DECLARATIONS */

extern void Initialize();         /* Initialize.c */

extern void DoAbout();             /* Dialog.c */

extern void AdjustInterface();         /* Interface.c */

extern void LocateWindows();          /* Palette.c */
extern short ActiveWindow();          /* Palette.c */
extern void DoSelectWindow();           /* Palette.c */
extern void DoDragWindow();           /* Palette.c */
extern void HiliteUserWindows();        /* Palette.c */

extern void ActivateDocument();         /* Window.c */
extern void UpdateDocument();           /* Window.c */
extern void SizeDocument();           /* Window.c */
extern void UpdatePalette();          /* Window.c */
extern void NewDocument();          /* Window.c */
extern void CloseDocument();          /* Window.c */

extern pascal short FindToolItem();     /* Menu.c */
extern pascal void HiliteToolItem();    /* Menu.c */
extern pascal short FindPatternItem();  /* Menu.c */
extern pascal void HilitePatternItem(); /* Menu.c */
extern pascal short FindColorItem();    /* Menu.c */
extern pascal void HiliteColorItem();   /* Menu.c */
extern void MovePalette();          /* Menu.c */

/*
--------------------------------------------------------
FORWARD FUNCTION DECLARATIONS */

void DoEvent();
void MouseDownEvent();
void KeyEvent();
void ActivateEvent();
void UpdateEvent();
void SuspendResumeEvent();
void MenuEvent();

void AppleMenu();
void FileMenu();
void EditMenu();
void ToolMenu();
void PatternMenu();
void ColorMenu();

void ContentEvent();
void GrowEvent();
void InvalGrow();
void CloseEvent();

/*
--------------------------------------------------------
TEAROFF PALETTE */

main() {

 Initialize();
 UnloadSeg(Initialize);
 
 DoAbout();
 
 while((!Finished) && (!OutOfMemory)) {
 AdjustInterface();
 DoEvent();
 }
}

/*
--------------------------------------------------------
DO EVENT */

void DoEvent() {
Boolean result;

 if(WNEIsImplemented) {
 result = WaitNextEvent(everyEvent, &Event, Sleep, 
 nil);
 }
 else {
 SystemTask();
 result = GetNextEvent(everyEvent, &Event);
 }
 /* Find all application windows in the window list, 
 and adjust their order if necessary. 
 IMPORTANT! Always call LocateWindows() after getting 
 the event. */
 LocateWindows();
 
 if(result) {
 
 switch(Event.what) {
 
 case mouseDown:
 
 if(!(ClosingAll || Quitting)) {
 MouseDownEvent();
 }
 break;
 
 case keyDown:
 case autoKey:
 
 if(!(ClosingAll || Quitting)) {
 KeyEvent();
 }
 break;
 
 case updateEvt:
 UpdateEvent();
 break;
 
 case activateEvt:
 ActivateEvent();
 break;
 
 case app4Evt:
 SuspendResumeEvent();
 }
 }
 else {
 
 if((ClosingAll || Quitting) && (Event.what
 == nullEvent)) {
 /* If the application is quitting or the option 
 key was held down when closing a window, the
 top visible window is closed during each pass 
 through the event loop until all windows are
 closed. */
 
 if(TopWindow != nil) {
 CloseEvent(TopWindow);
 }
 else {
 ClosingAll = false;
 
 if(Quitting) {
 Finished = true;
 }
 }
 }
 }
}
 
/*
--------------------------------------------------------MOUSEDOWN EVENT

 AcitiveWindow() replaces most traditional calls to FrontWindow(). All 
use of SelectWindow() and DragWindow() is replaced by calls to DoSelectWindow() 
and DoDragWindow(). This ensures mouseDown events in application windows 
handle floating palettes properly. */

void MouseDownEvent() {
 WindowPtr whichWindow;
 short whichPart;
 
 MouseInMenu = false;
 
 whichPart = FindWindow(Event.where, &whichWindow);
 
 switch(whichPart) {
 
 case inMenuBar:
 MouseInMenu = true;
 MenuEvent(MenuSelect(Event.where));
 break;
 
 case inSysWindow:
 SystemClick(&Event, whichWindow);
 break;
 
 case inContent:  
 if(!ActiveWindow(whichWindow)) {
 DoSelectWindow(whichWindow);
 }
 else {
 ContentEvent(whichWindow);
 }
 break;
 
 case inDrag: 
 DoDragWindow(whichWindow);
 break;
 
 case inGrow: 
 if(!ActiveWindow(whichWindow)) {
 DoSelectWindow(whichWindow);
 }
 else {
 GrowEvent(whichWindow);
 }
 break;
 
 case inGoAway: 
 if(!ActiveWindow(whichWindow)) {
 DoSelectWindow(whichWindow);
 }
 else {
 if(TrackGoAway(whichWindow, Event.where)) {
 CloseEvent(whichWindow);
 }
 }
 break;
 
 case inZoomIn:
 case inZoomOut:
 if(TrackBox(whichWindow, Event.where, 
 whichPart)) {
 SetPort(whichWindow);
 EraseRect(&whichWindow->portRect);
 ZoomWindow(whichWindow, whichPart, false);
 SizeDocument(whichWindow);
 }
 }
}

/*
--------------------------------------------------------
KEYDOWN AND AUTOKEY EVENT */

void KeyEvent() {
 char whichChar;
 
 whichChar = Event.message & charCodeMask;

 if((Event.modifiers & cmdKey) != 0) {
 
 if(Event.what != autoKey) {
 MenuEvent(MenuKey(whichChar));
 }
 }
}

/*
--------------------------------------------------------
ACTIVATE EVENT */

void ActivateEvent() {
 WindowPtr whichWindow;
 
 /* Ensure all application windows are hilited 
 correctly. */
 HiliteUserWindows();
 whichWindow = (WindowPtr) Event.message;
 
 if(((WindowPeek) whichWindow)->refCon 
 != DOCUMENT_WINDOW) {
 /* Pass activate event from palette to
 TopDocument. */
 whichWindow = TopDocument;
 }
 if(whichWindow != nil) {
 /* Ensure document window's controls are properly 
 visible and hilited. */
 ActivateDocument(whichWindow, Event.modifiers
 & activeFlag);
 }
}

/*
--------------------------------------------------------
UPDATE EVENT */

void UpdateEvent() {
 WindowPtr whichWindow;
 
 whichWindow = (WindowPtr) Event.message;
 
 SetPort(whichWindow);
 
 BeginUpdate(whichWindow);
 
 if(((WindowPeek) whichWindow)->refCon 
 == DOCUMENT_WINDOW) {
 UpdateDocument(whichWindow);
 }
 else {
 UpdatePalette(whichWindow);
 }
 DrawControls(whichWindow);
 DrawGrowIcon(whichWindow);
 
 EndUpdate(whichWindow);
}

/*
--------------------------------------------------------
SUSPEND/RESUME EVENT 

 Hide all palettes when the application is not the active layer. This 
leaves the screen much less cluttered. */

void SuspendResumeEvent() {
 short index;
 
 for(index = 0; index < PALETTE_COUNT; index++) {
 
 if((Event.message & 1) == 0) {
 PalettesVisible[index] = ((WindowPeek) 
 Palettes[index])->visible;
 ShowHide(Palettes[index], false);
 }
 else {
 ShowHide(Palettes[index], 
 PalettesVisible[index]);
 PalettesVisible[index] = false;
 }
 }
 LocateWindows();
 HiliteUserWindows();
 
 if((((WindowPeek) TopWindow)->windowKind == userKind) 
 && (TopDocument != nil)) {
 /* Ensure the TopDocument's controls are properly 
 visible and unhilited. */
 ActivateDocument(TopDocument, Event.message & 1);
 }
}

/*
--------------------------------------------------------
MENU AND COMMAND KEY EVENT */

void MenuEvent(menuChoice)
long menuChoice;
{
 short whichMenu;
 short whichItem;
 
 /* HiWord(menuChoice). */
 whichMenu = menuChoice >> 16;
 /* LoWord(menuChoice). */
 whichItem = menuChoice & 0xFFFF;
 
 switch(whichMenu) {
 
 case APPLE_MENU_ID:
 AppleMenu(whichItem);
 break;
 
 case FILE_MENU_ID:
 FileMenu(whichItem);
 break;
 
 case EDIT_MENU_ID:
 EditMenu(whichItem);
 break;
 
 case TOOL_MENU_ID:
 ToolMenu(whichItem);
 break;
 
 case PATTERN_MENU_ID:
 PatternMenu(whichItem);
 break;
 
 case COLOR_MENU_ID:
 ColorMenu(whichItem);
 }
 HiliteMenu(0);
}

/*
--------------------------------------------------------
SELECT APPLE MENU ITEM */

void AppleMenu(whichItem)
short whichItem;
{
 Str255 daName;
 
 if(whichItem == ABOUT_ITEM) {
 DoAbout();
 }
 else {
 GetItem(Menus[APPLE_MENU_INDEX], whichItem, 
 &daName);
 /* Ignore the result returned by the ROM. */
 (void) OpenDeskAcc(&daName);
 }
}

/*
--------------------------------------------------------
SELECT FILE MENU ITEM */

void FileMenu(whichItem)
short whichItem;
{
 switch(whichItem) {
 
 case NEW_ITEM:
 NewDocument();
 break;
 
 case CLOSE_ITEM:
 CloseEvent(TopDocument);
 break;
 
 case QUIT_ITEM:
 Quitting = true;
 Sleep = 0;
 }
}

/*
--------------------------------------------------------
SELECT EDIT MENU ITEM */

void EditMenu(whichItem)
short whichItem;
{
 (void) SystemEdit(whichItem - 1);
}

/*
--------------------------------------------------------
SELECT TOOL MENU ITEM */

void ToolMenu(whichItem)
short whichItem;
{
 WindowPtr paletteWindow;
 
 paletteWindow = Palettes[TOOL_PALETTE];
 
 if(whichItem == MOVE_PALETTE_ITEM) {
 /* The menu has been torn off. */
 MovePalette(paletteWindow, (*TearOffs[TOOL_PALETTE])->position);
 }
 else {
 
 if(whichItem
 != (*TearOffs[TOOL_PALETTE])
 ->currentItem) {
 SetPort(Palettes[TOOL_PALETTE]);
 
 /* Unhilte the old item. */
 HiliteToolItem(&paletteWindow->portRect, 
 (*TearOffs[TOOL_PALETTE])->currentItem, 
 false);
 /* Hilite the new item. */
 HiliteToolItem(&paletteWindow->portRect, 
 whichItem, true);
 
 /* Set currentItem equal to the new item. */
 (*TearOffs[TOOL_PALETTE])->currentItem 
 = whichItem;
 }
 }
}

/*
--------------------------------------------------------
SELECT PATTERN MENU ITEM */

void PatternMenu(whichItem)
short whichItem;
{
 WindowPtr paletteWindow;
 
 paletteWindow = Palettes[PATTERN_PALETTE];
 
 if(whichItem == MOVE_PALETTE_ITEM) {
 /* The menu has been torn off. */
 MovePalette(paletteWindow, (*TearOffs[PATTERN_PALETTE])->position);
 }
 else {
 
 if(whichItem 
 != (*TearOffs[PATTERN_PALETTE])
 ->currentItem) {
 SetPort(Palettes[PATTERN_PALETTE]);
 
 /* Unhilte the old item. */
 HilitePatternItem(&paletteWindow->portRect, 
 (*TearOffs[PATTERN_PALETTE])
 ->currentItem, false);
 /* Hilite the new item. */
 HilitePatternItem(&paletteWindow->portRect, 
 whichItem, true);
 
 /* Set currentItem equal to the new item. */
 (*TearOffs[PATTERN_PALETTE])->currentItem = 
 whichItem;
 }
 }
}

/*
--------------------------------------------------------
SELECT COLOR MENU ITEM */

void ColorMenu(whichItem)
short whichItem;
{
 WindowPtr paletteWindow;
 
 paletteWindow = Palettes[COLOR_PALETTE];
 
 if(whichItem == MOVE_PALETTE_ITEM) {
 /* The menu has been torn off. */
 MovePalette(paletteWindow, 
 (*TearOffs[COLOR_PALETTE])->position);
 }
 else {
 
 if(whichItem
 != (*TearOffs[COLOR_PALETTE])
 ->currentItem) {
 SetPort(Palettes[COLOR_PALETTE]);
 
 /* Unhilte the old item. */
 HiliteColorItem(&paletteWindow->portRect, 
 (*TearOffs[COLOR_PALETTE])->currentItem, 
 false);
 /* Hilite the new item. */
 HiliteColorItem(&paletteWindow->portRect, 
 whichItem, true);
 
 /* Set currentItem equal to the new item. */
 (*TearOffs[COLOR_PALETTE])->currentItem = 
 whichItem;
 }
 }
}

/*
--------------------------------------------------------
CONTENT EVENT */

void ContentEvent(whichWindow)
WindowPtr whichWindow;
{
 Point mousePt;
 short item;
 
 SetPort(whichWindow);
 
 mousePt = Event.where;
 GlobalToLocal(&mousePt);
 
 /* Use the window's refCon to determine what type of    application 
window received the event. */
 switch(((WindowPeek) whichWindow)->refCon) {
 
 case TOOL_PALETTE:
 item = FindToolItem(mousePt);
 
 if(item
 != (*TearOffs[TOOL_PALETTE])
 ->currentItem) {
 /* Unhilte the old item. */
 HiliteToolItem(&whichWindow->portRect, 
 (*TearOffs[TOOL_PALETTE])->
 currentItem, false);
 /* Hilite the new item. */
 HiliteToolItem(&whichWindow->portRect, item, 
 true);
 
 /* Set currentItem equal to the new item. */
 (*TearOffs[TOOL_PALETTE])->currentItem 
 = item;
 }
 break;
 
 case PATTERN_PALETTE:
 item = FindPatternItem(mousePt);
 
 if(item 
 != (*TearOffs[PATTERN_PALETTE])
 ->currentItem) {
 /* Unhilte the old item. */
 HilitePatternItem(&whichWindow->portRect, 
 (*TearOffs[PATTERN_PALETTE])
 ->currentItem, false);
 /* Hilite the new item. */
 HilitePatternItem(&whichWindow->portRect, 
 item, true);
 
 /* Set currentItem equal to the new item. */
 (*TearOffs[PATTERN_PALETTE])->currentItem 
 = item;
 }
 break;
 
 case COLOR_PALETTE:
 item = FindColorItem(mousePt);
 
 if(item 
 != (*TearOffs[COLOR_PALETTE])
 ->currentItem) {
 /* Unhilte the old item. */
 HiliteColorItem(&whichWindow->portRect, 
 (*TearOffs[COLOR_PALETTE])
 ->currentItem, false);
 /* Hilite the new item. */
 HiliteColorItem(&whichWindow->portRect, 
 item, true);
 
 /* Set currentItem equal to the new item. */
 (*TearOffs[COLOR_PALETTE])->currentItem
 = item;
 }
 }
}

/*
--------------------------------------------------------
GROW EVENT */

void GrowEvent(whichWindow)
WindowPtr whichWindow;
{
 Rect growRect;
 long size;
 short width;
 short height;
 
 growRect.left = MIN_WINDOW_WIDTH;
 growRect.top = MIN_WINDOW_HEIGHT;
 growRect.right = (*GrayRgn)->rgnBBox.right;
 growRect.bottom = (*GrayRgn)->rgnBBox.bottom 
 - TITLE_BAR_HEIGHT;
 
 size = GrowWindow(whichWindow, Event.where, 
 &growRect);
 
 if(size != 0) {
 SetPort(whichWindow);
 
 InvalGrow(whichWindow);
 
 height = size >> 16; /* HiWord(size). */
 width = size & 0xFFFF; /* LoWord(size). */
 
 SizeWindow(whichWindow, width, height, true);
 /* Redraw the scroll bars and grow icon. */
 SizeDocument(whichWindow);
 }
}

/*
--------------------------------------------------------
INVAL GROW

 Invalidate the grow region of the window. This includes the area occupied 
by the scroll bars. */

void InvalGrow(whichWindow)
WindowPtr whichWindow;
{
 Rect updateRect;
 
 updateRect = whichWindow->portRect;
 
 updateRect.top = updateRect.bottom - SCROLL_BAR_SIZE;
 InvalRect(&updateRect);
 
 updateRect.top = whichWindow->portRect.top;
 
 updateRect.left = updateRect.right - SCROLL_BAR_SIZE;
 InvalRect(&updateRect);
}

/*
--------------------------------------------------------
CLOSE EVENT */

void CloseEvent(whichWindow)
WindowPtr whichWindow;
{
 WindowPeek front;
 
 if(Event.modifiers & optionKey) {
 ClosingAll = true;
 }
 if ((front = (WindowPeek) TopWindow) != nil) {
 
 if(front->windowKind < 0) {
 CloseDeskAcc(front->windowKind);
 }
 else {
 
 if((front->windowKind == userKind) 
 && (whichWindow != nil)) {
 
 if(((WindowPeek) whichWindow)->refCon 
 == DOCUMENT_WINDOW) {
 CloseDocument(whichWindow);
 
 if(TopPalette == TopWindow) {
 /* Find the new TopDocument. */
 LocateWindows();
 
 if(TopDocument != nil) {
 /* Ensure the TopDocument and its 
 controls are hilited properly. */
 ActivateDocument(TopDocument, 
 true);
 }
 }
 }
 else {
 
 if(TopDocument != nil) {
 /* Don't generate any events that 
 might cause the TopDocument to be 
 unhilited. */
 ShowHide(whichWindow, false);
 }
 else {
 /* Ensure an activate event is 
 generated in case the next visible 
 window does not belong to the 
 application. */
 HideWindow(whichWindow);
 }
 }
 }
 }
 }
}

/*
------------------T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:Window.c
 Font:  Monaco, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD
 Segment: Main */

/*
--------------------------------------------------------GLOBAL CONSTANT 
DEFINITIONS AND VARIABLE DECLARATIONS */

#include "Constants.h"
#include "Variables.h"

/*
--------------------------------------------------------
EXTERNAL FUNCTION DECLARATIONS */

extern short GoodNewPtr();             /* Error.c */
extern short GoodResource();         /* Error.c */

extern short GetMenuBarHeight();       /* Interface.c */

extern pascal void DrawToolMenu();      /* Menu.c */
extern pascal void HiliteToolItem();    /* Menu.c */
extern pascal void DrawPatternMenu();   /* Menu.c */
extern pascal void HilitePatternItem(); /* Menu.c */
extern pascal void DrawColorMenu();     /* Menu.c */
extern pascal void HiliteColorItem();   /* Menu.c */

/*
--------------------------------------------------------
FORWARD FUNCTION DECLARATIONS */

void NewDocument();
Rect *GetDocumentRect();

short NewScrollBars();
Rect *GetHScrollBarRect();
Rect *GetVScrollBarRect();

void CloseDocument();
void ActivateDocument();
void UpdateDocument();
void SizeDocument();

void MoveSizeScrollBar();

void UpdatePalette();



/*
--------------------------------------------------------
NEW DOCUMENT */

void NewDocument() {
 StringHandle title;
 WindowPtr theDocument;
 Rect windowRect;
 
 if(!GoodResource(title = GetString(DOCUMENT_TITLE_STRING_ID))) {
 return;
 }
 HLock(title);
 
 if(!GoodNewPtr(theDocument = NewWindow(nil,
 GetDocumentRect(&windowRect),
 *title,
 NOT_VISIBLE,
 zoomDocProc,
 BottomPalette,
 GO_AWAY_BOX,
 DOCUMENT_WINDOW))) {
 HUnlock(title);
 
 return;
 }
 if(!NewScrollBars(theDocument)) {
 CloseDocument(theDocument);
 PositionCounter += 1;
 HUnlock(title);
 
 return;
 }
 PositionCounter += 1;
 HUnlock(title);
 
 if(TopPalette != nil) {
 
 if(TopPalette != TopWindow) {
 /* Bring the TopPalette to the front and 
 generate activate event. */
 SelectWindow(TopPalette);
 }
 else {
 HiliteWindow(theDocument, true);
 }
 if(TopDocument != nil) {
 /* Ensure the TopDocument and its controls are 
 unhilited properly. */
 ActivateDocument(TopDocument, false);
 }
 }
 ShowWindow(theDocument);
}

/*
--------------------------------------------------------
GET DOCUMENT RECT */

Rect *GetDocumentRect(windowRect)
Rect *windowRect;
{
 short position;
 
 position = (PositionCounter + DOCUMENT_COUNT) 
 % DOCUMENT_COUNT; windowRect->left 
 = SCREEN_MARGIN
 + (HORIZONTAL_WINDOW_OFFSET * position);
 windowRect->top = GetMenuBarHeight() 
 + TITLE_BAR_HEIGHT + SCREEN_MARGIN
 + (VERTICAL_WINDOW_OFFSET * position);
 windowRect->right = screenBits.bounds.right 
 - SCREEN_MARGIN - (((DOCUMENT_COUNT - 1) 
 - position) * HORIZONTAL_WINDOW_OFFSET);
 windowRect->bottom = screenBits.bounds.bottom 
 - SCREEN_MARGIN - (((DOCUMENT_COUNT - 1) 
 - position) * VERTICAL_WINDOW_OFFSET);
 return(windowRect);
}

/*
--------------------------------------------------
NEW SCROLL BARS */

short NewScrollBars(whichWindow)
WindowPtr whichWindow;
{
 Rect sBRect;
 
 if(GoodNewHandle(NewControl(whichWindow,
 GetHScrollBarRect(whichWindow, &sBRect),
 NULL,
 VISIBLE,
 0, 0, 255,
 scrollBarProc,
 NO_REFCON))) {
 
 if(GoodNewHandle(NewControl(whichWindow,
 GetVScrollBarRect(whichWindow, &sBRect),
 NULL,
 VISIBLE,
 0, 0, 255,
 scrollBarProc,
 NO_REFCON))) {
 return(true);
 }
 }
 return(false);
}

/*
--------------------------------------------------------
GET HORIZONTAL SCROLL BAR RECTANGLE */

Rect *GetHScrollBarRect(whichWindow, whichRect)
WindowPtr whichWindow;
Rect *whichRect;
{
 *whichRect = whichWindow->portRect;
 
 whichRect->left -= 1;
 whichRect->top = whichRect->bottom 
 - (SCROLL_BAR_SIZE - 1);
 whichRect->right -= (SCROLL_BAR_SIZE - 2);
 whichRect->bottom += 1;
 
 return(whichRect);
}

/*
--------------------------------------------------------
GET VERTICAL SCROLL BAR RECTANGLE */

Rect *GetVScrollBarRect(whichWindow, whichRect)
WindowPtr whichWindow;
Rect *whichRect;
{
 *whichRect = whichWindow->portRect;
 
 whichRect->left = whichRect->right 
 - (SCROLL_BAR_SIZE - 1);
 whichRect->top -= 1;
 whichRect->right += 1;
 whichRect->bottom -= (SCROLL_BAR_SIZE - 2);
 
 return(whichRect);
}

/*
--------------------------------------------------------
CLOSE DOCUMENT

 A REAL application would need a more complex CloseDocument() function. 
This is just a shell. */

void CloseDocument(whichWindow)
WindowPtr whichWindow;
{
 DisposeWindow(whichWindow);
 
 PositionCounter -= 1;
}

/*
--------------------------------------------------------ACTIVATE DOCUMENT 
*/

void ActivateDocument(whichWindow, activate)
WindowPtr whichWindow;
Boolean activate;
{
 HiliteWindow(whichWindow, activate);
 
 if(activate) {
 ShowControl(((WindowPeek) 
 whichWindow)->controlList);
 ShowControl((*((WindowPeek) 
 whichWindow)->controlList)->nextControl);
 }
 else {
 SetPort(whichWindow);
 HideControl(((WindowPeek)  whichWindow)->controlList);
 ValidRect(&(*((WindowPeek) whichWindow)->controlList)->contrlRect);
 HideControl((*((WindowPeek)  whichWindow)->controlList)->nextControl);
 ValidRect(&(*(*((WindowPeek) whichWindow)->controlList)
 ->nextControl)->contrlRect);
 }
 DrawGrowIcon(whichWindow);
}

/*
--------------------------------------------------------
UPDATE DOCUMENT

 A REAL application would need a more complex UpdateDocument() function. 
This is just a shell. */

void UpdateDocument(whichDocument)
WindowPtr whichDocument;
{
 EraseRect(&whichDocument->portRect);
}

/*
--------------------------------------------------------
SIZE DOCUMENT */

void SizeDocument(whichDocument)
WindowPtr whichDocument;
{
 Rect sBRect;
 
 MoveSizeScrollBar(((WindowPeek)   whichDocument)->controlList,
 GetHScrollBarRect(whichDocument, &sBRect));
 MoveSizeScrollBar((*((WindowPeek) 
 whichDocument)->controlList)
 ->nextControl,
 GetVScrollBarRect(whichDocument, &sBRect));
 
 BeginUpdate(whichDocument);
 UpdateDocument(whichDocument);
 EndUpdate(whichDocument);
 
 ShowControl(((WindowPeek)whichDocument)->controlList);
 ShowControl((*((WindowPeek)  whichDocument)->controlList)->nextControl);
 
 DrawGrowIcon(whichDocument);
}

/*
--------------------------------------------------------
MOVE AND SIZE SCROLL BAR */

void MoveSizeScrollBar(whichScrollBar, sBRect)
ControlHandle whichScrollBar;
Rect *sBRect;
{
 HideControl(whichScrollBar);
 
 MoveControl(whichScrollBar, sBRect->left, 
 sBRect->top);
 SizeControl(whichScrollBar, sBRect->right - 
 sBRect->left, sBRect->bottom - sBRect->top);
}

/*
--------------------------------------------------------UPDATE PALETTE 
*/

void UpdatePalette(whichPalette)
WindowPtr whichPalette;
{
 /* Use the palette's refCon to determine which 
 palette needs updating. */
 switch(((WindowPeek) whichPalette)->refCon) {
 
 case TOOL_PALETTE:
 DrawToolMenu(&whichPalette->portRect);
 HiliteToolItem(&whichPalette->portRect, 
 (*TearOffs[TOOL_PALETTE])->currentItem, 
 true);
 break;
 
 case PATTERN_PALETTE:
 DrawPatternMenu(&whichPalette->portRect);
 HilitePatternItem(&whichPalette->portRect, 
 (*TearOffs[PATTERN_PALETTE])
 ->currentItem, true);
 break;
 
 case COLOR_PALETTE:
 DrawColorMenu(&whichPalette->portRect);
 HiliteColorItem(&whichPalette->portRect, 
 (*TearOffs[COLOR_PALETTE])->currentItem, 
 true);
 }
}



/*
--------------------------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:Menu.c
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD
 Segment: Main */

/*
--------------------------------------------------------GLOBAL CONSTANT 
DEFINITIONS AND VARIABLE DECLARATIONS */

#include "Constants.h"
#include "Variables.h"

/*
--------------------------------------------------------EXTERNAL FUNCTION 
DECLARATIONS */

/* None */

/*
--------------------------------------------------------FORWARD FUNCTION 
DECLARATIONS */

pascal void DrawToolMenu();
pascal short FindToolItem();
pascal void HiliteToolItem();

pascal void DrawPatternMenu();
pascal short FindPatternItem();
pascal void HilitePatternItem();

pascal void DrawColorMenu();
pascal short FindColorItem();
pascal void HiliteColorItem();

void MovePalette();



/*
----------------------------------------------
DRAW TOOLS */

pascal void DrawToolMenu(destRect)
Rect *destRect;
{
 DrawPicture(GetPicture(TOOL_PICT_ID), destRect);
}

/*
----------------------------
DRAW PATTERNS */

pascal void DrawPatternMenu(destRect)
Rect *destRect;
{
 short index;
 Rect drawRect;
 Pattern thePattern;
 
 if(MouseInMenu) {
 EraseRect(destRect);
 }
 for(index = 1; index < PATTERN_COUNT; index++) {
 drawRect = PatternRects[index];
 drawRect.top += 1;
 drawRect.left += 1;
 OffsetRect(&drawRect, destRect->left, 
 destRect->top);
 
 GetIndPattern(&thePattern, PATTERNS_ID, index);
 FillRect(&drawRect, &thePattern);
 }
}

/*
----------------------------
DRAW COLORS */

pascal void DrawColorMenu(destRect)
Rect *destRect;
{
 CTabPtr colorTable;
 RGBColor saveColor;
 short index;
 Rect drawRect;
 
 if(MouseInMenu) {
 EraseRect(destRect);
 }
 HLock((*(*MaxDevice)->gdPMap)->pmTable);
 colorTable = *(*(*MaxDevice)->gdPMap)->pmTable;
 
 GetForeColor(&saveColor);
 
 for(index = 1; index < COLOR_COUNT; index++) {
 RGBForeColor(&colorTable->ctTable[index - 1].rgb);
 drawRect = ColorRects[index];
 drawRect.top += 1;
 drawRect.left += 1;
 OffsetRect(&drawRect, destRect->left,
 destRect->top);
 
 /* Remember black is a Color QuickDraw pattern. */
 FillRect(&drawRect, black);
 }
 RGBForeColor(&saveColor);
 
 HUnlock((*(*MaxDevice)->gdPMap)->pmTable);
}

/*
--------------------------------------------------------
FIND TOOL ITEM */

pascal short FindToolItem(mousePt)
Point mousePt;
{
 short index;
 
 for(index = 1; index < TOOL_COUNT; index++) {
 
 if(PtInRect(mousePt, &ToolRects[index])) {
 return(index);
 }
 }
 return(0);
}

/*
------------------------
FIND PATTERN ITEM */

pascal short FindPatternItem(mousePt)
Point mousePt;
{
 short index;
 Rect patternRect;
 
 for(index = 1; index < PATTERN_COUNT; index++) {
 
 patternRect = PatternRects[index];
 patternRect.bottom += 1;
 patternRect.right += 1;
 
 if(PtInRect(mousePt, &patternRect)) {
 return(index);
 }
 }
 return(0);
}

/*
----------------------
FIND COLOR ITEM */

pascal short FindColorItem(mousePt)
Point mousePt;
{
 short index;
 Rect colorRect;
 
 for(index = 1; index < COLOR_COUNT; index++) {
 
 colorRect = ColorRects[index];
 colorRect.bottom += 1;
 colorRect.right += 1;
 
 if(PtInRect(mousePt, &colorRect)) {
 return(index);
 }
 }
 return(0);
}

/*
----------------------------
HILITE TOOL ITEM */

pascal void HiliteToolItem(destRect, item, hilite)
Rect *destRect;
short item;
Boolean hilite;
{
 Rect hiliteRect;
 
 hiliteRect = ToolRects[item];
 hiliteRect.bottom -= 1;
 hiliteRect.right -= 1;
 OffsetRect(&hiliteRect, destRect->left, 
 destRect->top);
 InvertRect(&hiliteRect);
}

/*
----------------------------
HILITE PATTERN ITEM */

pascal void HilitePatternItem(destRect, item, hilite)
Rect *destRect;
short item;
Boolean hilite;
{
 Rect hiliteRect;
 PenState savePen;
 Pattern thePattern;
 
 hiliteRect = PatternRects[item];
 hiliteRect.top += 1;
 hiliteRect.left += 1;
 OffsetRect(&hiliteRect, destRect->left, 
 destRect->top);
 
 if(hilite) {
 GetPenState(&savePen);
 
 PenSize(2, 2);
 FrameRect(&hiliteRect);
 
 PenSize(1, 1);
 PenMode(patBic);
 InsetRect(&hiliteRect, 2, 2);
 FrameRect(&hiliteRect);
 
 SetPenState(&savePen);
 }
 else {
 GetIndPattern(&thePattern, PATTERNS_ID, item);
 FillRect(&hiliteRect, &thePattern);
 }
}

/*
------------------------------------------------
HILITE COLOR ITEM */

pascal void HiliteColorItem(destRect, item, hilite)
Rect *destRect;
short item;
Boolean hilite;
{
 PenState savePen;
 Rect hiliteRect;
 RGBColor saveColor;
 CTabPtr colorTable;
 
 GetPenState(&savePen);
 
 hiliteRect = ColorRects[item];
 hiliteRect.bottom += 1;
 hiliteRect.right += 1;
 OffsetRect(&hiliteRect, destRect->left, 
 destRect->top);
 
 if(hilite) {
 FrameRect(&hiliteRect);
 
 PenMode(patBic);
 InsetRect(&hiliteRect, 1, 1);
 FrameRect(&hiliteRect);
 
 SetPenState(&savePen);
 }
 else {
 GetForeColor(&saveColor);
 
 HLock((*(*MaxDevice)->gdPMap)->pmTable);
 colorTable = *(*(*MaxDevice)->gdPMap)->pmTable;
 
 PenMode(patBic);
 FrameRect(&hiliteRect);
 
 SetPenState(&savePen);
 RGBForeColor(&colorTable->ctTable[item - 1].rgb);
 
 InsetRect(&hiliteRect, 1, 1);
 /* Remember black is a Color QuickDraw pattern. */
 FillRect(&hiliteRect, black);
 
 HUnlock((*(*MaxDevice)->gdPMap)->pmTable);
 
 RGBForeColor(&saveColor);
 }
}

/*
--------------------------------------------------------
MOVE PALETTE */

void MovePalette(whichPalette, position)
WindowPtr whichPalette;
Point position;
{
 MoveWindow(whichPalette, position.h, position.v, 
 false);
 
 if((TopWindow != nil) && (((WindowPeek) 
 TopWindow)->windowKind != userKind)) {
 
 /* The TopWindow is a non-application document. */
 if(((WindowPeek) whichPalette)->visible) {
 /* The palette is visible. Bring it to the 
 front and generate an activate event. */
 SelectWindow(whichPalette);
 }
 else {
 BringToFront(whichPalette);
 /* Make the palette visible and generate an 
 activate event. */
 ShowWindow(whichPalette);
 }
 }
 else {
 /* The TopWindow is an application window or it is 
 equal to nil. */
 BringToFront(whichPalette);
 
 if(TopWindow == nil) {
 /* Make the palette visible and generate an 
 activate event. */
 ShowWindow(whichPalette);
 }
 else {
 /* Make the palette visible but don't generate 
 an activate event. */
 ShowHide(whichPalette, true);
 HiliteWindow(whichPalette, true);
 }
 }
}

/*
--------------------------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:Interface.c
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD
 Segment: Main */



/*
--------------------------------------------------------GLOBAL CONSTANT 
DEFINITIONS AND VARIABLE DECLARATIONS */

#include "Constants.h"
#include "Variables.h"

/*
--------------------------------------------------------
EXTERNAL FUNCTION DECLARATIONS */

/* None */

/*
--------------------------------------------------------
FORWARD FUNCTION DECLARATIONS */

void AdjustInterface();
short GetMenuBarHeight();



/*
--------------------------------------------------------
ADJUST INTERFACE */

void AdjustInterface() {

 if(Environment.hasColorQD) {
 MaxDevice = GetMaxDevice(&(*GrayRgn)->rgnBBox);
 
 if((*(*MaxDevice)->gdPMap)->pixelSize == 8) {
 
 if(!ColorMenuVisible) {
 /* Insert Color menu if 256 colors. */
 InsertMenu(Menus[COLOR_MENU_INDEX], 0);
 DrawMenuBar();
 ColorMenuVisible = true;
 }
 }
 else {
 
 if(ColorMenuVisible) {
 /* Remove Color menu if < 256 colors. */
 DeleteMenu(COLOR_MENU_ID);
 DrawMenuBar();
 ColorMenuVisible = false;
 
 if(((WindowPeek) 
 Palettes[COLOR_PALETTE])->visible) {
 HideWindow(Palettes[COLOR_PALETTE]);
 }
 PalettesVisible[COLOR_PALETTE] = false;
 }
 }
 }
}

/*
--------------------------------------------------------
GET MENU BAR HEIGHT */

short GetMenuBarHeight() {

 if(Environment.machineType < envMachUnknown) {
 /* Arrggh! Someone has the 64K ROMs. */
 return(20);
 }
 else {
 return(MBarHeight);
 }
}



/*
--------------------------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:Palette.c
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD
 Segment: Main */

/*
--------------------------------------------------------GLOBAL CONSTANT 
DEFINITIONS AND VARIABLE DECLARATIONS */

#include "Constants.h"
#include "Variables.h"

/*
--------------------------------------------------------
EXTERNAL FUNCTION DECLARATIONS */

extern void ActivateDocument();    /* Window.c */

extern short GetMenuBarHeight();   /* Interface.c */

/*
--------------------------------------------------------
FORWARD FUNCTION DECLARATIONS */

void LocateWindows();
void HiliteUserWindows();
short ActiveWindow();

void DoDragWindow();
void DoSelectWindow();
void SendToBack();
void Drag();

/*
--------------------------------------------------------LOCATE WINDOWS

 Find the TopWindow whether it belongs to the application or not. Also 
find the TopPalette, the BottomPalette, and the TopDocument. These globals 
will all point to visible windows or nil. If the TopDocument is above 
any visible palette, adjust the order of the windows. If a visible non-application 
window is between any palettes and the TopDocument, adjust the order 
of  the windows. This function replaces most uses of FrontWindow() in 
the application. */

void LocateWindows()
{
 short palettesFound;
 Boolean inOrder;
 WindowPeek next;
 WindowPeek bottom;
 
 /* Initialize globals before search is begun. */
 TopWindow = TopPalette = TopDocument = nil;
 /* Make sure all SendBehind() and NewWindow() calls     bring a window 
to the front if there are no visible palettes. */
 BottomPalette = BRING_TO_FRONT;
 
 palettesFound = 0;
 inOrder = true;
 
 for(next = WindowList; next != nil; 
 next = next->nextWindow) {
 
 if(next->visible) {
 
 if(TopWindow == nil) {
 /* First visible window. */
 TopWindow = (WindowPtr) next;
 }
 if(next->windowKind == userKind) {
 
 if(next->refCon == DOCUMENT_WINDOW) {
 
 if(TopDocument == nil) {
 /* First visible Document. */
 TopDocument = (WindowPtr) next;
 
 if(palettesFound == PALETTE_COUNT) {
 /* If all the palettes are found 
 exit the search loop. */
 break;
 }
 }
 }
 else {
 
 if(TopPalette == nil) {
 /* First visible palette. */
 TopPalette = (WindowPtr) next;
 }
 /* BottomPalette always points to the 
 last visible palette found. */
 BottomPalette = (WindowPtr) next;
 }
 }
 else {
 
 if((TopPalette != nil) 
 || (TopDocument != nil)) {
 /* Shucks, a non-application window is 
 visible between the palettes and 
 TopDocument. */
 inOrder = false;
 }
 }
 }
 /* All windows beforelast palette get here */
 if((next->windowKind == userKind) && (next->refCon 
 != DOCUMENT_WINDOW)) {
 palettesFound++;
 
 if((TopDocument != nil) && (next->visible)) {
 /* A visible palette is beneath the 
 TopDocument. */
 
 if(TopDocument == TopWindow) {
 /* Bring the TopPalette to the front. */
 SelectWindow(next);
 TopWindow = (WindowPtr) next;
 }
 else {
 /* Put TopDocument behind BottomPalette. */
 SendBehind(TopDocument, next);
 }
 next = (WindowPeek) TopDocument;
 
 if(palettesFound == PALETTE_COUNT) {
 /* If all the palettes are found exit the 
 search loop. */
 break;
 }
 }
 }
 }
 if((!inOrder) && (TopPalette != nil)) {
 
 if(TopDocument != nil) {
 bottom = (WindowPeek) TopDocument;
 }
 else {
 bottom = (WindowPeek) BottomPalette;
 }
 for(next = (WindowPeek) TopPalette; next != nil; 
 next = next->nextWindow) {
 
 if(next != bottom) {
 
 if(next->windowKind != userKind) {
 /* Remove the non-application window. */
 SendBehind(next, bottom);
 /* Start over. */
 next = (WindowPeek) TopPalette;
 }
 }
 else {
 break;
 }
 }
 }
}

/*
--------------------------------------------------------
HILITE USER WINDOWS */

void HiliteUserWindows() 
{
 WindowPeek bottom;
 WindowPeek next;
 Boolean hilite;
 short index;
 
 if(TopPalette != nil) {
 
 if(TopWindow == TopPalette) {
 hilite = true;
 }
 else {
 hilite = false;
 }
 for(index = 0; index < PALETTE_COUNT; index++) {
 HiliteWindow(Palettes[index], hilite);
 }
 if(TopDocument != nil) {
 
 HiliteWindow(TopDocument, hilite);
 /* Unhilite the remaining document windows. */
 for(next = ((WindowPeek) 
 TopDocument)->nextWindow; next != nil; 
 next = next->nextWindow) {
 
 if((next->windowKind == userKind) && 
 (next->refCon == DOCUMENT_WINDOW)) {
 ActivateDocument(next, false);
 }
 }
 }
 }
}

/*
--------------------------------------------------------
ACTIVE WINDOW

 Return true if whichWindow is a palette or the TopDocument and the application 
is active. */

short ActiveWindow(whichWindow)
WindowPtr whichWindow;
{
 if(whichWindow != nil) {
 
 if((((WindowPeek) TopWindow)->windowKind 
 == userKind)
 && ((whichWindow == TopDocument)
 || (((WindowPeek) whichWindow)->refCon 
 != DOCUMENT_WINDOW))) {
 return(true);
 }
 }
 return(false);
}

/*
------------------------------------------
DO DRAG WINDOW

 Since DragWindow() automatically brings a window to the front if it's 
not already the front window, it must be replaced by the application 
to keep palettes floating above documents. */

void DoDragWindow(whichWindow)
WindowPtr whichWindow;
{
 if(Event.modifiers & optionKey) {
 /*An optional extension of Macintosh interface. */
 SendToBack(whichWindow);
 }
 else {
 
 if(Event.modifiers & cmdKey) {
 Drag(whichWindow);
 }
 else {
 
 if(!ActiveWindow(whichWindow)) {
 DoSelectWindow(whichWindow);
 }
 else {
 
 if((((WindowPeek) whichWindow)->refCon 
 != DOCUMENT_WINDOW)
 && (whichWindow != TopPalette)) {
 BringToFront(whichWindow);
 /* Force the palette to be updated before 
 it is dragged. */
 SetPort(whichWindow);
 UpdatePalette(whichWindow);
 }
 Drag(whichWindow);
 }
 }
 }
}

/*
--------------------------------------------------------
DO SELECT WINDOW

 Since SelectWindow() brings any window to the front, it must be replaced 
by the application to keep palettes floating above documents. */

void DoSelectWindow(whichWindow)
WindowPtr whichWindow;
{
 RgnHandle updateRgn;
 
 if(((WindowPeek) whichWindow)->refCon 
 == DOCUMENT_WINDOW) {
 
 if(TopPalette != nil) {
 /* Calculate window area not visible that will 
 need to be updated. */
 CopyRgn(whichWindow->visRgn, updateRgn 
 = NewRgn());
 OffsetRgn(updateRgn,(*((WindowPeek) 
 whichWindow)->contRgn)->rgnBBox.left,
 (*((WindowPeek)whichWindow)
 ->contRgn)->rgnBBox.top);
 DiffRgn(((WindowPeek) whichWindow)->strucRgn, 
 updateRgn, updateRgn);
 
 /* Move document below the BottomPalette. */
 SendBehind(whichWindow, BottomPalette);
 CalcVisBehind(whichWindow, updateRgn);
 PaintOne(whichWindow, updateRgn);
 
 DisposeRgn(updateRgn);
 
 if(TopPalette != TopWindow) {
 /* Bring all active application windows 
 forward by generating an activate event. */
 SelectWindow(TopPalette);
 }
 else {
 /* Ensure whichWindow and its controls are 
 hilited properly. */
 ActivateDocument(whichWindow, true);
 } 
 /* Ensure whichWindow and its controls are 
 unhilited properly. */
 ActivateDocument(TopDocument, false);
 }
 else {
 /* No Palettes, bring the document to the front 
 and generate an activate event. */
 SelectWindow(whichWindow);
 }
 }
 else {
 if(((WindowPeek) TopWindow)->windowKind 
 == userKind) {
 /* Bring the palette to the front but don't 
 generate an activate event. */
 BringToFront(whichWindow);
 }
 else {
 /* Bring the palette to the front and generate 
 an activate event. */
 SelectWindow(whichWindow);
 }
 }
}

/*
--------------------------------------------------------
SEND TO BACK */

void SendToBack(whichWindow)
WindowPtr whichWindow;
{
 WindowPeek next;
 WindowPtr below;
 
 if(((WindowPeek) whichWindow)->refCon 
 == DOCUMENT_WINDOW) {
 
 below = whichWindow;
 
 /* Find the document below TopDocument. Documents 
 are always visible. */
 for(next = ((WindowPeek) whichWindow)->nextWindow; 
 next != nil; next = next->nextWindow) {
 
 if((next->windowKind == userKind) && 
 (next->refCon == DOCUMENT_WINDOW)) {
 below = (WindowPtr) next;
 break;
 }
 }
 if(whichWindow != below) {
 /* Send document all the way to the back. */
 SendBehind(whichWindow, nil);
 
 if(TopPalette == TopWindow) {
 /* Ensure whichWindow and its controls are 
 unhilited properly. */
 ActivateDocument(whichWindow, false);
 
 /* Find the new TopDocument. */
 LocateWindows();
 /* Ensure the TopDocument and its controls 
 are hilited properly. */
 ActivateDocument(TopDocument, true);
 }
 }
 } 
 else {
 
 if((whichWindow != BottomPalette) && (TopPalette 
 != BottomPalette)) {
 /* Move palette behind the BottomPalette. */
 SendBehind(whichWindow, BottomPalette);
 }
 }
}

/*
--------------------------------------------------------
DRAG */

void Drag(whichWindow)
WindowPtr whichWindow;
{
 GrafPtr savePort;
 Rect dragLimit;
 RgnHandle dragRgn;
 long result;
 Point move;
 Rect *portBounds;
 
 if(WaitMouseUp()) {
 GetPort(&savePort);
 SetPort(WMgrPort);
 
 SetClip(GrayRgn);
 
 /* Ensure the drag outline does not get drawn 
 through the windows above whichWindow. */
 ClipAbove(whichWindow);
 
 dragLimit = screenBits.bounds;
 dragLimit.top = GetMenuBarHeight();
 
 CopyRgn(((WindowPeek) whichWindow)->strucRgn, 
 dragRgn = NewRgn());
 
 /* Drag outline of window around the screen. */
 result = DragGrayRgn(dragRgn, Event.where, 
 &dragLimit, &dragLimit, noConstraint, nil);
 
 move.v = result >> 16; /* HiWord(result). */
 move.h = result & 0xFFFF; /* LoWord(result). */
 
 if(move.v != 0x8000) {
 
 /* The mouse button has been released inside 
 dragLimit. See 'Inside Macintosh Volume I,' 
 pages 294-295, for expalnation of 0x8000. */
 if(((CGrafPtr) whichWindow)->portVersion 
 & 0xc000) {
 /* The window is a color port. */
 portBounds = &(*((CGrafPtr) 
 whichWindow)->portPixMap)->bounds;
 }
 else {
 /* The window is an old-style port. */
 portBounds = &whichWindow->portBits.bounds;
 }
 move.v += whichWindow->portRect.top 
 - portBounds->top;
 move.h += whichWindow->portRect.left 
 - portBounds->left;
 
 MoveWindow(whichWindow, move.h, move.v, false);
 }
 DisposeRgn(dragRgn);
 SetPort(savePort);
 }
}

/*
--------------------------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:Dialog.c
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD
 Segment: Main */



/*
--------------------------------------------------------GLOBAL CONSTANT 
DEFINITIONS AND VARIABLE DECLARATIONS */

#include "Constants.h"
#include "Variables.h"

/*
--------------------------------------------------------EXTERNAL FUNCTION 
DECLARATIONS */

extern short GoodResource();/* Error.c */
extern short GetMenuBarHeight(); /* Interface.c */

/*
--------------------------------------------------------
FORWARD FUNCTION DECLARATIONS */

void DoAbout();
short PositionDialog();
pascal Boolean DialogFilter();



/*
--------------------------------------------------------
DO ABOUT */

void DoAbout() {
 DialogRecord theDialogRecord;
 short itemHit;
 
 if(PositionDialog(ABOUT_DIALOG_ID)) {
 (void) GetNewDialog(ABOUT_DIALOG_ID, 
 &theDialogRecord, BRING_TO_FRONT);
 ShowWindow(&theDialogRecord);
 
 do {
 ModalDialog(DialogFilter, &itemHit);
 }
 while(itemHit == 0);
 
 CloseDialog(&theDialogRecord);
 }
}

/*
--------------------------------------------------------
POSITION DIALOG

 PositionDialog() centers a DITL resource horizontally and positions 
it one-third down
the screen vertically. */

short PositionDialog(whichID)
short whichID;
{
 Point where;
 DialogTHndl theTemplate;
 
 if(!GoodResource(theTemplate = (DialogTHndl) 
 GetResource('DLOG', whichID))) {
 return(false);
 }
 OffsetRect(&(*theTemplate)->boundsRect,
 - (*theTemplate)->boundsRect.left
 + ((screenBits.bounds.right
 - screenBits.bounds.left)
 - ((*theTemplate)->boundsRect.right
 - (*theTemplate)->boundsRect.left)) / 2,
 - (*theTemplate)->boundsRect.top
 + (((screenBits.bounds.bottom
 - screenBits.bounds.top-GetMenuBarHeight())
 - ((*theTemplate)->boundsRect.bottom
 - (*theTemplate)->boundsRect.top)) / 3)
 + GetMenuBarHeight());
 return(true);
}

/*
--------------------------------------------------------
DIALOG FILTER

 DialogFilter() allows exit of ModalDialog() on any mouseDown or keyDown 
event. */

pascal Boolean DialogFilter(whichDialog, whichEvent, 
 whichItem)
DialogPtr whichDialog;
EventRecord *whichEvent;
short *whichItem;
{
 WindowPtr whichWindow;
 
 if((whichEvent->what == mouseDown) || 
 (whichEvent->what == keyDown)) {
 
 if((FindWindow(whichEvent->where, &whichWindow) 
 == inMenuBar)
 || ((whichEvent->what == keyDown) && 
 (whichEvent->modifiers & cmdKey))) {
 /* Re-post event and ignore any error. */
 (void) PostEvent(whichEvent->what, 
 whichEvent->message);
 }
 *whichItem = 1;
 return(true);
 }
 else {
 return(false);
 }
}

 
/*
------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:Error.c
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD
 Segment: Main */

/*
--------------------------------------------------------GLOBAL CONSTANT 
DEFINITIONS AND VARIABLE DECLARATIONS */

#include "Constants.h"
#include "Variables.h"

/*
--------------------------------------------------------
EXTERNAL FUNCTION DECLARATIONS */

/* None */

/*
--------------------------------------------------------
FORWARD FUNCTION DECLARATIONS */

short GoodNewPtr();
short GoodNewHandle();
short GoodResource();

pascal long RecoverMemory();

/*
--------------------------------------------------------
GOOD NEW POINTER */

short GoodNewPtr(whichPtr)
Ptr whichPtr;
{
 if((whichPtr != nil) && (MemErr == noErr)) {
 return(true);
 }
 return(false);
}

/*
--------------------------------------------------------
GOOD NEW HANDLE */

short GoodNewHandle(whichHandle)
Handle whichHandle;
{
 if(whichHandle != nil) {
 return(GoodNewPtr(*whichHandle));
 }
 return(false);
}


/*
--------------------------------------------------------
GOOD RESOURCE */

short GoodResource(whichHandle)
Handle whichHandle;
{
 if((whichHandle != nil) && (ResError() == noErr)) {
 
 if(*whichHandle != nil) {
 return(true);
 }
 }
 return(false);
}


/*
--------------------------------------------------------
RECOVER MEMORY

 RecoverMemory() is a very simple grow zone function designed to prevent 
the ROM or resident software such as desk accessories from generating 
out of memory errors. After this function is called, the application 
will quit before processing the next event.
 
 See 'Inside Macintosh Volume II,' pages 42-43, about setting up grow 
zone functions. */

pascal long RecoverMemory(memoryNeeded)
Size memoryNeeded;
{
 long bufferSize;
 
 SetUpA5();
 
 bufferSize = GetHandleSize(MemoryBuffer);
 
 if(bufferSize != 0) {
 DisposHandle(MemoryBuffer);
 }
 OutOfMemory = true;
 
 RestoreA5();
 
 return(bufferSize);
}



/*
--------------------------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:Initialize.c
 Font:  Courier, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   APPL
 Creator: TOPD
 Segment: Initialize
 
 IMPORTANT! Use ResEdit to set the code resource of this segment to unlocked. 
*/

/*
--------------------------------------------------------GLOBAL CONSTANT 
DEFINITIONS AND VARIABLE DECLARATIONS */

#include "Constants.h"
#include "Variables.h"

/*
--------------------------------------------------------
EXTERNAL FUNCTION DECLARATIONS */

extern short GoodNewPtr();  /* Error.c */
extern short GoodNewHandle(); /* Error.c */
extern short GoodResource();/* Error.c */
extern pascal long RecoverMemory();/* Error.c */

extern pascal void DrawToolMenu(); /* Menu.c */
extern pascal short FindToolItem();/* Menu.c */
extern pascal void HiliteToolItem(); /* Menu.c */
extern pascal void DrawPatternMenu();/* Menu.c */
extern pascal short FindPatternItem(); /* Menu.c */
extern pascal void HilitePatternItem();/* Menu.c */
extern pascal void DrawColorMenu();/* Menu.c */
extern pascal short FindColorItem(); /* Menu.c */
extern pascal void HiliteColorItem();/* Menu.c */

/*
--------------------------------------------------------
FORWARD FUNCTION DECLARATIONS */

void Initialize();

void SetupEnvironment();
void SetupMemory();

void SetupPalettes();
void SetupToolPalette();
void SetupPatternPalette();
void SetupColorPalette();
void SetupPaletteRects();

void SetupMenus();



/*
--------------------------------------------------------
INITIALIZE */

void Initialize() {

 InitGraf(&thePort);
 InitFonts();
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs(nil);
 InitCursor();
 
 FlushEvents(everyEvent, 0);
 
 SetupEnvironment();
 SetupMemory();
 SetupPalettes();
 SetupMenus();

 TopWindow = TopPalette = TopDocument = nil;
 BottomPalette = BRING_TO_FRONT;
 
 PositionCounter = 0;
 
 Sleep = SLEEP_DURATION;
 
 ClosingAll = Quitting = Finished = false;
}

/*
--------------------------------------------------------
SETUP ENVIRONMENT */

void SetupEnvironment() {

 if(SysEnvirons(VERSION_REQUESTED, &Environment)
  == envBadSel) {
 ExitToShell();
 }
 if(Environment.machineType >= envMachUnknown) {
 WNEIsImplemented =
 NGetTrapAddress 
 (WAIT_NEXT_EVENT_TRAP_NUMBER,ToolTrap)
 != NGetTrapAddress  
 (UNIMPLEMENTED_TRAP_NUMBER, ToolTrap);
 }
 else {
 WNEIsImplemented = false;
 }
}

/*
--------------------------------------------------------
SETUP MEMORY */

void SetupMemory() {

 MaxApplZone();
 
 MoreMasters();
 MoreMasters();
 
 if(!GoodNewHandle(MemoryBuffer = 
 NewHandle(MEMORY_BUFFER_SIZE))) {
 ExitToShell();
 }
 OutOfMemory = false;
 
 /* Setup a simple grow zone function. */
 SetGrowZone(RecoverMemory);
}

/*
--------------------------------------------------------SETUP PALETTES 
*/

void SetupPalettes() {
 short index;
 TearOffMGlobalsPtr whichTearOff;
 Rect windowRects[PALETTE_COUNT];
 short attributes;
 
 for(index = 0; index < PALETTE_COUNT; index++) {
 
 /* Get a handle to TearOffMenuGlobals. */
 if(!GoodResource(TearOffs[index]
 = (TearOffMGlobalsHdl)
 GetResource(TEAR_OFF_MENU_GLOBALS_TYPE,
 TOOL_MENU_ID + index))) {
 ExitToShell();
 }
 MoveHHi(TearOffs[index]);
 HLock(TearOffs[index]);
 whichTearOff = *TearOffs[index];
 
 switch(index) {
 
 case TOOL_PALETTE:
 SetupToolPalette(whichTearOff, 
 &windowRects[index]);
 break;
 
 case PATTERN_PALETTE:
 SetupPatternPalette(whichTearOff, 
 &windowRects[index]);
 break;
 
 case COLOR_PALETTE:
 SetupColorPalette(whichTearOff, 
 &windowRects[index]);
 }
 if(Environment.hasColorQD) {
 /* Create a color port. */
 if(!GoodNewPtr(Palettes[index]
 = NewCWindow(nil,
 &windowRects[index],
 NULL,
 NOT_VISIBLE,
 /* Set procedure ID based on 
 PaletteWDEF resource ID. */
 (16 * PALETTE_WDEF_ID)
 + noGrowDocProc,
 BRING_TO_FRONT,
 GO_AWAY_BOX,
 (long) index))) {
 ExitToShell();
 }
 }
 else {
 /* Create an old-style port. */
 if(!GoodNewPtr(Palettes[index]
 = NewWindow(nil,
 &windowRects[index],
 NULL,
 NOT_VISIBLE,
 /* Set procedure ID based on 
 PaletteWDEF resource ID. */
 (16 * PALETTE_WDEF_ID)
 + noGrowDocProc,
 BRING_TO_FRONT,
 GO_AWAY_BOX,
 (long) index))) {
 ExitToShell();
 }
 }
 /* Finish Initializing TearOffMenuGlobals 
 structure. */
 whichTearOff->environment = &Environment;
 whichTearOff->paletteWindow = Palettes[index];
 
 HUnlock(TearOffs[index]);
 }
}

/*
--------------------------------------------------------
SETUP TOOL PALETTE */

void SetupToolPalette(whichTearOff, windowRect)
TearOffMGlobalsPtr whichTearOff;
Rect *windowRect;
{
 PicHandle toolPicture;
 
 if(!GoodResource(toolPicture = 
 GetPicture(TOOL_PICT_ID))) {
 ExitToShell();
 }
 HNoPurge(toolPicture);
 
 /* Calculate palette's portRect from toolPicture. */
 *windowRect = (*toolPicture)->picFrame;
 OffsetRect(windowRect, -windowRect->left, 
 -windowRect->top);
 
 SetupPaletteRects(&ToolRects,
 TOOLS_ACROSS,
 TOOLS_DOWN,
 (windowRect->right + 1) / TOOLS_ACROSS,
 (windowRect->bottom + 1) / TOOLS_DOWN);
 
 /* Initialize the TearOffMenuGlobals structure. */
 whichTearOff->currentItem = DEFAULT_TOOL;
 whichTearOff->drawMenuProc = DrawToolMenu;
 whichTearOff->findItemProc = FindToolItem;
 whichTearOff->hiliteItemProc = HiliteToolItem;
}

/*
--------------------------------------------------------
SETUP PATTERN PALETTE */

void SetupPatternPalette(whichTearOff, windowRect)
TearOffMGlobalsPtr whichTearOff;
Rect *windowRect;
{
/* Calculate the portRect from number of patterns. */
 windowRect->left = 0;
 windowRect->top = 0;
 windowRect->right = (PATTERNS_ACROSS
 * (PATTERN_ITEM_WIDTH + 1)) + 1;
 windowRect->bottom = (PATTERNS_DOWN 
 * (PATTERN_ITEM_HEIGHT + 1)) + 1;
 
 SetupPaletteRects(&PatternRects,
 PATTERNS_ACROSS,
 PATTERNS_DOWN,
 PATTERN_ITEM_WIDTH + 1,
 PATTERN_ITEM_HEIGHT + 1);
 
 /* Initialize the TearOffMenuGlobals structure. */
 whichTearOff->currentItem = DEFAULT_PATTERN;
 whichTearOff->drawMenuProc = DrawPatternMenu;
 whichTearOff->findItemProc = FindPatternItem;
 whichTearOff->hiliteItemProc = HilitePatternItem;
}

/*
--------------------------------------------------------
SETUP COLOR PALETTE */

void SetupColorPalette(whichTearOff, windowRect)
TearOffMGlobalsPtr whichTearOff;
Rect *windowRect;
{
/* Calculate the portRect from the number of colors. */
 windowRect->left = 0;
 windowRect->top = 0;
 windowRect->right = (COLORS_ACROSS 
 * (COLOR_ITEM_WIDTH + 1)) + 1;
 windowRect->bottom = (COLORS_DOWN 
 * (COLOR_ITEM_HEIGHT + 1)) + 1;
 
 SetupPaletteRects(&ColorRects,
 COLORS_ACROSS,
 COLORS_DOWN,
 COLOR_ITEM_WIDTH + 1,
 COLOR_ITEM_HEIGHT + 1);
 
 /* Initialize the TearOffMenuGlobals structure. */
 whichTearOff->currentItem = DEFAULT_COLOR;
 whichTearOff->drawMenuProc = DrawColorMenu;
 whichTearOff->findItemProc = FindColorItem;
 whichTearOff->hiliteItemProc = HiliteColorItem;
}

/*
--------------------------------------------------------
SETUP PALETTE RECTS */

void SetupPaletteRects(whichRects, itemsAcross, 
 itemsDown, itemWidth, itemHeight)
Rect *whichRects;
short itemsAcross;
short itemsDown;
short itemWidth;
short itemHeight;
{
 short index;
 short across;
 short down;
 
 whichRects->left = 0;
 whichRects->top = 0;
 whichRects->right = 0;
 whichRects->bottom = 0;
 
 index = 1;
 
 for(down = 0; down < itemsDown; down++) {
 
 for(across = 0; 
 across < itemsAcross; across++) {
 (whichRects + index)->left 
 = across * itemWidth;
 (whichRects + index)->top 
 = down * itemHeight;
 (whichRects + index)->right 
 = (across + 1) * itemWidth;
 (whichRects + index)->bottom 
 = (down + 1) * itemHeight;
 index += 1;
 }
 }
}

/*
--------------------------------------------------------
SETUP MENUS */

void SetupMenus() {
 short index;
 
 for(index = APPLE_MENU_INDEX; 
 index < MENU_COUNT - 1; index++) {
 Menus[index] = GetMenu(index + MENU_ID_OFFSET);
 InsertMenu(Menus[index], 0);
 }
 AddResMenu(Menus[APPLE_MENU_INDEX], 'DRVR');
 
 ColorMenuVisible = false;
 
 if(Environment.hasColorQD) {
 MaxDevice = GetMaxDevice(&(*GrayRgn)->rgnBBox);
 
 Menus[COLOR_MENU_INDEX] =GetMenu(COLOR_MENU_ID);
 
 if((*(*MaxDevice)->gdPMap)->pixelSize == 8) {
 /* Insert Color menu if 256 colors. */
 InsertMenu(Menus[COLOR_MENU_INDEX], 0);
 ColorMenuVisible = true;
 }
 }
 DrawMenuBar();
}


/*
--------------------------------------------------------
T E A R O F F   M E N U   D E F I N I T I O N

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:TearOffMDEF.c
 Font:  Monaco, 9 point
 Tab setting:    2
 Compiler:LightspeedC 2.15
 Project type:   MDEF
 ID:    128
 Name:  (None)
 
 IMPORTANT! Use ResEdit to set the MDEF resource to non-purgeable.
 
 IMPORTANT! Use ResEdit or FEdit to change hex string '21c809ce' at hex 
offset 14 in the MDEF resource to '4e714e71.' This substitutes two 'nop' 
instructions for a 'move.l a0,ToolScratch.'

--------------------------------------------------------
DESCRIPTION

 TearOffMDEF is an MDEF code resource implementing the common actions 
of tear-off menus for any application. It's designed to communicate with 
an application via a TearOffMenuGlobals structure in a TOMG resource. 
At runtime, TearOffMDEF uses the menuID of the MenuInfo, passed to it 
by the Macintosh ROM, to find a TOMG resource of the same ID. The TearOffMenuGlobals 
structure contains eight elements:
 
 drawMenuProc    A function pointer to a menu in global coordinates within 
the WMgrPort.

 findItemProc    A function pointer to a pascal-type procedure which 
returns the number of the menu item where the mouse is currently located.

 hiliteItemProc  A function pointer to a pascal-type procedure which 
hilites or unhilites a given menu item.

 environmentA pointer to a SysEnvRec used to test if the 64K ROMs are 
present.

 paletteWindow   A WindowPtr used to calculate menuWidth, menuHeight, 
and the structure boundary from the window's portRect.

 currentItemA short int containing the number of the currently hilited 
menu item.

 position A Point passed to the application from TearOffMDEF indicating 
the top, left point, in global coordinates, of a torn-off menu.

 itemHilitedA Boolean used internally by TearOffMDEF.
 
 All of these elements, except position and itemHilited, must be initialized 
by the application before a tear-off menu is inserted into the menu list.
 
 IMPORTANT! The menuProc field of a tear-off MENU resource must equal 
TearOffMDEF's resource ID.
 
 The application is completely responsible for the appearance of the 
menu. TearOffMDEF does not contain code to draw the contents of a menu, 
nor does it contain code to hilite menu items. A set of the three following 
procedures must be declared in the application for each tear-off menu:
 
 pascal void DrawMenuProc(destRect)
 Rect *destRect;   /* The menu rectangle in the 
 current grafPort. */
 
 pascal short FindItemProc(mousePt)
 Point mousePt;  /*The point in which the mouse 
 is currently located, relative 
 to the top,left of the menu 
 rectangle.
 
 The number of the menu item 
 where the mouse is currently 
 located should be returned to 
 TearOffMDEF by the application. 
 */
 
 pascal void HiliteItemProc(destRect, item, hilite)
 Rect *destRect; /* The menu rectangle in the 
 current grafPort. */
 short item;   /* The number of the menu item 
 to be hilited or unhilited.
 Boolean hilite; /* A flag indicating the hilite 
 state. Set true to hilite and 
 false to unhilite.
 
 IMPORTANT! The segment in which these procedures are declared must remain 
locked at runtime.
 
 It's very simple for the application to call these same procedures to 
draw into the window once the user has torn it off.
 
 TearOffMDEF will return -1 to the Menu Manager if a menu has been torn 
off. It's the application's responsibility to move the window into position 
and make it visible.
 
 Note: TearOffMDEF is not designed to implement pop-up menus. */

/*
--------------------------------------------------------
INCLUDE DEFINITIONS */

#include "MacTypes.h"
#include "MemoryMgr.h"
#include "MenuMgr.h"
#include "OSUtil.h"
#include "pascal.h"
#include "Quickdraw.h"
#include "ResourceMgr.h"
#include "WindowMgr.h"

/*
--------------------------------------------------------
GLOBAL CONSTANT DEFINITIONS */

#define nil 0

typedef struct QuickDrawGlobals { 
 char private[76];
 long randSeed;
 BitMap screenBits;
 Cursor arrow;
 Pattern dkGray;
 Pattern ltGray;
 Pattern gray;
 Pattern black;
 Pattern white;
 GrafPtr thePort;
} QuickDrawGlobals;

typedef struct TearOffMenuGlobals {
 void (*drawMenuProc)();
 short (*findItemProc)();
 void (*hiliteItemProc)();
 SysEnvRec *environment;
 WindowPtr paletteWindow;
 Point position;
 short currentItem;
 Boolean itemHilited;
} TearOffMenuGlobals, *TearOffMGlobalsPtr, **TearOffMGlobalsHdl;

#define TEAR_OFF_MENU_GLOBALS_TYPE 'TOMG'

#define TEAR_OFF_MARGIN 15
#define MOVE_PALETTE_ITEM (-1)

#define PALETTE_TITLE_BAR_HEIGHT 10
#define PALETTE_SHADOW_INDENT 3
#define PALETTE_OFFSET 5

/*
--------------------------------------------------------
GLOBAL VARIABLE DECLARATIONS */

/* None, even though the LightspeedC compiler allows it, others don't. 
*/

/*
--------------------------------------------------------EXTERNAL FUNCTION 
DECLARATIONS */

/* None, they're not allowed. */

/*
--------------------------------------------------------
FORWARD FUNCTION DECLARATIONS */

pascal void main();
void ChooseItem();
void DragMenu();

/*
--------------------------------------------------------
TEAROFF MENU DEFINITION

 The main function is called by the Menu Manager in the Macintosh ROM. 
It's designed to repsond to three messages: drawing the menu, choosing 
an item, and  calculating the menu size. It will not respond to a pop-up 
menu request. Before it dispatches to these routines, it first checks 
to see if the TearOffMenuGlobals structure is available on the heap, 
 and locks it down. It does nothing if it cannot find this structure.
 
 See 'Inside Macintosh Volume I,' pages 362-363, for more information 
on this function. */

pascal void main(message, whichMenu, menuRect, hitPt,          whichItem)
short message;
MenuHandle whichMenu;
Rect *menuRect;
Point hitPt;
short *whichItem;
{
 TearOffMGlobalsHdl whichTOMGlobalsHdl;
 TearOffMGlobalsPtr tearOffMGlobals;
 
 /* Make sure A5 is valid so the application code can    use it's own 
global variables. */
 SetUpA5();
 
 /* Use the menuID to get a handle to the correct  TearOffMenuGlobals 
structure. */
 whichTOMGlobalsHdl = (TearOffMGlobalsHdl) 
 GetResource(TEAR_OFF_MENU_GLOBALS_TYPE,
 (*whichMenu)->menuID);
 
 if((ResErr == noErr) && (whichTOMGlobalsHdl != nil)) {
 /* Lock the TearOffMenuGlobals structure so it can 
 be dereferenced safely. */
 HLock(whichTOMGlobalsHdl);
 tearOffMGlobals = *whichTOMGlobalsHdl;
 
 /* Dispatch according to the message received from 
 the Macintosh ROM.Use multiple 'if's
 rather than 'switch' to avoid linking of 400+ 
 bytes of runtime code. */
 
 if(message == mDrawMsg) {
 CallPascal(menuRect, 
 (tearOffMGlobals)->drawMenuProc);
 /* Hilite currentItem here instead of in 
 ChooseItem(). It may not be called son. */
 CallPascal(menuRect,     (tearOffMGlobals)->currentItem, true,
 (tearOffMGlobals)->hiliteItemProc);
 /* Let ChooseItem() know currentItem has been 
 hilited. */
 (tearOffMGlobals)->itemHilited = true;
 }
 else if(message == mChooseMsg) {
 ChooseItem(tearOffMGlobals, menuRect, hitPt, 
 whichItem);
 }
 else if(message == mSizeMsg) {
 /* Calculate the menuRect from the 
 paletteWindow's portRect. */
 (*whichMenu)->menuWidth
 = ((tearOffMGlobals)->paletteWindow)
 ->portRect.right
 - ((tearOffMGlobals)->paletteWindow)
 ->portRect.left;
 (*whichMenu)->menuHeight
 = ((tearOffMGlobals)->paletteWindow)
 ->portRect.bottom
 - ((tearOffMGlobals)->paletteWindow)
 ->portRect.top;
 }
 HUnlock(whichTOMGlobalsHdl);
 }
 RestoreA5();
}

/*
--------------------------------------------------------
CHOOSE ITEM

 If the mouse is within the menuRect, ChooseItem() will call the application 
to find in which item the mouse is currently located. The application 
is also called to hilite or unhilite a single item. If the mouse is outside 
the menuRgn ChooseItem will call DragMenu(). The menuRgn is defined as 
an area made up of the menuRect with a margin and the menu bar. */

void ChooseItem(tearOffMGlobals, menuRect, hitPt, 
 whichItem)
TearOffMGlobalsPtr tearOffMGlobals;
Rect *menuRect;
Point hitPt;
short *whichItem;
{
 short saveItem;
 short hiliteItem;
 Point mousePt;
 Rect marginRect;
 RgnHandle menuRgn;
 RgnHandle tempRgn;
 QuickDrawGlobals *qdGlobals;
 
 /* Set up pointer to QuickDraw's global variables. */
 qdGlobals = (QuickDrawGlobals *)(*(Byte **)CurrentA5
 - (sizeof(QuickDrawGlobals) 
 - sizeof(GrafPtr)));
 
 saveItem = *whichItem;
 hiliteItem = *whichItem = 0;
 
 /* If the hitPt is empty, the Menu Manager is calling   TearOffMDEF 
repeatedly to flash the item. */
 
 if((hitPt.h != 0) && (hitPt.v != 0)) {
 
 if(PtInRect(hitPt, menuRect)) {
 mousePt.h = hitPt.h - menuRect->left;
 mousePt.v = hitPt.v - menuRect->top;
 
 hiliteItem = *whichItem = CallPascalW(mousePt, 
 (tearOffMGlobals)->findItemProc);
 
 if((tearOffMGlobals)->itemHilited) {
 saveItem = (tearOffMGlobals)->currentItem;
 (tearOffMGlobals)->itemHilited = false;
 }
 }
 else {
 
 if(!(tearOffMGlobals)->itemHilited) {
 hiliteItem = (tearOffMGlobals)->currentItem;
 (tearOffMGlobals)->itemHilited = true;
 }
 }
 }
 if(saveItem != hiliteItem) {
 /* Unhilite the old item. */
 CallPascal(menuRect, saveItem, false, 
 (tearOffMGlobals)->hiliteItemProc);
 /* Hilite the new item. */
 CallPascal(menuRect, hiliteItem, true, 
 (tearOffMGlobals)->hiliteItemProc);
 }
 /* Calculate the menu region. if the mouse is outside   this region 
tear off the menu. */
 marginRect = *menuRect;
 marginRect.left -= TEAR_OFF_MARGIN;
 marginRect.bottom += TEAR_OFF_MARGIN;
 marginRect.right += TEAR_OFF_MARGIN;
 RectRgn(menuRgn = NewRgn(), &marginRect);
 
 RectRgn(tempRgn = NewRgn(), &qdGlobals->screenBits.bounds);
 
 if(tearOffMGlobals->environment->machineType 
 < envMachUnknown) {
 /* Arrggh! Someone has the 64K ROMs. */
 (*tempRgn)->rgnBBox.bottom = 20;
 }
 else {
 (*tempRgn)->rgnBBox.bottom = MBarHeight;
 }
 UnionRgn(menuRgn, tempRgn, menuRgn);
 
 if(!PtInRgn(hitPt, menuRgn)) {
 DragMenu(tearOffMGlobals, hitPt, whichItem, 
 menuRgn, tempRgn, qdGlobals);
 }
 DisposeRgn(tempRgn);
 DisposeRgn(menuRgn);
}

/*
--------------------------------------------------------
DRAG MENU

 DragMenu() draws an outline of the paletteWindow as the mouse is moved 
outside the menuRgn. If the mouse button is released outside this region, 
DragMenu() will set whichItem to -1 and exit. */

void DragMenu(tearOffMGlobals, hitPt, whichItem, 
 menuRgn, saveRgn, qdGlobals)
TearOffMGlobalsPtr tearOffMGlobals;
Point hitPt;
short *whichItem;
RgnHandle menuRgn;
RgnHandle saveRgn;
QuickDrawGlobals *qdGlobals;
{
 PenState savePen;
 long finalTicks;
 Rect windowRect;
 RgnHandle structureRgn;
 short hOffset;
 Point oldMouse;
 Point newMouse;
 RgnHandle dragRgn;
 
 GetPenState(&savePen);
 PenSize(1, 1);
 PenMode(notPatXor);
 PenPat(qdGlobals->gray);
 
 /* Since paletteWindow may not be visible, its    portRect must be used 
to calculate structureRgn.  First, calculate the window frame. */
 windowRect 
 = (tearOffMGlobals->paletteWindow)->portRect;
 
 /* Correct windowRect if top, left of portRect 
 isn't 0,0. */
 OffsetRect(&windowRect, -windowRect.left, 
 - windowRect.top);
 
 windowRect.right += 3;
 windowRect.bottom += PALETTE_TITLE_BAR_HEIGHT + 3;
 RectRgn(structureRgn = NewRgn(), &windowRect);
 
 hOffset = windowRect.right / 2;
 
 /* Next, calculate the frame's drop shadow. */
 windowRect.top += PALETTE_SHADOW_INDENT;
 windowRect.left += PALETTE_SHADOW_INDENT;
 windowRect.bottom += 1;
 windowRect.right += 1;
 RectRgn(saveRgn, &windowRect);
 
 /* Combine frame, shadow to calculate structure. */
 UnionRgn(structureRgn, saveRgn, structureRgn);
 
 GetClip(saveRgn);
 SetClip(GrayRgn);
 
 oldMouse = newMouse = hitPt;
 
 CopyRgn(structureRgn, dragRgn = NewRgn());
 OffsetRgn(dragRgn, newMouse.h - hOffset, newMouse.v 
 - PALETTE_OFFSET);
 
 /* Draw first drag outline. */
 FrameRgn(dragRgn);
 
 /* Mimic DragGrayRgn() by staying in a loop until the   mouse button 
is released or the mouse is inside the 
 menuRgn again. */
 
 do {
 /* Don't erase old drag outline if the mouse 
 hasn't moved. */
 
 if(!EqualPt(newMouse, oldMouse)) {
 /* Erase old drag outline. */
 FrameRgn(dragRgn);
 
 CopyRgn(structureRgn, dragRgn);
 OffsetRgn(dragRgn, newMouse.h - hOffset, 
 newMouse.v - PALETTE_OFFSET);
 
 /* Draw new drag outline. */
 FrameRgn(dragRgn);
 }
 /* Keep drag outline from flickering. */
 Delay(2, &finalTicks);
 
 oldMouse = newMouse;
 GetMouse(&newMouse);
 }
 while(WaitMouseUp() && !PtInRgn(newMouse, menuRgn));
 
 /* Erase final drag outline. */
 FrameRgn(dragRgn);
 
 if(!PtInRgn(newMouse, menuRgn)) {
 (tearOffMGlobals)->position.h = 
 (*dragRgn)->rgnBBox.left + 1;
 (tearOffMGlobals)->position.v = 
 (*dragRgn)->rgnBBox.top + 11;
 
 /* Tell application the menu was torn off. */
 *whichItem = MOVE_PALETTE_ITEM;
 }
 DisposeRgn(structureRgn);
 DisposeRgn(dragRgn);
 
 SetClip(saveRgn);
 SetPenState(&savePen);
}

/*
------------------------------------------------------
T E A R O F F   P A L E T T E

 version 1.0
 by Don Melton and Mike Ritter
 
 Copyright (C)1987, 1988 by Impulse Technologies, 
 Inc., all rights reserved. 
 
 Filename:TearOffPalette.project.r
 Font:  Monaco, 9 point
 Tab setting:    2
 Compiler:MPW Rez 2.0 */



/*
--------------------------------------------------------INCLUDE TYPES 
*/

#include "Types.r"

/*
--------------------------------------------------------
TYPE DEFINITIONS */

type 'TOPD' as 'STR ';

/*
--------------------------------------------------------
RESOURCE DEFINITIONS */

resource 'BNDL' (128) {
 'TOPD',
 0,
 {
 'ICN#',
 {
 0, 128
 },
 'FREF',
 {
 0, 128
 }
 }
};

resource 'FREF' (128) {
 'APPL',
 0,
 ""
};

resource 'ICN#' (128, purgeable) {
 {
 /* Data */
 $"FFFF FFE0 8000 0030 8000 0030 EFBF FFF0"
 $"8880 0030 E8BF FFF0 8880 0030 EFBF FFF0"
 $"8000 0030 81FF FFFE FF00 0002 81BA AAAA"
 $"8128 0003 81BA AAAB 8100 0003 81FF FFFF"
 $"8100 0003 8100 0003 8100 0003 8100 0003"
 $"8100 0003 8100 0003 8100 0003 8100 0003"
 $"8100 0003 8100 0003 81FF FFFF 803F FFFF"
 $"8000 0030 8000 0030 FFFF FFF0 7FFF FFF0",
 /* Mask */
 $"FFFF FFE0 FFFF FFF0 FFFF FFF0 FFFF FFF0"
 $"FFFF FFF0 FFFF FFF0 FFFF FFF0 FFFF FFF0"
 $"FFFF FFF0 FFFF FFFE FFFF FFFE FFFF FFFE"
 $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
 $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
 $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
 $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
 $"FFFF FFF0 FFFF FFF0 FFFF FFF0 7FFF FFF0"
 }
};

resource 'PAT#' (128, preload) {
 { /* 64 elements */
 $"",
 $"80",
 $"8000 0000 08",
 $"8800 0000 08",
 $"8800 0000 88",
 $"8800 2000 88",
 $"8800 2000 8800 02",
 $"8800 2200 8800 02",
 $"8800 2200 8800 22",
 $"A800 2200 8800 22",
 $"A800 2200 8A00 22",
 $"AA00 2200 8A00 22",
 $"AA00 2200 AA00 22",
 $"AA00 A200 AA00 22",
 $"AA00 A200 AA00 2A",
 $"AA00 AA00 AA00 AA",
 $"AA40 AA00 AA00 AA",
 $"AA40 AA00 AA04 AA",
 $"AA44 AA00 AA04 AA",
 $"AA44 AA00 AA44 AA",
 $"AA44 AA10 AA44 AA",
 $"AA44 AA10 AA44 AA01",
 $"AA44 AA11 AA44 AA01",
 $"AA44 AA11 AA44 AA11",
 $"AA54 AA11 AA44 AA11",
 $"AA54 AA11 AA45 AA11",
 $"AA55 AA11 AA45 AA11",
 $"AA55 AA11 AA55 AA11",
 $"AA55 AA51 AA55 AA11",
 $"AA55 AA51 AA55 AA15",
 $"AA55 AA55 AA55 AA15",
 $"AA55 AA55 AA55 AA55",
 $"EA55 AA55 AA55 AA55",
 $"EA55 AA55 AE55 AA55",
 $"EE55 AA55 AE55 AA55",
 $"EE55 AA55 EE55 AA55",
 $"EE55 BA55 EE55 AA55",
 $"EE55 BA55 EE55 AB55",
 $"EE55 BB55 EE55 AB55",
 $"EE55 BB55 EE55 BB55",
 $"FE55 BB55 EE55 BB55",
 $"FE55 BB55 EF55 BB55",
 $"FF55 BB55 EF55 BB55",
 $"FF55 BB55 FF55 BB55",
 $"FF55 FB55 FF55 BB55",
 $"FF55 FB55 FF55 BF55",
 $"FF55 FF55 FF55 BF55",
 $"FF55 FF55 FF55 FF55",
 $"FFD5 FF55 FF55 FF55",
 $"FFD5 FF55 FF5D FF55",
 $"FFDD FF55 FF5D FF55",
 $"FFDD FF55 FFDD FF55",
 $"FFDD FF75 FFDD FF55",
 $"FFDD FF75 FFDD FF57",
 $"FFDD FF77 FFDD FF57",
 $"FFDD FF77 FFDD FF77",
 $"FFFD FF77 FFDD FF77",
 $"FFFD FF77 FFDF FF77",
 $"FFFF FF77 FFDF FF77",
 $"FFFF FF77 FFFF FF77",
 $"FFFF FFF7 FFFF FF77",
 $"FFFF FFF7 FFFF FF7F",
 $"FFFF FFFF FFFF FF7F",
 $"FFFF FFFF FFFF FFFF"
 }
};

resource 'STR ' (128, preload) {
 "Untitled"
};

resource 'PICT' (128, preload) {
 1270,
 {0, 0, 83, 99},
 $"1101 A000 8201 000A 0000 0000 02D0 0240"
 $"9800 0E00 0000 0000 5300 6800 0000 0000"
 $"5300 6300 0000 0000 5300 6300 000C FE00"
 $"0680 0000 4000 0020 FD00 0CFE 0006 8000"
 $"0040 0000 20FD 000E FE00 0880 0000 4006"
 $"0020 0380 FF00 0F0B 0F3E 7080 1FC0 4069"
 $"C020 0280 FF00 0F0B 0800 1080 E030 4099"
 $"2020 0380 FF00 0F0B 0800 1081 0008 4099"
 $"2820 0380 FF00 0EFE 0008 8200 0840 4934"
 $"2003 80FF 000E FE00 0884 0008 4049 2420"
 $"0380 FF00 0F0B 0800 1084 0030 41A0 2420"
 $"0380 FF00 0F0B 0800 1084 01C0 4260 0420"
 $"1FF0 FF00 0F0B 0800 1083 8E00 4220 0820"
 $"1010 FF00 0F0B 0800 1086 7000 4100 0820"
 $"1FF0 FF00 0EFE 0008 8540 0040 8008 2010"
 $"10FF 000E FE00 0883 8000 4080 1020 1010"
 $"FF00 0F0B 0800 1080 8000 4040 1020 1010"
 $"FF00 0F06 0800 1080 8000 40FE 2001 1550"
 $"FF00 0F0B 0E7C F081 0000 4010 2020 2AB0"
 $"FF00 0EFE 0008 8000 0040 1020 207F E0FF"
 $"000C FE00 0680 0000 4000 0020 FD00 0CFE"
 $"0006 8000 0040 0000 20FD 0005 F5FF 01E0"
 $"000C FE00 0680 0000 4000 0020 FD00 0CFE"
 $"0006 8000 0040 0000 20FD 000D 0900 0F00"
 $"8000 0040 1C00 21FD 000E 0A00 0880 8000"
 $"0040 2200 2040 FE00 0E0A 0010 8080 07F0"
 $"4026 0021 15FE 000F 0B00 1900 8008 1040"
 $"2B00 2043 80FF 000F 0B00 2700 8010 3840"
 $"32C0 2104 40FF 000F 0B00 2200 8020 5840"
 $"2270 200F E0FF 000F 0B00 4200 8040 B040"
 $"4238 2008 20FF 000F 0B00 4400 8081 6040"
 $"851C 2009 E0FF 000F 0B00 8400 8102 C041"
 $"021C 2009 20FF 000F 0B00 8800 8205 8042"
 $"003C 2009 E0FF 000F 0B01 0800 87FB 0042"
 $"005C 2009 20FF 000F 0B01 1000 840E 0041"
 $"009C 2009 E0FF 000F 0B01 E000 840C 0040"
 $"811C 2009 E0FF 000F 0B01 C000 87F8 0040"
 $"421C 2008 20FF 000F 0B01 8000 8000 0040"
 $"2418 2008 20FF 000F 0B01 0000 8000 0040"
 $"1810 200F E0FF 000C FE00 0680 0000 4000"
 $"0020 FD00 0CFE 0006 8000 0040 0000 20FD"
 $"0005 F5FF 01E0 000C FE00 0680 0000 4000"
 $"0020 FD00 0CFE 0006 8000 0040 0000 20FD"
 $"000C FE00 0680 0000 4000 0020 FD00 0DFE"
 $"0007 8000 0040 0000 2003 FE00 0EFE 0008"
 $"87FF F840 1F80 200C C0FF 000E FE00 0884"
 $"0008 4060 6020 3030 FF00 0F0B 0C00 0084"
 $"0008 4180 1820 C00C FF00 0F0B 0300 0084"
 $"0008 4100 0820 8004 FF00 0F0B 00C0 0084"
 $"0008 4200 0420 8004 FF00 0F0B 0030 0084"
 $"0008 4200 0420 8004 FF00 0F0B 000C 0084"
 $"0008 4200 0420 8004 FF00 0F0B 0003 0084"
 $"0008 4200 0420 8004 FF00 0F0B 0000 C084"
 $"0008 4100 0820 8004 FF00 0F0B 0000 3084"
 $"0008 4180 1820 C00C FF00 0EFE 0008 8400"
 $"0840 6060 2030 30FF 000E FE00 0887 FFF8"
 $"401F 8020 0CC0 FF00 0DFE 0007 8000 0040"
 $"0000 2003 FE00 0CFE 0006 8000 0040 0000"
 $"20FD 000C FE00 0680 0000 4000 0020 FD00"
 $"0CFE 0006 8000 0040 0000 20FD 0005 F5FF"
 $"01E0 000C FE00 0680 0000 4000 0020 FD00"
 $"0CFE 0006 8000 0040 0000 20FD 000C FE00"
 $"0680 0000 4000 0020 FD00 0D09 07FF E080"
 $"0000 4000 0020 FD00 0D09 0618 6081 FFE0"
 $"4000 0020 FD00 0F0B 0418 2082 0010 40E0"
 $"7020 0FE0 FF00 0F0B 0418 2084 0008 4118"
 $"8820 1020 FF00 0E08 0018 0084 0008 4207"
 $"04FE 20FF 000F 0B00 1800 8400 0842 0004"
 $"2040 20FF 000F 0B00 1800 8400 0842 0004"
 $"2080 20FF 000F 0B00 1800 8400 0842 0004"
 $"2100 10FF 000F 0B00 1800 8400 0841 0008"
 $"2080 08FF 000F 0B00 1800 8400 0841 0010"
 $"2040 04FF 000F 0700 1800 8400 0840 80FE"
 $"2000 02FF 000F 0B00 1800 8200 1040 60C0"
 $"201F FEFF 000D 0900 1800 81FF E040 1F00"
 $"20FD 000D 0900 7E00 8000 0040 0000 20FD"
 $"000C FE00 0680 0000 4000 0020 FD00 0CFE"
 $"0006 8000 0040 0000 20FD 000C FE00 0680"
 $"0000 4000 0020 FD00 A000 83FF"
};

resource 'PICT' (129, purgeable) {
 858,
 {0, 0, 51, 166},
 $"1101 A000 8201 000A 0000 0000 02D0 0240"
 $"9800 1600 0000 0000 3300 A800 0000 0000"
 $"3300 A600 0000 0000 3300 A600 0006 FE00"
 $"0080 EF00 0703 0000 0140 EF00 0703 0000"
 $"0220 EF00 0703 0000 0410 EF00 0703 0000"
 $"0808 EF00 0703 0000 1004 EF00 0703 0000"
 $"2002 EF00 0703 0000 4001 EF00 0804 0000"
 $"8000 80F0 0008 0400 0100 0040 F000 0D04"
 $"0002 0000 20FB 0001 0180 F800 0D04 0004"
 $"0000 10FB 0001 0F80 F800 0D04 0008 001C"
 $"08FB 0001 3F80 F800 0C04 0010 00FC 04FB"
 $"0000 07F7 000C 0400 2000 FC02 FB00 0007"
 $"F700 0C04 0040 00FC 01FB 0000 07F7 000D"
 $"0500 8000 F800 80FC 0000 0FF7 000D 0501"
 $"0000 F800 40FC 0000 0EF7 000D 0502 0000"
 $"F800 20FC 0000 0EF7 000B 0304 0001 F0FA"
 $"0000 0EF7 0012 0E08 0001 F070 703C 787C"
 $"1C07 0E07 F01F FA00 130F 1000 01F1 F1F4"
 $"FCF8 FE7C 0F1E 1FF0 7F80 FB00 130F 2000"
 $"01E7 F3F3 FFF9 9EFC 0E1C 3871 C380 FB00"
 $"130F 4000 01EC E673 3E33 0F9C 0E1C 3031"
 $"8380 FB00 130F 8000 01E0 EC76 B83E 0E1C"
 $"0E1C 7023 8380 FB00 130F 4000 03C0 F87D"
 $"383C 0E3C 1E1C 7003 8780 FB00 120E 2000"
 $"03C1 F0FB 7878 0E38 1C3C 7807 0FFA 0012"
 $"0E30 0003 C1F0 F778 780E 381C 383F C73C"
 $"FA00 120E 1800 0381 E0F7 7070 1C38 3C38"
 $"3FE7 E0FA 0011 0D0C 0003 81E0 F670 701C"
 $"783C 3807 FFF9 0011 0D06 0000 03C1 ECF0"
 $"F038 7078 3800 FEF9 0011 0D03 0000 03C1"
 $"E8F0 E038 70F8 7800 7EF9 0011 0401 8007"
 $"03C1 FEE0 0570 71B8 70C0 7EF9 0012 0E00"
 $"C00F 8381 C0E0 E070 F338 71C0 6E03 FA00"
 $"120E 0060 0F87 83C1 EDE0 E0F6 3B77 C0CF"
 $"06FA 0012 0E00 300F 8783 C1F9 F1C0 FC7E"
 $"7CE3 8F8C FA00 120E 0018 0F87 0B81 F1FF"
 $"00F8 7C78 FF07 F8FA 0012 0E00 0C07 071B"
 $"80C1 FC00 6070 607C 03E0 FA00 0B07 0006"
 $"0000 3C00 03C0 F300 0B07 0003 0000 7800"
 $"03C0 F300 0B07 0001 8000 F000 03C0 F300"
 $"1715 0000 C001 E000 03C1 F1F0 78E7 1CE0"
 $"F0E0 3C0F 1C7C 3C00 1715 0000 6003 C000"
 $"0381 5090 8842 0C41 1840 4611 0824 4400"
 $"1715 0000 3007 8000 0780 4081 0042 0A42"
 $"0840 8220 0820 4000 1715 0000 180F 0000"
 $"0780 40E1 007E 0A42 0840 8220 0838 3800"
 $"1715 0000 0C1E 0000 0780 4081 0042 0942"
 $"0840 8223 8820 0C00 1715 0000 063C 0000"
 $"0780 4081 0042 0942 0840 8221 0820 0400"
 $"1603 0000 0378 FD00 0D40 9188 4208 C310"
 $"44C4 3108 2444 0016 0300 0001 F0FD 000D"
 $"E1F0 F0E7 1CC1 E0FC 781E 1C7C 7800 06FE"
 $"0000 E0EF 0006 FE00 0040 EF00 A000 83FF"
};

resource 'DLOG' (128, preload) {
 {0, 0, 160, 418},
 dBoxProc,
 invisible,
 noGoAway,
 0x0,
 128,
 ""
};

resource 'DITL' (128, preload) {
 {
 {10, 10, 42, 42},
 Icon {
 enabled,
 128
 },
 {14, 52, 30, 408},
 StaticText {
 enabled,
 "TearOffPalette by Don Melton and Mike Ritter"
 },
 {30, 52, 46, 408},
 StaticText {
 enabled,
 "©1987, 1988 by Impulse Technologies, Inc."
 },
 {56, 10, 72, 408},
 StaticText {
 enabled,
 "Designed for MacTutor: The Macintosh "
 "Programming Journal"
 },
 {72, 10, 88, 408},
 StaticText {
 enabled,
 "Compiled with LightspeedC™ version 2.15"
 },
 {98, 190, 130, 408},
 StaticText {
 enabled,
 ""Sure. We can do that. We don't even have to "
 "have a reason.""
 },
 {134, 190, 150, 408},
 StaticText {
 enabled,
 "Call us at (408) 296-6110"
 },
 {98, 10, 149, 176},
 Picture {
 enabled,
 129
 }
 }
};

resource 'TOPD' (0) {
 "TearOffPalette by Don Melton and Mike Ritter, "
 "©1987, 1988 by Impulse Technologies, Inc., "
 "all rights reserved."
};

data 'TOMG' (131, preload) {
 $"0000 0000 0000 0000 0000 0000 0000"
 $"0000 0000 0000 0000 0000 0000 0000"
};

data 'TOMG' (132, preload) {
 $"0000 0000 0000 0000 0000 0000 0000"
 $"0000 0000 0000 0000 0000 0000 0000"
};

data 'TOMG' (133, preload) {
 $"0000 0000 0000 0000 0000 0000 0000"
 $"0000 0000 0000 0000 0000 0000 0000"
};

resource 'MENU' (128, preload) {
 128,
 textMenuProc,
 0x7FFFFFFD,
 enabled,
 apple,
 {
 "About TearOffPalette...", noIcon, "", "", plain,
 "-", noIcon, "", "", plain
 }
};

resource 'MENU' (129, preload) {
 129,
 textMenuProc,
 0x7FFFFFF5,
 enabled,
 "File",
 {
 "New", noIcon, "N", "", plain,
 "-", noIcon, "", "", plain,
 "Close", noIcon, "W", "", plain,
 "-", noIcon, "", "", plain,
 "Quit", noIcon, "Q", "", plain
 }
};

resource 'MENU' (130, preload) {
 130,
 textMenuProc,
 0x7FFFFFFD,
 enabled,
 "Edit",
 {
 "Undo", noIcon, "Z", "", plain,
 "-", noIcon, "", "", plain,
 "Cut", noIcon, "X", "", plain,
 "Copy", noIcon, "C", "", plain,
 "Paste", noIcon, "V", "", plain,
 "Clear", noIcon, "", "", plain
 }
};

resource 'MENU' (131, preload) {
 131,
 128,
 allEnabled,
 enabled,
 "Tool",
 { /* 16 elements */
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain
 }
};

resource 'MENU' (132, preload) {
 132,
 128,
 allEnabled,
 enabled,
 "Pattern",
 { /* 64 elements */
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain
 }
};

resource 'MENU' (133, preload) {
 133,
 128,
 allEnabled,
 enabled,
 "Color",
 { /* 256 elements */
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain,
 "*", noIcon, "", "", plain
 }
};

resource 'SIZE' (-1) {
 dontSaveScreen,
 acceptSuspendResumeEvents,
 doOwnActivate,
 393216,
 393216
};

resource 'SIZE' (0) {
 dontSaveScreen,
 acceptSuspendResumeEvents,
 doOwnActivate,
 393216,
 393216
};

resource 'ICON' (128, purgeable) {
 $"FFFF FFE0 8000 0030 8000 0030 EFBF FFF0"
 $"8880 0030 E8BF FFF0 8880 0030 EFBF FFF0"
 $"8000 0030 81FF FFFE FF00 0002 81BA AAAA"
 $"8128 0003 81BA AAAB 8100 0003 81FF FFFF"
 $"8100 0003 8100 0003 8100 0003 8100 0003"
 $"8100 0003 8100 0003 8100 0003 8100 0003"
 $"8100 0003 8100 0003 81FF FFFF 803F FFFF"
 $"8000 0030 8000 0030 FFFF FFF0 7FFF FFF0"
};
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Whitethorn Games combines two completely...
If you have ever gone fishing then you know that it is a lesson in patience, sitting around waiting for a bite that may never come. Well, that's because you have been doing it wrong, since as Whitehorn Games now demonstrates in new release Skate... | Read more »
Call of Duty Warzone is a Waiting Simula...
It's always fun when a splashy multiplayer game comes to mobile because they are few and far between, so I was excited to see the notification about Call of Duty: Warzone Mobile (finally) launching last week and wanted to try it out. As someone who... | Read more »
Albion Online introduces some massive ne...
Sandbox Interactive has announced an upcoming update to its flagship MMORPG Albion Online, containing massive updates to its existing guild Vs guild systems. Someone clearly rewatched the Helms Deep battle in Lord of the Rings and spent the next... | Read more »
Chucklefish announces launch date of the...
Chucklefish, the indie London-based team we probably all know from developing Terraria or their stint publishing Stardew Valley, has revealed the mobile release date for roguelike deck-builder Wildfrost. Developed by Gaziter and Deadpan Games, the... | Read more »
Netmarble opens pre-registration for act...
It has been close to three years since Netmarble announced they would be adapting the smash series Solo Leveling into a video game, and at last, they have announced the opening of pre-orders for Solo Leveling: Arise. [Read more] | Read more »
PUBG Mobile celebrates sixth anniversary...
For the past six years, PUBG Mobile has been one of the most popular shooters you can play in the palm of your hand, and Krafton is celebrating this milestone and many years of ups by teaming up with hit music man JVKE to create a special song for... | Read more »
ASTRA: Knights of Veda refuse to pump th...
In perhaps the most recent example of being incredibly eager, ASTRA: Knights of Veda has dropped its second collaboration with South Korean boyband Seventeen, named so as it consists of exactly thirteen members and a video collaboration with Lee... | Read more »
Collect all your cats and caterpillars a...
If you are growing tired of trying to build a town with your phone by using it as a tiny, ineffectual shover then fear no longer, as Independent Arts Software has announced the upcoming release of Construction Simulator 4, from the critically... | Read more »
Backbone complete its lineup of 2nd Gene...
With all the ports of big AAA games that have been coming to mobile, it is becoming more convenient than ever to own a good controller, and to help with this Backbone has announced the completion of their 2nd generation product lineup with their... | Read more »
Zenless Zone Zero opens entries for its...
miHoYo, aka HoYoverse, has become such a big name in mobile gaming that it's hard to believe that arguably their flagship title, Genshin Impact, is only three and a half years old. Now, they continue the road to the next title in their world, with... | Read more »

Price Scanner via MacPrices.net

B&H has Apple’s 13-inch M2 MacBook Airs o...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for up to $150 off Apple’s new MSRP, starting at only $849. Free 1-2 day delivery is available to most US... Read more
M2 Mac minis on sale for $100-$200 off MSRP,...
B&H Photo has Apple’s M2-powered Mac minis back in stock and on sale today for $100-$200 off MSRP. Free 1-2 day shipping is available for most US addresses: – Mac mini M2/256GB SSD: $499, save $... Read more
Mac Studios with M2 Max and M2 Ultra CPUs on...
B&H Photo has standard-configuration Mac Studios with Apple’s M2 Max & Ultra CPUs in stock today and on Easter sale for $200 off MSRP. Their prices are the lowest available for these models... Read more
Deal Alert! B&H Photo has Apple’s 14-inch...
B&H Photo has new Gray and Black 14″ M3, M3 Pro, and M3 Max MacBook Pros on sale for $200-$300 off MSRP, starting at only $1399. B&H offers free 1-2 day delivery to most US addresses: – 14″ 8... Read more
Department Of Justice Sets Sights On Apple In...
NEWS – The ball has finally dropped on the big Apple. The ball (metaphorically speaking) — an antitrust lawsuit filed in the U.S. on March 21 by the Department of Justice (DOJ) — came down following... Read more
New 13-inch M3 MacBook Air on sale for $999,...
Amazon has Apple’s new 13″ M3 MacBook Air on sale for $100 off MSRP for the first time, now just $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD/Space Gray): $999 $100 off MSRP... Read more
Amazon has Apple’s 9th-generation WiFi iPads...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80-$100 off MSRP, starting only $249. Their prices are the lowest available for new iPads anywhere: – 10″ 64GB WiFi iPad (Space Gray or... Read more
Discounted 14-inch M3 MacBook Pros with 16GB...
Apple retailer Expercom has 14″ MacBook Pros with M3 CPUs and 16GB of standard memory discounted by up to $120 off Apple’s MSRP: – 14″ M3 MacBook Pro (16GB RAM/256GB SSD): $1691.06 $108 off MSRP – 14... Read more
Clearance 15-inch M2 MacBook Airs on sale for...
B&H Photo has Apple’s 15″ MacBook Airs with M2 CPUs (8GB RAM/256GB SSD) in stock today and on clearance sale for $999 in all four colors. Free 1-2 delivery is available to most US addresses.... Read more
Clearance 13-inch M1 MacBook Airs drop to onl...
B&H has Apple’s base 13″ M1 MacBook Air (Space Gray, Silver, & Gold) in stock and on clearance sale today for $300 off MSRP, only $699. Free 1-2 day shipping is available to most addresses in... Read more

Jobs Board

Medical Assistant - Surgical Oncology- *Apple...
Medical Assistant - Surgical Oncology- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Business Analyst | *Apple* Pay - Banco Popu...
Business Analyst | Apple PayApply now " Apply now + Apply Now + Start applying with LinkedIn Start + Please wait Date:Mar 19, 2024 Location: San Juan-Cupey, PR Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.