TweetFollow Us on Twitter

Tool Window
Volume Number:4
Issue Number:12
Column Tag:C Workshop

Tool Window Wanager

By Thomas Fruin, The Netherlands

The Tool Window Manager

Thomas Fruin is a graduate student of Computer Science at Leiden Universit, who also does study-related work for the two universities in Amsterdam, the University of Amsterdam and the Free University. He is the president and co-founder of VAMP (Vereniging Apple Macintosh Programmeurs), the main Dutch association for Macintosh programmers. Otherwise, he has done various odd Mac-related jobs to earn money, usually writing software (educational, communications, etc.).

The Macintosh user interface has been enhanced with some interesting new features lately: tear-off menus, pop-up menus, hierarchical menus, window layers, color, and even a new name: human interface. While most of these additions are in the new ROMs and the latest System file, not everything has been dealt with by Apple. Most notably is the lack of code to support tear-off menus, the most recent novelty and pioneered by HyperCard. They are something you are going to have to code all by yourself (well, with a bit of help from others). Darryl Lovato addressed the problem of tearing off a menu first, followed by an in depth article by Don Melton and Mike Ritter. In my contribution I will concentrate on keeping windows (torn-off menus or otherwise) floating above the others on your desktop, by presenting an alternative window manager - called the Tool Window Manager - that does all the work.

While tear-off menus are new to the Mac, floating windows are not. For several years now there have been applications putting their tools, patterns and other wares in these little windows, like FullPaint and PageMaker to name just two. Tool windows are a tricky business though, and it takes some careful planning to get everything right. Even commercial programs make mistakes at this (see the sidebar on the bugs in FullPaint and MacPaint).

Tool Windows Have a Special Appearance

First of all, since tool windows always lie on top, there is no distinction between an active or inactive tool window. This means that the tool should either have no title bar (like in FullPaint), or a title bar that never changes its appearance (like in HyperCard or Claris MacPaint). The latter case requires a special window definition function. This is not the subject of this article, however, since window definition functions have already been dealt with by MacTutor. Besides, you can cheat by experimenting with the WDEF for tool windows in the new MacPaint. Just copy WDEF 34 from your copy of MacPaint with ResEdit and paste it in your own application. Of course you cannot distribute it ... (but you knew that, didn’t you?)

Don Melton and Mike Ritter’s comments on the user interface guidelines for tool windows, and especially their additions to the guidelines, are a good idea. Since the article came out, I have modified my manager to incorporate these additions: when a non-application window is brought to the front (usually a desk accessory), my manager will unhighlight all the tool windows. When that window disappears, all the tools are highlighted again. I also added this behavior when a modal dialog window is brought to the front, because it seems to make sense. Finally, the unhighlighting of the tool windows also takes place when another application is activated under MultiFinder. It is up to the application that uses the Tool Window Manager to hide the tool windows when the application is juggled out, since it may not always want to hide the tools (one of them may be some kind of status window, for example).

Tool Windows or GhostWindows?

Tool windows are also sometimes called ghost windows. This is probably due to the obscure GhostWindow low memory global that is mentioned in Inside Macintosh volume I, page 287. You had better forget about it. I’m not only doing this as a public service (since Apple is discouraging people to use low memory globals), but also because it really doesn’t help in our case. As in almost every part of Inside Macintosh, the information is there, but it takes some careful reading to understand what the paragraph is about. Besides, GhostWindow will only let you have one tool window at a time, and we want to create as many tool windows as will fit in memory (it’s ok with me if you only want two or three). So it seems the only way out is a front end for all the Window Manager routines that could cause a tool window to become obscured. This is what I have written in the Tool Window Manager, or TWindow Manager for short.

The Mad T Party

The TWindow Manager is a set of 10 functions that replace calls to functions in the Window and Event Managers in your source file. These new functions do some extra processing before they call the Window and Event Manager themselves. The names of these TWindow Manager routines are the same as their corresponding Window and Event Manager functions, except for a prefixed “T”, as in TSelectWindow and TGetNextEvent. Their parameters are almost the same as the original functions, with a few slight modifications. See the explanations below for details. There are six categories of window manipulations that need to be replaced by TWindow Manager functions to work correctly with tool windows: (1) Adding windows to the desktop: use TNewWindow, TGetNewWindow and TShowWindow. (2) Removing windows: use TCloseWindow, TDisposeWindow and THideWindow. (3) Bringing lower windows to the top with TSelectWindow. (4) Dragging with TDragWindow. (5) Utility functions you will need every now and then: TFrontWindow and the new TInitWindows and TGetWKind. (6) Event handling: TGetNextEvent. This last function is included, because activate and deactivate events are handled differently when there are tool windows about. Note that I did not mention resizing windows (yes, I’m mentioning it now, thank you). Amazingly, GrowWindow is smart enough to draw its grow outline below any windows that are lying on top of it. It’s always nice to have less work to do.

There are three source files that go with this article. TWindows.c is the TWindow Manager itself, written in MPW C, and can be compiled into a separate module for later linking with your own program. TWindows.h is a file for inclusion by your program. It holds common data types and the TWindow function types. Your program will need this file if it wants to call TWindow Manager functions and use constants such as toolKind and anyKind. Finally, TWindowTester.c (also in MPW C) is an example program that uses the TWindow Manager. It is a familiar ‘vanilla’ Macintosh program that lets you create normal and tool windows to experiment with, as may as you like (see figure 1). You can select and drag windows, and open desk accessories as well. (Note that I got the tool window definition function from MacPaint 2.0, as I described above. In the source I am distributing, the windows look more like FullPaint’s tools, because MacPaint’s defproc is not included.) The menus called Windows and Tools let you make any document window or tool invisible and visible again, even if it isn’t in front. I added these menus myself to test the robustness of the TWindow Manager. Note that you can’t do anything useful with TWindowTester (although you might derive great pleasure from opening, say, 100 windows). For example, the scroll bars in the document windows don’t work. I just put them there so you can see that windows get activated and deactivated properly.

Background Information, Utility Functions

The TWindow Manager keeps tool windows and document windows apart by storing a special value in the WindowRecord’s windowKind field. This value is defined as the toolKind constant (equal to 30000) in the source file for TWindows. If you like, you can modify it to a value more suitable to your application, but there should be little conflicts as is. If any of the TWindow Manager functions wants to know what kind of window it is dealing with, it calls the utility function TGetWKind. This function is precisely for determining the windowKind value of a window. Why write a function for such a simple task? Well, TGetWKind doesn’t just return the window’s windowKind, but does some extra processing as well. If the window is a desk accessory window (with a negative windowKind), TGetWKind will always return the constant systemKind (equal to -1). If the window is a dialog window, with windowKind equal to the toolbox constant dialogKind, TGetWKind only returns dialogKind if the window is a modal dialog window. If the dialog is modeless, TGetWKind returns the toolbox constant userKind (the toolbox constant for normal document windows). This is because modeless dialog windows behave almost exactly the same as document windows, i.e. they lie behind tool windows. TGetWKind figures out what kind of dialog it is by examining the window’s variation code and definition procedure. Window definition procedures have an (optional) header embedded in their code that holds information like the WDEF’s resource ID (see Technical Note 110 for more information about WDEFs - and make sure you have the “real” 110). The dialog is considered modeless if its window is a document window: this is when the WDEF’s resource ID is zero and the variation code is equal to documentProc, noGrowDocProc or zoomDocProc. To play it safe, I let TGetWKind also check if the resource type in the header is really ‘WDEF’. If the WDEF doesn’t have a header (like the MacPaint tool window for example), it’s extremely unlikely that there will be a ‘WDEF’ string embedded at that particular position in the code.

The Window Manager in the Macintosh ROM has (as far as I can determine) only one global variable dealing with window positions, called WindowList - at location $9D6. It holds the very first window in the window list. By comparison, my TWindow Manager has three position globals of its own: frontToolWindow, backToolWindow and frontDocWindow. FrontToolWindow points to the frontmost visible tool window, backToolWindow to the backmost visible tool window, and frontDocWindow to the frontmost visible document window. Two other global variables I use mimic the CurActivate ($A64) and CurDeactive ($A68) low memory globals in the Macintosh: toBeActivated and toBeDeactivated. They hold the real windows that need activating and deactivating, and my TGetNextEvent takes them into account when it returns activate events. They are set by various TWindow Manager routines whenever activate events need to be posted. Finally I have two utility global variables, firstRgn and secondRgn. All these seven globals need to be initialized by the TInitWindows function, before you start using any functions in the TWindow Manager.

Figure 1: The TWindowTester Application

TFrontWindow is my replacement for FrontWindow. You can specify a window type in the windowKind parameter (note that FrontWindow has no parameters at all). This window type is one of the constants toolKind, userKind or anyKind. Usually you will want to call TFrontWindow with windowKind equal to userKind; it will then return the frontmost visible document window (the active window), ignoring any tool windows that may be lying above it (unless there is a desk accessory or modal dialog on top - it then returns the DA’s or dialog’s window). Calling TFrontWindow with windowKind equal to toolKind returns the frontmost visible tool window (again taking DAs and dialogs into account). When you call TFrontWindow with anyKind as the parameter, it behaves exactly like the original FrontWindow function - it simply returns the frontmost visible window.

Selecting Windows

I’m saving the subjects of adding and removing windows for later, because they are the trickiest ones to handle. TSelectWindow, by comparison, is much easier. Here, the main problem is to bring a document window to the “front” while keeping it behind any tool windows that may be visible. Apple, in its infinite wisdom, already has a function that almost does this: it’s called SendBehind and it’s listed on page I-286 in Inside Macintosh. You use it to move a window closer to the front (ah, so that’s why it’s called SendBehind), and specify the window behind which your window should be moved. In our case this will usually be the backmost tool window. Note what Inside Macintosh has to say about this: “If you’re moving theWindow closer to the front (that is, if it’s initially even further behind behindWindow), you must make the following calls after calling SendBehind:

wPeek = POINTER( theWindow );
PaintOne( wPeek, wPeek^.strucRgn );
CalcVis( wPeek );

This means that you have to do some low-level stuff, because SendBehind isn’t really meant to bring windows forward. PaintOne (5 pages further) is a routine that whitewashes the newly exposed part of the window. CalcVis (on the next page in Inside Macintosh) recalculates the window’s visRgn. Unfortunately, it seems Apple goofed here. CalcVis only recalculates the visRgn of one window, while we need to recalculate the visRgns of all the windows following our window. Apple makes up for this, though, by supplying us with the routine CalcVisBehind (on same page as CalcVis). This function does not only recalculate the visRgn of our window, but also of all the windows following it. If you’re curious, try replacing CalcVisBehind by CalcVis and watch what happens on the screen (don’t do this if you dislike making a mess of things).

Another thing: the above call to PaintOne paints the whole window white, and not only the newly exposed part. Therefore, I’ve created the utility function BringForward, that calls SendBehind, PaintOne and CalcVisBehind, and only repaints those newly exposed parts. BringForward is called by TSelectWindow and many other routines in the TWindow Manager.

If you look at the source code for TSelectWindow, you’ll see it does a lot more than just calling BringForward. This is because we have to take desk accessories into account. An open desk accessory will always lie above any tool windows when it is active. If the user selects one of the document windows, it naturally comes to the front, but not until all of the tool windows have also been brought to the front (remember that they were lying behind the desk accessory). I’ve written another utility function for this, obviously called BringToolsForward. This function makes repeated calls to BringForward for each visible tool window. These calls it makes to BringForward have a special flag to tell it not to call CalcVisBehind each time. This is because CalcVisBehind is a relatively time-consuming function. Once all the tool windows have been brought forward, BringToolsForward makes one final call to CalcVisBehind, recalculating all those changed visRgns at once. This speeds things up by about 30%, which is nice when you have a lot of tool windows (at one time during testing, I created about 50). The speed bottleneck, however, seems to be SendBehind. I wish Apple would publish interfaces to the Layer Manager in MultiFinder, because it has much more efficient ways of quickly moving windows to the front. Those aren’t standard Window Manager functions working there ...

Two other useful utility functions internal to the TWindow Manager are NextVisWindow and PrevVisWindow. They operate fairly straightforward, and do exactly what their names imply: NextVisWindow returns the next visible window, and PrevVisWindow returns the previous visible window in the window list, with respect to the window that gets passed to it. NextVisWindow and PrevVisWindow are used in various places in the TWindow Manager, including here in TSelectWindow.

Dragging Windows

TDragWindow replaces the standard toolbox function DragWindow. It lets you drag a document window, while ensuring that it doesn’t obscure any tool windows that may be lying in front of it. Again, I am grateful to Apple for doing the dirty work: they wrote this useful low level function called DragGrayRgn (Inside Macintosh page I-294) that pulls a gray outline of a region around. DragWindow actually calls DragGrayRgn, but in the wrong way (for our purposes).

The first thing TDragWindow does is check if the user is holding down the Command key. It gets this information from the EventRecord that you pass it. (Note that TDragWindow wants you to pass an EventRecord as its second parameter, while DragWindow wants the point where the mouse was clicked.) If the Command key is not being held down, TDragWindow calls TSelectWindow to move the window to the front (while staying below any tool windows, remember?). Once that is done, we can call DragGrayRgn to move an outline of the window. This outline is the window’s strucRgn, so we pass copy of the strucRgn to DragGrayRgn. DragGrayRgn needs some more parameters, like the point the mouse was originally clicked and the bounding rectangles for the drag, but they are pretty obvious from the documentation in Inside Macintosh.

We have one last problem: how do we get DragGrayRgn to draw its outline below the tool windows? Aha, well, there’s yet another low-level Window Manager function for that (I hope I’m not boring you). It’s called ClipAbove, and Inside Macintosh says that “ClipAbove sets the clipRgn of the Window Manager port to be the desktop intersected with the current clipRgn, minus the structure regions of all the windows in front of the given window.” Great. So all we have to do is set the current port to the Window Manager port, set its clipRgn to the whole current desktop, and call ClipAbove. Then we go ahead and call DragGrayRgn. You’ll see that the gray outline gets drawn in the Window Manager port, and stays neatly below the tool windows. After dragging, we check if the user really did move the window by examining DragGrayRgn’s result. If he did, we call MoveWindow to actually move the window to its new position. And of course we restore the Window Manager port’s clipRgn (since we’ve been messing with it), and reset the current port.

Dealing With Activate and Deactivate Events

Normally, the Window Manager in the Macintosh ROM posts an deactivate event when the frontmost window gets moved back, and a activate event when a window gets moved to the front. This won’t work in our case, because our document windows never get moved to the front. They always stay behind our tool windows. But we still want selected document windows to receive (de)activate events, and preferably in the standard way: as events returned by some kind of TGetNextEvent function. My solution was to copy the mechanism Apple uses: the Macintosh has two low memory globals, CurActivate and CurDeactive. GetNextEvent checks these two globals first, before it examines the event queue. If it finds that one of these globals is nonzero, it fills in the user’s EventRecord with an activate event for the window pointed to by one of these globals. CurActivate holds a pointer to the window that needs to be activated, and CurDeactive a pointer to the window that needs to be deactivated. Deactivate events have a higher priority than activate events.

My new TGetNextEvent does more or less the same, but instead it uses my globals toBeActivated and toBeDeactivated. (Please note that they are not in low memory, so don’t worry about using them.) Whenever any routine in the TWindow Manager moves windows around, it updates these two globals. When TGetNextEvent gets called (as a replacement for GetNextEvent - writing TWaitNextEvent is left as an exercise for the reader), it checks these two globals and returns its own activate events back to the calling application.

TGetNextEvent handles three types of situations. It calls EventAvail to see what kind of event is currently pending. (1) The first situation arises when there is a true (de)activate event pending. In that case, it gets the event with GetNextEvent (the real one). Depending on the type of event (activate or deactivate), it compares the event’s window with either the toBeActivated or the toBeDeactivated global. If the window the toolbox wants to activate is equal to toBeActivated, or the window it wants to deactivate is equal to toBeDeactivated, the toolbox is doing fine and there is nothing left for us to rectify. If toBeActivated or toBeDeactivated are nil, it means we didn’t anticipate the activate event. This is an important occurrence and happens when a desk accessory suddenly gets opened or closed. In this case we substitute our front document window (frontDocWindow) for the window that gets (de)activated; additionally, we call HiliteWindow to highlight or unhighlight the frontDocWindow’s title bar, since that hasn’t been done yet, and we call HiliteTools to unhighlight or highlight all the tool windows. HiliteTools is a simple utility function I wrote that traverses all the tool windows, and calls HiliteWindow for each visible one. Oh yes, if there wasn’t any document window open to pass the event to, we call TGetNextEvent again (recursively), thereby discard the current (de)activate event, and get a new event. After handling the event we reset toBeActivated or toBeDeactivated to nil and return.

(2) The second situation occurs when EventAvail does not return an (de)activate event and either toBeDeactivated or toBeActivated is not nil. This means some action of the TWindow Manager generated an (de)activate event without the toolbox knowing about it (like when you call TSelectWindow to activate a window without bringing it completely to the front). The thing to do is to fill the caller’s EventRecord with an activate event for the window in the toBeActivated or toBeDeactivated global. The call to WindowExists has to do with dialog windows: I’ll get to that later. (3) The third and final situation occurs when EventAvail doesn’t return an (de)activate event, and both toBeActivated and toBeDeactivated are nil. There’s nothing out of the ordinary to do here, so we simply call GetNextEvent and return the event.

While I was debugging this routine, I discovered something I had not anticipated since it had not been mentioned in Inside Macintosh: now and then, activate events were being returned for desk accessories! I was expecting GetNextEvent to take care of this by calling SystemEvent to pass the event on to the desk accessory directly (see page I-442). To remedy this I included the extra test in TGetNextEvent on the type of window belonging to an activate event. If the window isn’t one of our (tool or document-) windows, we leave the event as it is. Another surprise were the deactivate events that were being posted for invisible windows. But if you think about it, it makes sense. When you hide the front window, the toolbox moves the window behind it to the front and activates it. Should you make your hidden window visible again, it will appear behind the current frontmost window, and should thus appear deactivated. Therefore the deactivate event. It makes you realize a lot is happening behind the scenes ...

Adding Windows

At first I thought it would be easy to write front ends for functions like NewWindow and GetNewWindow. All I would have to do was make sure that the calling application wasn’t trying to create new document windows in front of existing tool windows. I thought I could check this with a couple of nested if-else statements. I managed to put together some stuff, but it didn’t seem to cover everything. Whenever I thought about the code for a while, I would come up with a strange situation (usually in the bathroom, does that happen to you too?) that I hadn’t anticipated yet. Things went really wrong when I tried to write TShowWindow! There were so many different combinations of windows, I had to take a completely different approach. The important thing to realize is that once the application program starts creating new windows, or making windows visible, these windows can appear absolutely anywhere in the window list. A document window could appear on top, with tools behind it. Or a desk accessory might turn up between a tool and a document window, which is also not allowed. When calling GetNewWindow or NewWindow, windows can turn up in the wrong place, because the caller can specify any position for the new window. When calling ShowWindow, things can go wrong, because you have very little control over what is happening with your invisible windows. OK, I admit I make things worse myself, because the BringToolsForward function, for example, only moves visible tool windows, and leaves invisible tools scattered all over the window list. But it is more efficient to keep only the visible windows in their correct planes. We just have to be very careful when we make windows visible again.

First of all, I changed my earlier TGetNewWindow and TNewWindow into very simple functions that do two things: (1) They create the window as specified by their parameters, but make it invisible. They then set the window’s windowKind field according to an extra TNewWindow or TGetNewWindow parameter. This parameter is also called windowKind, and lets the application indicate if it wants a tool window or document window. (Remember that the TWindow Managers distinguishes tool windows by looking at the windowKind field in the WindowRecord.) But the important thing is that the window is always created invisible. (2) If the caller wants the window to be visible, I call the function TShowWindow afterwards. TShowWindow does the tedious work of making the window show up in an allowed position, and all the testing code is centralized in this one function. So how do I find out if the caller wants a visible or invisible window? Well, when the calling application uses TNewWindow it’s easy: I just examine the visible parameter. With TGetNewWindow I have do some extra work, because there is no visible parameter, but only a WIND resource. TGetNewWindow reads the WIND resource manually with a GetResource call, and gets the visible field from there. While the resource is in memory, TGetNewWindow also reads the other fields from it, such as the title, goAwayFlag etc. It then calls ReleaseResource to get rid of the WIND template, and creates the window with a normal NewWindow call.

Removing Windows

Before I get into TShowWindow, I want to point out that removing windows is very similar to adding windows. TCloseWindow and TDisposeWindow are very simple functions that call THideWindow to hide the window and make sure the rest of the windows are still in allowed positions. They then get rid of the window data structures through the usual calls to CloseWindow and DisposeWindow. There is one tricky situation though: suppose the window is the active one. THideWindow will then post a deactivate event for it in toBeDeactivated. If the window gets destroyed, that event will have to be removed. That’s why I check if the window is equal to toBeDeactivated, and if it is, clear toBeDeactivated. The toolbox does the same thing (hardly surprising, because that’s where I got the idea in the first place): CloseWindow and DisposeWindow check CurDeactive and clear it if necessary.

So it all boils down to THideWindow and TShowWindow - on to the next section, class.

Hiding and Showing - Where Are We in the First Place?

If you start thinking about it, many strange permutations of windows can occur on the desktop: desk accessories, tool windows, document windows, modal dialogs and modeless dialogs. Somehow, we have to keep the tools in front of the documents and modeless dialogs, while letting DAs and modal dialogs be on top from time to time. And any category of windows might be absent - suppose the application doesn’t have any tool windows visible?

There may be many window permutations, but you will be pleased to hear the number is not infinite. In fact, there are precisely ten distinct positions a window can be in with respect to the other windows on the screen. (There may be more or less, depending on your own way of classifying windows.) Once you know in what position a particular window is, it’s very clear what you have to do when that window is hidden or shown. The utility function TGetWPosition finds out what this position is: THideWindow or TShowWindow pass it a window, and it will tell them in which of the ten places the window is. It returns one of the ten possible constants describing this position. Note that TGetWPosition works for both visible and invisible windows. Since these ten positions are central to understanding how THideWindow and TShowWindow work, let me list them for you (remember that our window can be visible or invisible, and can be either a tool-, document- or dialog window). You’ll want to glance at figure 2 along the way.

alone there are no other visible windows on the screen

beforeDialog our window is completely in front, and the window behind it is a modal dialog

beforeSystem our window is completely in front, and the window behind it is a desk accessory

beforeTool our window is completely in front, and the window behind it is a tool window

beforeDocument our window is completely in front, and the window behind it is a document window

behindFrontSystem there are one or more desk accessories in front, and our window is lying just behind them (but before any tool or document windows); it could be our window is the frontmost tool window

betweenTools our window is lying among the tool windows; if it’s visible, it could also be the last tool window

behindTools our window is lying behind all the tool windows, but in front of any document windows; if our window is a tool, it can only be invisible, otherwise it would be in the betweenTools position

behindSystem this is really an obscure arrangement, and only exists when our window is invisible: there are no document windows, there is a tool in front, and the tools are followed by one or more desk accessories; our invisible window is lying behind the desk accessories

betweenDocuments our window is lying among the document windows; if it’s a visible document window, it certainly isn’t the frontmost one

TGetWPosition starts with a pointer to the frontmost visible window, and works its way through the window list until it finds our window. While it’s doing this, it keeps track of current window position. Once it hits our window, it returns this position. Notice that when our window is visible, TGetWPosition really looks for the window preceding it. That way the algorithm works for both visible and invisible windows. As a bonus you also get the windowKind of the frontmost window in one of the parameters. THideWindow and TShowWindow might want to look at this to see if there is a desk accessory in front.

Figure 2: Window Positions

We’ve Found Our Place!

Now everything falls into place. TShowWindow and THideWindow simply call TGetWPosition, and let three big switch statements (the first if we are dealing with a tool window, the second for a document window/modeless dialog and a little one for a modal dialog) handle the nitty gritty details. We have a case for almost every window position. Some cases are the same (like hiding a tool in the positions alone and beforeSystem), while others are so obscure that they cannot possibly occur in each function (there is no case for hiding a window in the behindSystem position). If you look at the source, you’ll see that each case executes a small but different block of code. The code deals with things like setting the toBeActivated and toBeDeactivated globals, bringing tools forward with BringToolsForward, updating frontToolWindow, backToolWindow and frontDocWindow, and sometimes highlighting windows manually with HiliteWindow and HiliteTools. Most cases are easy to follow, although there are a few complex ones. The behindTools case for a hiding a document window does a lot of things, so it is a nice example to illustrate how all of this works.

An Example of Hiding a Window

When our document window is behindTools, it is obviously the frontmost document window (it is lying just behind the tools). This means we have to find the new document window (our window is going to be hidden). We call NextVisWindow with a windowKind parameter of userKind. This means NextVisWindow will return the next visible document window (or modeless dialog, because TGetWKind considers modeless dialogs the same as document windows). But suppose the next visible window is a desk accessory? That’s why there is the other call to NextVisWindow. This call determines what the real first window behind ours is, regardless of the type of the window, because we use the anyKind parameter. We then hide our window with ShowHide.

If there is no window behind ours, we only have to post a deactivate event for our hidden window. If there is, there are two possibilities: it is a desk accessory - if behindWindow differs from frontDocWindow - or it is a document window, in which case behindWindow should be equal to frontDocWindow. A desk accessory will have to be brought completely to the front - if there isn’t already a DA in front - with SelectWindow. Then something important: a document window that could be lying much further back (behind the DA) has to be brought forward, just behind the tool windows. Tools always “stick” to at least one document window. This is what the BringForward call is for. Finally, if the window behind ours was not a DA, but a document window, we make it the active window by highlighting it and posting the necessary activate events. All of this only if there isn’t a DA in front, of course. Phew! Rest assured that if you understood this, the rest of the cases are a piece of cake.

TNewDialog, TGetNewDialog, TCloseDialog in TDisposDialog?

Don’t panic! I’m not going to write front ends for every manager in the toolbox. You will have to do a few extra things for dialogs though (especially to get the window behind it to deactivate properly). When you want to put up a new dialog, do the following: first call NewDialog or GetNewDialog to create the dialog, but make sure it is still invisible. Then call TShowWindow to make it visible. TShowWindow will ensure that the document window behind it gets deactivated properly, and that all the tools are unhighlighted. Chances are that you are already doing this (creating an invisible dialog, and showing it afterwards) to install a user item to boldly go where no man ... oops, to boldly outline the OK button in the dialog. To remove a dialog you should do something similar: call THideWindow to hide it, and CloseDialog or DisposDialog afterwards.

You might remember that when a window gets destroyed, a possible pending deactivate event for it should be removed as well. Unfortunately, THideWindow posts a deactivate event in toBeDeactivated, but it never gets removed. Sure, CloseDialog and DisposDialog remove the toolbox’s event in CurDeactive, but what about our own event? I suppose I could have written front ends for these Dialog Manager functions, but I think I’ve gone far enough as is. So this is where the WindowExists function comes in (remember, I mentioned it back when I was discussing TGetNextEvent). As a last resort, WindowExists gets called to make sure the window that needs deactivating still exists. If it doesn’t, we call TGetNextEvent again to get a new event instead.

The Point Of It All

The advantage of all this tedious, boring, dense and voluminous code is that you can forget it. Yes, go ahead and forget it. Once the TWindows.c module has been compiled, you can link it with your application and you won’t have to worry about tool windows again. Just change your usual calls to the Window Manager into calls to the TWindow Manager. (See TWindowTester for examples). Whenever you create new document or tool windows, hide them, drag them around the screen, or bring them to the front, the TWindow Manager will do its utmost best to keep you happy by keeping those tools floating above your documents.

There is far more information in the source files than I have mentioned here. Especially the sources on the source code disks have lots more comments and explanations than what would fit in the magazine. Still, I might have overlooked something. In that case (or for any not necessarily useful reason - what I mean to say is: I don’t mind getting mail), you can always reach me at one of the addresses below:

Thomas Fruin fruin@hlerul5.BITNET

Galgewater 38 thomas@uvabick.UUCP

2311 VZ LEIDEN dibs@well.UUCP

The Netherlands hol0066 on AppleLink

2:508/15 on FidoNet

Continued in next frame
Volume Number:4
Issue Number:12
Column Tag:C Workshop

Tool Window Wanager (code)

{1}
Listing:  TWindow.c

/*
 *  TWindows.c
 *
 *  C source of an extended Window Manager that
 *  supports tool  windows; these are windows
 *  that always float on top, for palettes and
 *  tools.
 *
 *  Written in MPW C 2.0
 *
 *  Copyright Thomas Fruin 1988
 *  All rights reserved.
 */


/*
 *  T O O L B O X   I N C L U D E S
 */

#include <Types.h>
#include <Memory.h>
#include <Quickdraw.h>
#include <Events.h>
#include <Windows.h>
#include <ToolUtils.h>
#include <OSUtils.h>
#include <Resources.h>


/*
 *  T O O L B O X   D E F I N I T I O N S
 */

#define GrayRgn     (*(RgnHandle *)0x9EE)
#define WindowList  (*(WindowPeek *)0x9D6)


/*
 *  D E F I N I T I O N S
 */

  /* Defines LONGINT, INTEGER, the windowKind
     constants and the externally callable
     TWindow Manager functions. */

#include <TWindows.h>

  /* Result codes describing window positions, as
     returned by the function GetWPosition. */

#define alone             0
#define beforeDialog      1
#define beforeSystem      2
#define beforeTool        3
#define beforeDocument    4
#define behindFrontSystem 5
#define betweenTools      6
#define behindTools       7
#define behindSystem      8
#define betweenDocuments  9

  /* Miscellaneous */

#define deactivFlag       0
#define noDrag       -32768
#define invisible     false
#define curVersion        1
#define postponeCalc   true
#define normalCalc    false


/*
 *  T Y P E S
 */

  /* The WINDData structure matches the structure
     of a WIND resource in a resource file.  It
     is used to access the various fields of the
     WIND resource after it has been read in from
     the file. */

struct WINDRecord
  {
    Rect      boundsRect;
    INTEGER   procID;
    Boolean   visible;
    char      filler1;
    Boolean   goAwayFlag;
    char      filler2;
    LONGINT   refCon;
    Str255    title;
  };
typedef struct WINDRecord WINDData;
typedef struct WINDRecord *WINDPtr;
typedef struct WINDRecord **WINDHandle;

  /* The WDEFHeader structure matches the
     structure of the (optional) header of a
     window definition procedure resource. */

struct WDEFRecord
  {
    INTEGER   branch;
    INTEGER   flags;
    LONGINT   type;
    INTEGER   ID;
    INTEGER   version;
  };
typedef struct WDEFRecord WDEFHeader;
typedef struct WDEFRecord *WDEFPtr;
typedef struct WDEFRecord **WDEFHandle;


/*
 *  G L O B A L S
 */

static WindowPeek toBeActivated,
                  toBeDeactivated,
                  frontToolWindow,
                  backToolWindow,
                  frontDocWindow;
static RgnHandle  firstRgn,
                  secondRgn;
static SysEnvRec  theWorld;


/*
 *  T I N I T W I N D O W S
 */

void
TInitWindows()
{
  OSErr   err;

  frontToolWindow =
  backToolWindow  =
  frontDocWindow  =
  toBeDeactivated =
  toBeActivated   = nil;
  firstRgn  = NewRgn();
  secondRgn = NewRgn();
  err = SysEnvirons( curVersion, &theWorld );
}


/*
 *  T G E T N E W W I N D O W
 */

WindowPtr
TGetNewWindow( windowKind, windowID,
               wStorage, behind )
  INTEGER     windowKind;
  INTEGER     windowID;
  Ptr         wStorage;
  WindowPtr   behind;
{
  Rect        boundsRect;
  Str255      title;
  Boolean     visible;
  INTEGER     procID;
  Boolean     goAwayFlag;
  LONGINT     refCon;
  WindowPtr   theWindow;
  WINDHandle  theWINDHandle;
  WINDPtr     theWINDPtr;

  theWINDHandle =
    ( WINDHandle )GetResource( ‘WIND’,windowID );
  if ( theWINDHandle != nil )
  {
    theWINDPtr = *theWINDHandle;
    boundsRect = theWINDPtr->boundsRect;
    visible    = theWINDPtr->visible;
    procID     = theWINDPtr->procID;
    goAwayFlag = theWINDPtr->goAwayFlag;
    refCon     = theWINDPtr->refCon;
    if ( theWINDPtr->title.length == 0 )
      title.length = 0;
    else
      BlockMove( &( theWINDPtr->title ), &title,
          ( Size )theWINDPtr->title.length + 1 );
    p2cstr( &title );

    ReleaseResource(( Handle )theWINDHandle );
    theWindow =
      NewWindow( wStorage, &boundsRect, &title,
                 invisible, procID, behind,
                 goAwayFlag, refCon );
    if ( theWindow != nil )
    {
      (( WindowPeek )theWindow )->windowKind =
          windowKind;
      if ( visible != false )
        TShowWindow( theWindow );
    }
  }
  else
    theWindow = nil;
  return( theWindow );
}


/*
 *  T N E W W I N D O W
 */

WindowPtr
TNewWindow( windowKind, wStorage, boundsRect,
            title, visible, procID, behind,
            goAwayFlag, refCon )
  INTEGER     windowKind;
  Ptr         wStorage;
  Rect        *boundsRect;
  char        *title;
  Boolean     visible;
  INTEGER     procID;
  WindowPtr   behind;
  Boolean     goAwayFlag;
  LONGINT     refCon;
{
  WindowPtr theWindow;

  theWindow =
    NewWindow( wStorage, boundsRect, title,
               invisible, procID, behind,
               goAwayFlag, refCon );
  if ( theWindow != nil )
  {
    (( WindowPeek )theWindow )->windowKind =
          windowKind;
    if ( visible != false )
      TShowWindow( theWindow );
  }
  return( theWindow );
}


/*
 *  T C L O S E W I N D O W
 */

void
TCloseWindow( theWindow )
  WindowPtr   theWindow;
{
  THideWindow( theWindow );
  CloseWindow( theWindow );

  if ( theWindow == toBeDeactivated )
    toBeDeactivated = nil;
}


/*
 *  T D I S P O S E W I N D O W
 */

void
TDisposeWindow( theWindow )
  WindowPtr   theWindow;
{
  THideWindow( theWindow );
  DisposeWindow( theWindow );

  if ( theWindow == toBeDeactivated )
    toBeDeactivated = nil;
}


/*
 *  T S E L E C T W I N D O W
 */

void
TSelectWindow( theWindow )
  WindowPtr   theWindow;
{
  void        BringForward();
  WindowPeek  thePWindow,
              theFrontWindow,
              NextVisWindow(),
              PrevVisWindow();
  INTEGER     theWKind,
              frontWKind;
  Boolean     toolsVisible,
              BringToolsForward();

      /* Initialize some variables. */

  thePWindow     = ( WindowPeek )theWindow;
  theFrontWindow = ( WindowPeek )FrontWindow();
  theWKind       = TGetWKind( theWindow );
  frontWKind     =
    TGetWKind(( WindowPtr )theFrontWindow );

  if ( theWKind == toolKind )
  {
    if ( frontWKind == systemKind )
    {
        /* Window is a tool window, with a desk
           accessory on top. */

      SelectWindow( theWindow );
      (void)BringToolsForward( thePWindow );
      if ( frontDocWindow != nil )
      {
        frontDocWindow->hilited = true;
        BringForward( frontDocWindow,
                      backToolWindow,
                      normalCalc );
        toBeActivated = frontDocWindow;
      }
      frontToolWindow = thePWindow;
    }
    else
    {
        /* Window is a tool window, and no desk
           accessory on top. */

      if ( thePWindow != frontToolWindow )
      {
        if ( thePWindow == backToolWindow )
        {
          backToolWindow =
            NextVisWindow( toolKind, thePWindow );
          if ( backToolWindow == nil )
            backToolWindow =
              PrevVisWindow( thePWindow );
        }
        BringToFront( theWindow );
        frontToolWindow = thePWindow;
      }
    }
  }
  else if ( theWKind == userKind )
  {
    if ( frontWKind == systemKind )
    {
        /* Window is a document window (or a
           modeless dialog window), with a desk
           accessory on top. */

      toolsVisible =
        BringToolsForward(( WindowPeek )inFront );
      if ( toolsVisible == false )
        SelectWindow( theWindow );
      else
      {
        thePWindow->hilited = true;
        BringForward( theWindow,
                      backToolWindow,
                      normalCalc );
      }
      toBeActivated  =
      frontDocWindow = thePWindow;
    }
    else
    {
        /* Window is a document window (or a
           modeless dialog window), and no desk
           accessory on top. */

      if ( thePWindow != frontDocWindow )
      {
        if ( frontToolWindow == nil )
          SelectWindow( theWindow );
        else
        {
          HiliteWindow(( WindowPtr)frontDocWindow,
                                          false );
          thePWindow->hilited = true;
          BringForward( thePWindow,
                        backToolWindow,
                        normalCalc );
        }
        toBeDeactivated = frontDocWindow;
        toBeActivated   =
        frontDocWindow  = thePWindow;
      }
    }
  }
}


/*
 *  T H I D E W I N D O W
 */

void
THideWindow( theWindow )
  WindowPtr   theWindow;
{
  WindowPeek  thePWindow,
              behindWindow;
  INTEGER     theWKind,
              thePosition,
              frontWKind,
              GetWPosition();
  void        HiliteTools();

  if ((( WindowPeek )theWindow )->visible
                                != false )
  {
    thePWindow  = ( WindowPeek )theWindow;
    theWKind    = TGetWKind( theWindow );
    thePosition = GetWPosition( thePWindow,
                                &frontWKind );

      /* Hide the window, and update the desktop
         according to the window’s position. */

    if ( theWKind == toolKind )
    {
        /* The window is a tool window. */

      switch( thePosition )
      {
        case alone:
        case beforeSystem:
            HideWindow( theWindow );
            frontToolWindow =
            backToolWindow  = nil;
            break;

        case beforeTool:
            ShowHide( theWindow, false );
            frontToolWindow =
              ( WindowPeek )FrontWindow();
            break;

        case behindFrontSystem:
            ShowHide( theWindow, false );
            frontToolWindow =
              NextVisWindow( toolKind,
                             thePWindow );
            if ( frontToolWindow == nil )
              backToolWindow = nil;
            break;

        case beforeDocument:
            frontToolWindow =
            backToolWindow  = nil;
            ShowHide( theWindow, false );
            break;

        /* NOTE: no behindTools possible! */

        case betweenTools:
            ShowHide( theWindow, false );
            if ( thePWindow == backToolWindow )
              backToolWindow =
                PrevVisWindow( thePWindow );
            break;
      }
    }
    else if ( theWKind == userKind )
    {
        /* The window is a document window
           (or a modeless dialog window). */

      thePWindow->hilited = false;

      switch( thePosition )
      {
        case alone:
        case beforeSystem:
            toBeDeactivated = thePWindow;
            frontDocWindow  = nil;

        case betweenDocuments:
            HideWindow( theWindow );
            break;

        case beforeDocument:
            HideWindow( theWindow );
            toBeDeactivated = thePWindow;
            toBeActivated   =
            frontDocWindow  =
              ( WindowPeek )FrontWindow();
            break;

        case behindFrontSystem:
            ShowHide( theWindow, false );
            frontDocWindow =
              NextVisWindow( userKind,theWindow );
            break;

        case behindTools:
            frontDocWindow =
              NextVisWindow( userKind,theWindow);
            behindWindow =
              NextVisWindow( anyKind, theWindow );
            ShowHide( theWindow, false );
            if ( behindWindow == nil )
              toBeDeactivated = thePWindow;
            else
            {
              if ( behindWindow != frontDocWindow)
              {
                if ( frontWKind == toolKind )
                {
                  HiliteTools( false );
                  SelectWindow(( WindowPtr )
                                behindWindow );
                  toBeDeactivated = thePWindow;
                }
                if ( frontDocWindow != nil )
                  BringForward( frontDocWindow,
                                backToolWindow,
                                normalCalc );
              }
              else
              {
                if ( frontWKind == toolKind )
                {
                  HiliteWindow( frontDocWindow,
                                true );
                  toBeActivated = frontDocWindow;
                  toBeDeactivated = thePWindow;
                }
              }
            }
            break;
      }
    }
    else
    {
        /* The window is a modal dialog window.*/

      switch( thePosition )
      {
        case alone:
        case beforeSystem:
        case beforeDialog:
            HideWindow( theWindow );
            break;

        case beforeTool:
        case beforeDocument:
            ShowHide( theWindow, false );
            HiliteTools( true );
            if ( frontDocWindow != nil )
            {
              toBeDeactivated = thePWindow;
              toBeActivated   = frontDocWindow;
              HiliteWindow(( WindowPtr)
                          frontDocWindow, true );
            }
            break;
      }
    }
  }
}


/*
 *  T S H O W W I N D O W
 */

void
TShowWindow( theWindow )
  WindowPtr   theWindow;
{
  WindowPeek  thePWindow,
              theFrontWindow,
              behind,
              PrevVisWindow();
  INTEGER     theWKind,
              frontWKind,
              thePosition,
              GetWPosition();
  Boolean     toolsExist,
              BringToolsForward();
  void        BringForward(),
              HiliteTools();

  if ((( WindowPeek )theWindow )->visible
                                  == false )
  {
            /* The window is still invisible. */

    thePWindow     = ( WindowPeek )theWindow;
    theFrontWindow = ( WindowPeek )FrontWindow();
    theWKind       = TGetWKind( theWindow );
    thePosition    =
      GetWPosition( thePWindow, &frontWKind );

    if ( theWKind == toolKind )
    {
        /* The window is a tool window.  Assume
           it needs to appear highlighted
           (correct this later). */

      thePWindow->hilited = true;

      switch( thePosition )
      {
        case beforeSystem:
            ShowWindow( theWindow );
            ( void )BringToolsForward(thePWindow);
            if ( frontDocWindow != nil )
            {
              frontDocWindow->hilited = true;
              BringForward( frontDocWindow,
                            backToolWindow,
                            normalCalc );
              toBeActivated = frontDocWindow;
            }
            frontToolWindow = thePWindow;
            break;

        case behindFrontSystem:
            thePWindow->hilited = false;
            
        case alone:
        case beforeDocument:
            if ( backToolWindow == nil )
               backToolWindow =  thePWindow;

        case beforeTool:
            ShowHide( theWindow, true );
            frontToolWindow = thePWindow;
            break;

        case behindTools:
            backToolWindow = thePWindow;

        case betweenTools:
            if ( frontWKind == systemKind )
              thePWindow->hilited = false;
            ShowHide( theWindow, true );
            break;

        case behindSystem:
            SendBehind( theWindow,
              ( WindowPtr )backToolWindow );
            if ( frontWKind == systemKind )
              thePWindow->hilited = true;
            ShowHide( theWindow, true );
            backToolWindow  = thePWindow;
            break;

        case betweenDocuments:
            if ( backToolWindow != nil )
              behind = backToolWindow;
            else
            {
              behind =
                PrevVisWindow( frontDocWindow );
              frontToolWindow = thePWindow;
            }
            if ( behind == nil )
              BringToFront( theWindow );
            else
            {
              SendBehind( theWindow, behind );
              if ( frontWKind == systemKind )
                thePWindow->hilited = false;
            }
            ShowHide( theWindow, true );
            backToolWindow = thePWindow;
            break;
      }
    }
    else if ( theWKind == userKind )
    {
        /* The window is a document window. */

      switch( thePosition )
      {
        case beforeSystem:
            toolsExist = BringToolsForward(
                    ( WindowPeek )inFront );
            if ( toolsExist != false )
              thePWindow->hilited = true;

        case alone:
            ShowWindow( theWindow );
            toBeActivated  =
            frontDocWindow = thePWindow;
            break;

        case beforeTool:
            if ( frontDocWindow != nil )
            {
              HiliteWindow(( WindowPtr )
                      frontDocWindow, false );
              toBeDeactivated = frontDocWindow;
            }
            SendBehind( theWindow, 
                        backToolWindow );
            thePWindow->hilited = true;
            ShowHide( theWindow, true );
            toBeActivated  =
            frontDocWindow = thePWindow;
            break;

        case beforeDocument:

              /* The three steps HiliteWindow,
                 HiliteWindow and ShowHide could
                 be replaced with one call to
                 ShowWindow, but this gives a
                 better visual effect. (Try the
                 difference.) */

            HiliteWindow( frontDocWindow, false );
            HiliteWindow( theWindow, true );
            ShowHide( theWindow, true );
            toBeDeactivated = frontDocWindow;
            toBeActivated   =
            frontDocWindow  = thePWindow;
            break;

        case behindFrontSystem:
            if ( backToolWindow != nil )
              SendBehind( theWindow,
                  ( WindowPtr )backToolWindow );
            ShowHide( theWindow, true );
            frontDocWindow  = thePWindow;
            break;

        case betweenTools:
            SendBehind( theWindow,
                  ( WindowPtr )backToolWindow );

        case behindTools:
            if ( frontWKind == toolKind )
            {
              thePWindow->hilited = true;
              toBeActivated = thePWindow;
              if ( frontDocWindow != nil )
              {
                HiliteWindow(( WindowPtr )
                    frontDocWindow, false );
                toBeDeactivated = frontDocWindow;
              }
            }
            ShowHide( theWindow, true );
            frontDocWindow  = thePWindow;
            break;

        case behindSystem:
            SendBehind( theWindow,
                  ( WindowPtr )backToolWindow );
            if ( frontWKind == toolKind )
            {
              thePWindow->hilited = true;
              toBeActivated = thePWindow;
            }
            ShowHide( theWindow, true );
            frontDocWindow  = thePWindow;
            break;

        case betweenDocuments:
            ShowHide( theWindow, true );
            break;
      }
    }
    else
    {
        /* The window is a modal dialog window.*/

      switch( thePosition )
      {
        case alone:
        case beforeSystem:
        case beforeDialog:
            ShowWindow( theWindow );
            break;

        case beforeTool:
        case beforeDocument:
            HiliteTools( false );
            if ( frontDocWindow != nil )
            {
              toBeDeactivated = frontDocWindow;
              HiliteWindow(( WindowPtr )
                    frontDocWindow, false );
            }
            ShowHide( theWindow, true );
            break;
      }
    }
  }
}


/*
 *  T F R O N T W I N D O W
 */

WindowPtr
TFrontWindow( wantedKind )
  INTEGER     wantedKind;
{
  WindowPtr   theWindow;
  INTEGER     theWKind;

  theWindow = FrontWindow();
  theWKind  = TGetWKind( theWindow );

  if ( theWKind == systemKind  ||
       theWKind == dialogKind )
    return( theWindow );
  else
    switch ( wantedKind )
    {
      case toolKind:
          return(( WindowPtr )frontToolWindow );
          break;

      case userKind:
          return(( WindowPtr )frontDocWindow );
          break;

      case anyKind:
          return( theWindow );
          break;
    }
}


/*
 *  T D R A G W I N D O W
 */

void
TDragWindow( theWindow, theEvent, boundsRect )
  WindowPtr   theWindow;
  EventRecord *theEvent;
  Rect        *boundsRect;
{
  GrafPtr     savePort,
              wPort;
  Rect        limitRect,
              slopRect;
  RgnHandle   desktopRgn;
  LONGINT     result;
  INTEGER     hDrag,
              vDrag;
  Point       thePoint;

  if (( theEvent->modifiers & cmdKey ) == false )
  {
    TSelectWindow( theWindow );
    if ( StillDown() == false )
      return;
  }

  GetPort    ( &savePort );
  GetWMgrPort( &wPort );
  SetPort    (  wPort );

  if ( theWorld.machineType == envMac  ||
       theWorld.machineType == envXL  )
    desktopRgn = GrayRgn;
  else
    desktopRgn = GetGrayRgn();

  GetClip  ( firstRgn );
  SetClip  ( desktopRgn );
  ClipAbove(( WindowPeek )theWindow );

  CopyRgn((( WindowPeek )theWindow )->strucRgn,
                                     secondRgn );
  SetPt  ( &thePoint, theEvent->where.h,
                      theEvent->where.v );
  GlobalToLocal( &thePoint );
  result = DragGrayRgn( secondRgn, &thePoint,
                  boundsRect, boundsRect,
                  noConstraint, ( ProcPtr )nil );

  SetClip( firstRgn );

  vDrag = HiWord( result );
  hDrag = LoWord( result );
  if ( !( vDrag == noDrag && hDrag == noDrag ))
  {
    SetPort( theWindow );
    SetPt  ( &thePoint,
              theWindow->portRect.left,
              theWindow->portRect.top );
    LocalToGlobal( &thePoint );
    MoveWindow( theWindow, thePoint.h + hDrag,
                    thePoint.v + vDrag, false );
  }

  SetPort( savePort );
  SetEmptyRgn( firstRgn );
  SetEmptyRgn( secondRgn );
}


/*
 *  T G E T N E X T E V E N T
 */

Boolean
TGetNextEvent( eventMask, theEvent )
  INTEGER   eventMask;
  EventRecord *theEvent;
{
  Boolean   result,
        WindowExists();
  INTEGER   theWKind;
  void    HiliteTools();

  if ( EventAvail( eventMask, theEvent )
              != false  &&
       theEvent->what == activateEvt )
  {
    result = GetNextEvent( eventMask, theEvent );
    theWKind =
      TGetWKind(( WindowPtr )theEvent->message );

    if (( theEvent->modifiers & activeFlag )
            == false )
    {
      if ( theEvent->message
            != ( LONGINT )toBeDeactivated )
      {
        if ( toBeDeactivated != nil )
          theEvent->message =
            ( LONGINT )toBeDeactivated;
        else
        {
          if ( theWKind == userKind  ||
               theWKind == toolKind  )
          {
            if ( theWKind == toolKind )
              HiliteTools( false );
            if ( frontDocWindow != nil )
            {
              HiliteWindow(( WindowPtr )
                  frontDocWindow, false );
              theEvent->message  =
                ( LONGINT )frontDocWindow;
            }
            else
              result = TGetNextEvent( eventMask,
                                      theEvent );
          }
        }
      }
      toBeDeactivated = nil;
    }
    else
    {
      if ( theEvent->message
            != ( LONGINT )toBeActivated )
      {
        if ( toBeActivated != nil )
          theEvent->message =
            ( LONGINT )toBeActivated;
        else
        {
          if ( theWKind == userKind  ||
               theWKind == toolKind  )
          {
            if ( theWKind == toolKind )
              HiliteTools( true );
            if ( frontDocWindow != nil )
            {
              HiliteWindow(( WindowPtr )
                  frontDocWindow, true );
              theEvent->message  =
                ( LONGINT )frontDocWindow;
            }
            else
              result = TGetNextEvent( eventMask,
                                      theEvent );
          }
        }
      }
      toBeActivated = nil;
    }
  }
  else
  {
    if ( toBeDeactivated != nil         &&
         ( eventMask & activMask ) != false )
    {
      if ( WindowExists( toBeDeactivated )
            != false )
      {
        theEvent->what      = activateEvt;
        theEvent->message   =
          ( LONGINT )toBeDeactivated;
        theEvent->modifiers = deactivFlag;
        toBeDeactivated     = nil;
        result = true;
      }
      else
      {
        toBeDeactivated   = nil;
        result =
          TGetNextEvent( eventMask, theEvent );
      }
    }
    else if ( toBeActivated != nil        &&
              ( eventMask & activMask) != false )
    {
      theEvent->what      = activateEvt;
      theEvent->message   =
        ( LONGINT )toBeActivated;
      theEvent->modifiers = activeFlag;
      toBeActivated       = nil;
      result = true;
    }
    else
    {
      result = GetNextEvent( eventMask,theEvent);
    }
  }

  return( result );
}


/*
 *  W I N D O W E X I S T S
 */

Boolean
WindowExists( thePWindow )
  WindowPeek  thePWindow;
{
  WindowPeek  loopWindow;
  Boolean     done;

  for ( done = false,
          loopWindow = WindowList;
      done == false  &&  loopWindow != nil;
      loopWindow = loopWindow->nextWindow )
  {
    if ( loopWindow == thePWindow )
      done = true;
  }
  return( done );
}


/*
 *  G E T W P O S I T I O N
 */

static INTEGER
GetWPosition( thePWindow, frontWKind )
  WindowPeek  thePWindow;
  INTEGER     *frontWKind;
{
  WindowPeek  theFrontWindow,
              newFrontWindow,
              loopWindow,
              NextVisWindow();
  Boolean     done;
  INTEGER     result,
              loopWKind;

  theFrontWindow = ( WindowPeek )FrontWindow();

  if ( thePWindow->visible != false &&
       theFrontWindow == thePWindow )
    newFrontWindow =
      NextVisWindow( anyKind, theFrontWindow );
  else
    newFrontWindow = theFrontWindow;

  if ( newFrontWindow == nil )
    return( alone );
  *frontWKind =
    TGetWKind(( WindowPtr )newFrontWindow );

  if ( thePWindow->visible != false &&
       theFrontWindow == thePWindow ||
       theFrontWindow ==
        NextVisWindow( anyKind, thePWindow ))
  {
      /* Our window is visible and the frontmost,
         or it’s invisible and lying in front of
         the frontmost visible window. */

    if ( *frontWKind == systemKind )
      result = beforeSystem;
    else if ( *frontWKind == dialogKind )
      result = beforeDialog;
    else if ( *frontWKind == toolKind )
      result = beforeTool;
    else
      result = beforeDocument;
  }
  else
  {
    if (thePWindow->visible != false )
      thePWindow = PrevVisWindow( thePWindow );

    for ( done = false,
            loopWindow = theFrontWindow;
          done == false;
          loopWindow = loopWindow->nextWindow )
    {
      loopWKind =
        TGetWKind(( WindowPtr )loopWindow );
      if ( loopWindow == theFrontWindow  &&
           loopWKind  == systemKind    )
        result = behindFrontSystem;
      else if ( loopWindow == frontToolWindow &&
            backToolWindow != frontToolWindow  )
        result = betweenTools;
      else if ( loopWindow == backToolWindow )
        result = behindTools;
      else if ( loopWKind == systemKind       &&
                loopWindow->visible != false  &&
                result == behindTools )
        result = behindSystem;
      else if ( loopWindow == frontDocWindow )
        result = betweenDocuments;

      if ( loopWindow == thePWindow )
        done = true;
    }

    return( result );
  }
}


/*
 *  T G E T W K I N D
 */

INTEGER
TGetWKind( theWindow )
  WindowPtr   theWindow;
{
  INTEGER     theWKind,
              varCode,
              windowID;
  WDEFHandle  defProc;

  if ( theWindow != nil )
  {
    theWKind =
      (( WindowPeek )theWindow )->windowKind;

    if ( theWKind <= systemKind )
      theWKind = systemKind;
    else if ( theWKind == dialogKind )
    {
      defProc =
        ( WDEFHandle )
        ((WindowPeek )theWindow )->windowDefProc;

      if ( theWorld.machineType == envMac  ||
           theWorld.machineType == envXL  ) 
        varCode  =
          ( LONGINT )defProc >> 24 && 0x0000000F;
      else
        varCode = GetWVariant( theWindow );

      if (   defProc != nil   &&
            *defProc != nil   &&
          ( *defProc )->type == ‘WDEF’ )
        windowID =
          ( *defProc )->ID * 16 + varCode;
      else
        windowID = dBoxProc;

      if ( windowID == documentProc   ||
           windowID == noGrowDocProc  ||
           windowID == zoomDocProc     )
        theWKind = userKind;
    }
    else if ( theWKind == toolKind )
      /* nothing */ ;
    else if ( theWKind >= userKind )
      theWKind = userKind;
  }
  return( theWKind );
}


/*
 *  B R I N G T O O L S F O R W A R D
 */

static Boolean
BringToolsForward( thePWindow )
  WindowPeek  thePWindow;
{
  WindowPeek  NextVisWindow(),
              toolWindow,
              loopWindow;
  void        BringForward();

  if ( thePWindow == ( WindowPeek )inFront )
  {
    toolWindow = NextVisWindow( toolKind,
                  ( WindowPeek )FrontWindow() );

    if ( toolWindow != nil )
    {
      loopWindow      = toolWindow->nextWindow;
      SelectWindow(( WindowPtr )toolWindow );
      frontToolWindow =
      backToolWindow  = toolWindow;
    }
    else
    {
      return( false );
    }
  }
  else
  {
    loopWindow     = thePWindow->nextWindow;
    backToolWindow = thePWindow;
  }

  SetEmptyRgn( secondRgn );
  thePWindow = nil;

  while ( loopWindow != nil )
  {
    if ( loopWindow->windowKind == toolKind  &&
         loopWindow->visible    != false   )
    {
      toolWindow = loopWindow;
      toolWindow->hilited = true;
      BringForward( toolWindow, backToolWindow,
                                postponeCalc );
      UnionRgn( secondRgn, toolWindow->strucRgn,
                secondRgn );
      backToolWindow = toolWindow;
      if ( thePWindow == nil )
        thePWindow = toolWindow;
    }
    loopWindow = loopWindow->nextWindow;
  }

  CalcVisBehind( thePWindow, secondRgn );
  SetEmptyRgn( secondRgn );

  return( true );
}

/* End of BringToolsForward */


/*
 *  N E X T V I S W I N D O W
 */

static WindowPeek
NextVisWindow( wantedKind, thePWindow )
  INTEGER     wantedKind;
  WindowPeek  thePWindow;
{
  WindowPeek  loopWindow,
              wantedWindow;

  if ( thePWindow == ( WindowPeek )inFront )
  {
    thePWindow = ( WindowPeek )FrontWindow();
    if ( thePWindow == nil )
      return( thePWindow );
    else if ( wantedKind == anyKind   ||
              wantedKind ==
              TGetWKind(( WindowPtr)thePWindow ))
      return( thePWindow );
  }

  for ( wantedWindow = nil,
          loopWindow = thePWindow->nextWindow;
        wantedWindow == nil  &&
          loopWindow != nil;
        loopWindow = loopWindow->nextWindow )
  {
    if ( loopWindow->visible != false )
      if ( wantedKind == anyKind    ||
           wantedKind ==
             TGetWKind(( WindowPtr )loopWindow ))
        wantedWindow = loopWindow;
  }

  return( wantedWindow );
}


/*
 *  P R E V V I S W I N D O W
 */

static WindowPeek
PrevVisWindow( thePWindow )
  WindowPeek  thePWindow;
{
  WindowPeek  loopWindow,
              prevWindow,
              theFrontWindow,
              NextVisWindow();
  Boolean     done;

  theFrontWindow = ( WindowPeek )FrontWindow();

  if ( thePWindow  == theFrontWindow  ||
    theFrontWindow ==
      NextVisWindow( anyKind, thePWindow ))
    return(( WindowPeek )nil );

  prevWindow = theFrontWindow;
  loopWindow = prevWindow->nextWindow;
  done = false;

  while ( done == false )
  {
    if ( loopWindow == thePWindow )
      done = true;
    else
      if ( loopWindow->visible != false )
        prevWindow = loopWindow;
      loopWindow = loopWindow->nextWindow;
  }

  return( prevWindow );
}


/*
 *  B R I N G F O R W A R D
 */

static void
BringForward( thePWindow, behindWindow,
              waitWithCalc )
  WindowPeek  thePWindow;
  WindowPeek  behindWindow;
  Boolean     waitWithCalc;
{
  CopyRgn  ( thePWindow->strucRgn, firstRgn );
  OffsetRgn( thePWindow->port.visRgn,
        - thePWindow->port.portBits.bounds.left,
        - thePWindow->port.portBits.bounds.top );
  DiffRgn  ( firstRgn, thePWindow->port.visRgn,
             firstRgn );
  OffsetRgn( thePWindow->port.visRgn,
          thePWindow->port.portBits.bounds.left,
          thePWindow->port.portBits.bounds.top );

  SendBehind(( WindowPtr )thePWindow,
             ( WindowPtr )behindWindow );
  PaintOne( thePWindow, firstRgn );
  if ( waitWithCalc == false )
    CalcVisBehind( thePWindow,
                   thePWindow->strucRgn );
  SetEmptyRgn( firstRgn );
}


/*
 *  H I L I T E T O O L S
 */

static void
HiliteTools( hilite )
  Boolean     hilite;
{
  WindowPeek  loopWindow;
  Boolean     done;

  if ( frontToolWindow != nil )
  {
    for ( done = false,
            loopWindow = frontToolWindow;
          done == false  &&  loopWindow != nil;
          loopWindow = loopWindow->nextWindow )
    {
      if ( TGetWKind(( WindowPtr )loopWindow )
            == toolKind  &&
           loopWindow->visible != false )
      {
        HiliteWindow(( WindowPtr )loopWindow,
                                      hilite );
        if ( loopWindow == backToolWindow )
          done = true;
      }
    }
  }
}

{2}
Listing:  TWindow.h

/*
 *  TWindows.h
 *
 *  Include file for usage with the TWindow
 *  Manager, an extended window manager that
 *  supports tool windows;  these are windows
 *  that always float on top, for palettes and
 *  tools.
 *
 *  Written in MPW C 2.0
 *
 *  Copyright Thomas Fruin 1988
 *  All rights reserved.
 */


/*
 *  T Y P E S
 */

  /* Define the standard toolbox types INTEGER
     and LONGINT in terms of the equivalents for
     the MPW C compiler. */

#define INTEGER       short
#define LONGINT       long


  /* Constants for the kindes of windows */
  
/*      dialogKind      2   */
/*      userKind        8   */
#define systemKind     -1
#define toolKind    30000
#define anyKind     30001
#define inFront   ((WindowPtr)-1)


  /* Callable functions in the TWindow Manager */

void          TInitWindows();
WindowPtr     TNewWindow();
WindowPtr     TGetNewWindow();
void          TCloseWindow();
void          TDisposeWindow();
void          TSelectWindow();
void          THideWindow();
void          TShowWindow();
WindowPtr     TFrontWindow();
void          TDragWindow();
Boolean       TGetNextEvent();
INTEGER       TGetWKind();
{3}
Listing: make TWindowTester

#
#   Makefile for TWindowTester and TWindows
#
#   TWindowTester is a simple program to demon-
#   strate the usage of tool windows by using
#   routines from TWindows,  a complete manager
#   for handling these windows that always float
#   on top, ususally used for palettes and tools.
#
#   Copyright Thomas Fruin 1988
#   All rights reserved
#


Libs    = “{CLibraries}”CInterface.o      
          “{CLibraries}”CRuntime.o        
          “{CLibraries}”glueenvirons.a.o  
          “{CLibraries}”StdCLib.o

TWindowTester ƒƒ  TWindowTester.c.o TWindows.c.o
  Link TWindowTester.c.o  
     TWindows.c.o         
     {Libs}               
     -o  TWindowTester    
     -t  ‘APPL’           
     -c  ‘TWIN’

TWindowTester ƒƒ  TWindowTester.r
  rez -o TWindowTester    
     Types.r              
     TWindowTester.r      
     -a

TWindowTester.c.o ƒ TWindowTester.c TWindows.h
  C TWindowTester.c

TWindows.c.o      ƒ TWindows.c
  C TWindows.c -d __ALLNU__
{4}
Listing:  TWindowTester.c

/*
 * TWindowTester
 *
 * C source of a program to demo usage of tool windows by
 * using routines from the TWindow Manager.  Tool windows  are 
 * windows that always float on top, typically for palettes and 
 *tools.
 * Thomas Fruin 1988
 *
 * fruin@hlerul5.BITNET   University of Leiden
 * thomas@uvabick.UUCP    University of Amsterdam
 * dibs@well.UUCP
 * hol0066.AppleLink
 * 2:508/15.FidoNetThe Netherlands
 *
 * TWindowTester is based on MiniEdit - Mini text
 * editor,converted from
 * the listing in Macintosh Revealed, vol II. As little as 
 * possible was
 * modified in the original program, although most of the
 * functionality is no longer there.
 */


#include <Types.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Windows.h>
#include <Events.h>
#include <TextEdit.h>
#include <Controls.h>
#include <Dialogs.h>
#include <Menus.h>
#include <Memory.h>
#include <OSUtils.h>
#include <ToolUtils.h>
#include <Desk.h>

#include <TWindows.h>

#define MenuBarHeight20
#define TitleBarHeight  18
#define ScreenMargin  4

#define MinWidth 80
#define MinHeight80
#define SBarWidth16

#define AppleID   1
#define AboutItem 1

#define FileID    2
#define NewItem   1
#define NewToolItem 2
#define CloseItem 3
#define QuitItem  5

#define EditID    3
#define UndoItem  1
#define CutItem   3
#define CopyItem  4
#define PasteItem 5
#define ClearItem 7

#define WindowsID 4

#define ToolsID   5

#define AboutID     128
#define windowID   1000
#define scrollID   1000
#define toolID     1001

#define okButton  1
#define aboutBold 8

#define wHOffset 20
#define wVOffset 20
#define tHOffset 40
#define tVOffset 40

#define undoCmd   0
#define cutCmd    2
#define copyCmd   3
#define pasteCmd  4
#define clearCmd  5

 /* z-ordered list of windows (nearest first) */
 
#define windowList (*(WindowPeek *)0x9D6)
#define nilproc  ((ProcPtr)0)

struct WindowData
{
 ControlHandle scBar;
};

typedef struct WindowData WindowData;
typedef struct WindowData *WDPtr;
typedef struct WindowData **WDHandle;

MenuHandleAppleMenu,
 FileMenu,
 EditMenu,
 WindowsMenu,
 ToolsMenu;
 
INTEGER toolCount,
 windowCount,
 lastToolsItem,
 lastWindowsItem;
 
Boolean Finished,
 ErrorFlag;

main()
{
 Initialize();

 do
 {
 if ( FrontWindow() == nil )
 DisableItem( FileMenu, CloseItem );
 SystemTask();
 DoEvent();
 }
 while ( !Finished );
}

Initialize()
{
 INTEGERtheMask;

 InitGraf( &qd.thePort );
 InitFonts();
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs( nilproc );
 
 TInitWindows();

 theMask = everyEvent - keyUpMask;
 SetEventMask( theMask );
 FlushEvents( everyEvent, 0 );

 SetUpMenus();
 InitCursor();
}

SetUpMenus()
{
 AppleMenu = GetMenu( AppleID );
 AddResMenu( AppleMenu, ‘DRVR’ );
 InsertMenu( AppleMenu, 0 );

 FileMenu = GetMenu( FileID );
 InsertMenu( FileMenu, 0 );

 EditMenu = GetMenu( EditID );
 InsertMenu( EditMenu, 0 );
 
 WindowsMenu = GetMenu( WindowsID );
 InsertMenu( WindowsMenu, 0 );
 
 ToolsMenu = GetMenu( ToolsID );
 InsertMenu( ToolsMenu, 0 );

 DrawMenuBar();
 
 lastToolsItem   =
 lastWindowsItem = 0;
}

DoEvent()
{
 EventRecordtheEvent;
 
 ErrorFlag = false;

 if ( TGetNextEvent( everyEvent, &theEvent ))
 {
 switch ( theEvent.what )
 {
 case mouseDown:
 DoMouseDown( &theEvent );
 break;
 
 case keyDown:
 case autoKey:
 DoKeyStroke( &theEvent );
 break;
 
 case updateEvt:
 DoUpdate( &theEvent );
 break;
 
 case activateEvt:
 DoActivate( &theEvent );
 break;
 
 default:
 break;
 }
 }
}

DoMouseDown( theEvent )
 EventRecord*theEvent;
{
 WindowPtrwhichWindow;
 INTEGERthePart;

 thePart = FindWindow( &theEvent->where, &whichWindow );

 switch ( thePart )
 {
 case inDesk:
 break;
 
 case inMenuBar:
 DoMenuClick( theEvent );
 break;
 
 case inSysWindow:
 SystemClick( theEvent, whichWindow );
 break;
 
 case inContent:
 DoContent( theEvent, whichWindow );
 break;
 
 case inDrag:
 DoDrag( theEvent, whichWindow );
 break;
 
 case inGrow:
 DoGrow( theEvent, whichWindow );
 break;
 
 case inGoAway:
 DoGoAway( theEvent, whichWindow );
 break;
 }
}

DoMenuClick( theEvent )
 EventRecord*theEvent;
{
 LONGINTmenuChoice;

 menuChoice = MenuSelect( &theEvent->where );
 DoMenuChoice( menuChoice );
}

DoMenuChoice( menuChoice )
 LONGINTmenuChoice;
{
 INTEGERtheMenu,
 theItem;

 if ( menuChoice )
 {
 theMenu = HiWord( menuChoice );
 theItem = LoWord( menuChoice );

 switch (theMenu)
 {
 case AppleID:
 DoAppleChoice( theItem );
 break;
 
 case FileID:
 DoFileChoice( theItem );
 break;
 
 case EditID:
 DoEditChoice( theItem );
 break;
 
 case WindowsID:
 DoWindowsChoice( WindowsMenu, theItem );
 break;
 
 case ToolsID:
 DoWindowsChoice( ToolsMenu, theItem );
 break;
 }
 HiliteMenu( 0 );
 }
}

DoAppleChoice( theItem )
 INTEGERtheItem;
{
 char   accName[255];
 INTEGERaccNumber;

 switch ( theItem )
 {
 case AboutItem:
 DoAbout();
 break;
 
 default:
 EnableItem( FileMenu, CloseItem );

 EnableItem( EditMenu, UndoItem  );
 EnableItem( EditMenu, CutItem   );
 EnableItem( EditMenu, CopyItem  );
 EnableItem( EditMenu, PasteItem );
 EnableItem( EditMenu, ClearItem );
 
 GetItem( AppleMenu, theItem, accName );
 accNumber = OpenDeskAcc( accName );
 break;
 }
}

DoAbout()
{
 EventRecordtheEvent;
 DialogPtraboutDialog;
 INTEGERtheItem;
 void   SetBold();
 
 aboutDialog = GetNewDialog( AboutID, nil, inFront );
 SetBold( aboutDialog, aboutBold );
 TShowWindow( aboutDialog );

 if ( TGetNextEvent( activMask, &theEvent ))
 DoActivate( &theEvent );
 
 if ( TGetNextEvent( activMask, &theEvent ))
 DoActivate( &theEvent );
 
 ModalDialog( nilproc, &theItem );
 
 THideWindow ( aboutDialog );
 DisposDialog( aboutDialog );
}

DoFileChoice( theItem )
 INTEGERtheItem;
{
 switch (theItem)
 {
 case NewItem:
 DoNew();
 break;
 
 case NewToolItem:
 DoNewTool();
 break;
 
 case CloseItem:
 DoClose();
 break;
 
 case QuitItem:
 DoQuit();
 break;
 }
}

DoNew()
{
 WindowPtrtheWindow;
 WDHandle theData;
 char   title[ 255 ];

 theWindow = TGetNewWindow( userKind, windowID, nil, inFront );

 OffsetWindow( theWindow, wHOffset, wVOffset, &windowCount );
 TShowWindow ( theWindow );

 SetPort( theWindow );

 theData = ( WDHandle )NewHandle(( Size )sizeof( WindowData ));
 SetWRefCon( theWindow, ( LONGINT )theData );
 HLock( theData );

 ( *theData )->scBar = GetNewControl( scrollID, theWindow );

 HUnlock( theData );

 EnableItem( FileMenu, CloseItem );
 
 GetWTitle( theWindow, title );
 InsMenuItem( WindowsMenu, title, lastWindowsItem++ );
 CheckItem  ( WindowsMenu, lastWindowsItem, true );
}

DoNewTool()
{
 WindowPtrtoolWindow;
 char   title[ 255 ];

 toolWindow = TGetNewWindow( toolKind, toolID, nil, inFront );
 
 OffsetWindow( toolWindow, tHOffset, tVOffset, &toolCount );
 TShowWindow( toolWindow );
 
 SetPort( toolWindow );
 
 EnableItem( FileMenu, CloseItem );
 
 GetWTitle( toolWindow, title );
 InsMenuItem( ToolsMenu, title, lastToolsItem++ );
 CheckItem  ( ToolsMenu, lastToolsItem, true );
}

OffsetWindow( whichWindow, hOffset, vOffset, count )
 WindowPtrwhichWindow;
 INTEGERhOffset,
 vOffset,
 *count;
{
 INTEGERwindowWidth,
 windowHeight,
 hExtra,
 vExtra,
 hMax,
 vMax,
 windowLeft,
 windowTop;
 Rect   pRect;
 char   title [ 255 ],
 number[ 3 ];

 pRect = whichWindow->portRect;

 windowWidth   = pRect.right - pRect.left;
 windowHeight  = pRect.bottom - pRect.top;
 windowHeight += TitleBarHeight;

 hExtra = qd.screenBits.bounds.right  -   windowWidth;
 vExtra = qd.screenBits.bounds.bottom - ( windowHeight + MenuBarHeight 
);

 hMax = ( hExtra / hOffset ) + 1;
 vMax = ( vExtra / vOffset ) + 1;

 ++*count;

 windowLeft = ( *count % hMax ) * hOffset;
 windowTop  = ( *count % vMax ) * vOffset;
 windowTop  = windowTop + TitleBarHeight + MenuBarHeight;

 MoveWindow( whichWindow, windowLeft, windowTop, false );
 
 GetWTitle( whichWindow, title );
 NumToString(( LONGINT )*count, number );
 SetWTitle( whichWindow, strcat( title, number ));
}

DoClose()
{
 WindowPtrwhichWindow;
 INTEGERtheWKind;
 
 whichWindow = TFrontWindow( userKind );
 
 if ( whichWindow != nil )
 {
 theWKind = TGetWKind( whichWindow );
 switch( theWKind )
 {
 case userKind:
 CloseDocWindow( whichWindow );
 break;
 
 case systemKind:
 CloseSysWindow ( whichWindow );
 break;
 }
 }
 else
 {
 whichWindow = TFrontWindow( toolKind );
 
 if ( whichWindow != nil )
 CloseToolWindow( whichWindow );
 }
}

CloseDocWindow( whichWindow )
 WindowPtrwhichWindow;
{
 WDHandle theData;
 INTEGERItemOf();
 EventRecordtheEvent;

 lastWindowsItem--;
 DelMenuItem( WindowsMenu, ItemOf( whichWindow, WindowsMenu ));
 
 theData = ( WDHandle )GetWRefCon( whichWindow );
 
 THideWindow( whichWindow );

 if ( TGetNextEvent( activMask, &theEvent ))
 DoActivate( &theEvent );
 
 if ( TGetNextEvent( activMask, &theEvent ))
 DoActivate( &theEvent );

 if ( theData != nil )
 DisposHandle( theData );
 TDisposeWindow( whichWindow );
}

CloseToolWindow( whichWindow )
 WindowPtrwhichWindow;
{
 INTEGERItemOf();
 
 lastToolsItem--;
 DelMenuItem( ToolsMenu, ItemOf( whichWindow, ToolsMenu ));
 TDisposeWindow( whichWindow );
}

CloseSysWindow( whichWindow )
 WindowPtrwhichWindow;
{
 INTEGERaccNumber;

 accNumber = (( WindowPeek )whichWindow )->windowKind;
 CloseDeskAcc( accNumber );
}

DoQuit()
{
 Finished = true;
}

DoEditChoice( theItem )
 INTEGERtheItem;
{
 switch ( theItem )
 {
 case UndoItem:
 if ( !SystemEdit( undoCmd  ))
 DoUndo();
 break;
 
 case CutItem:
 if ( !SystemEdit( cutCmd   ))
 DoCut();
 break;
 
 case CopyItem:
 if ( !SystemEdit( copyCmd  ))
 DoCopy();
 break;
 
 case PasteItem:
 if ( !SystemEdit( pasteCmd ))
 DoPaste();
 break;
 
 case ClearItem:
 if ( !SystemEdit(clearCmd  ))
 DoClear();
 break;
 }
}

DoUndo()
{
 SysBeep(1);
}

DoCut()
{
 SysBeep(1);
}

DoCopy()
{
 SysBeep(1);
}

DoPaste()
{
 SysBeep(1);
}

DoClear()
{
 SysBeep(1);
}

DoWindowsChoice( theMenu, theItem )
 MenuHandle theMenu;
 INTEGERtheItem;
{
 INTEGERtheMark;
 WindowPtrwhichWindow,
 WindowOf();
 
 whichWindow = WindowOf( theMenu, theItem );
 GetItemMark( theMenu, theItem, &theMark );
 
 if ( theMark == noMark )
 {
 CheckItem( theMenu, theItem, true );
 TShowWindow( whichWindow );
 }
 else
 {
 CheckItem( theMenu, theItem, false );
 THideWindow( whichWindow );
 }
}

DoContent( theEvent, whichWindow )
 EventRecord*theEvent;
 WindowPtrwhichWindow;
{
 INTEGERtheWKind;

 theWKind = TGetWKind( whichWindow );

 if ( theWKind == userKind )
 {
 if ( whichWindow != TFrontWindow( userKind ))
 TSelectWindow( whichWindow );
 }
 else if ( theWKind == toolKind )
 {
 if ( whichWindow != TFrontWindow( toolKind ))
 TSelectWindow( whichWindow );
 
 /* Only if the window has no title bar */
 
 DoDrag( theEvent, whichWindow );
 }
}

DoDrag( theEvent, whichWindow)
 EventRecord*theEvent;
 WindowPtrwhichWindow;
{
 Rect   limitRect;

 SetRect( &limitRect, 0, MenuBarHeight, 
 qd.screenBits.bounds.right, 
 qd.screenBits.bounds.bottom );
 InsetRect( &limitRect, ScreenMargin, ScreenMargin );
 TDragWindow( whichWindow, theEvent, &limitRect );
}

DoGrow( theEvent, whichWindow )
 EventRecord*theEvent;
 WindowPtrwhichWindow;
{
 Rect   sizeRect;
 LONGINTnewSize;
 INTEGERnewWidth,
 newHeight;
 GrafPtrsavePort;

 if ( whichWindow != TFrontWindow( userKind ))
 TSelectWindow( whichWindow );
 else
 {
 SetRect( &sizeRect, MinWidth, MinHeight,
 qd.screenBits.bounds.right,
 qd.screenBits.bounds.bottom - MenuBarHeight );
 newSize = GrowWindow( whichWindow, &theEvent->where, &sizeRect );

 if ( newSize )
 {
 GetPort( &savePort );
 SetPort( whichWindow );
 EraseRect( &whichWindow->portRect );
 newWidth  = LoWord( newSize );
 newHeight = HiWord( newSize );
 SizeWindow( whichWindow, newWidth, newHeight, true );
 InvalRect( &whichWindow->portRect );
 FixScrollBar( whichWindow );
 SetPort( savePort );
 }
 }
}

FixScrollBar( whichWindow )
 WindowPtrwhichWindow;
{
 WDHandle theData;
 ControlHandle theScrollBar;
 Rect   pRect;

 theData = ( WDHandle )GetWRefCon( whichWindow );
 if ( theData != nil )
 {
 theScrollBar = ( *theData )->scBar;
 HideControl( theScrollBar );
 pRect = whichWindow->portRect;
 
 MoveControl( theScrollBar, pRect.right - ( SBarWidth - 1 ), -1 );
 SizeControl( theScrollBar, SBarWidth, ( pRect.bottom + 1 )
 - ( pRect.top - 1 ) - ( SBarWidth - 1 ));
 ShowControl( theScrollBar );
 ValidRect( &( *theScrollBar )->contrlRect );
 }
}

DoGoAway( theEvent, whichWindow )
 EventRecord*theEvent;
 WindowPtrwhichWindow;
{
 INTEGERtheWKind;
 
 theWKind = TGetWKind( whichWindow );
 
 if ( theWKind == userKind )
 {
 if ( whichWindow != TFrontWindow( userKind ))
 TSelectWindow( whichWindow );
 else if ( TrackGoAway( whichWindow, &theEvent->where ))
 CloseDocWindow( whichWindow );
 }
 else if ( theWKind == toolKind )
 {
 if ( TrackGoAway( whichWindow, &theEvent->where ))
 {
 lastToolsItem--;
 DelMenuItem( ToolsMenu, ItemOf( whichWindow, ToolsMenu ));
 TDisposeWindow( whichWindow );
 }
 }
}

DoKeyStroke( theEvent )
 EventRecord*theEvent;
{
 INTEGER  ch;
 LONGINT  menuChoice;

 ch = ( INTEGER )( theEvent->message & charCodeMask );

 if ( theEvent->modifiers & cmdKey )
 {
 if ( theEvent->what != autoKey )
 {
 menuChoice = MenuKey( ch );
 DoMenuChoice( menuChoice );
 }
 }
}

DoUpdate( theEvent )
 EventRecord*theEvent;
{
 GrafPtrsavePort;
 WindowPtrwhichWindow;
 WDHandle theData;
 INTEGERtheWKind;

 GetPort( &savePort );

 whichWindow = ( WindowPtr )theEvent->message;
 theWKind = TGetWKind( whichWindow );
 SetPort( whichWindow );
 BeginUpdate( whichWindow );

 if ( theWKind == userKind )
 {
 EraseRect( &whichWindow->portRect );
 
 if ( GetWRefCon( whichWindow ) != nil )
 {
 DrawGrowIcon( whichWindow );
 DrawControls( whichWindow );
 }
 }
 else if ( theWKind == toolKind )
 {
 EraseRect( &whichWindow->portRect );
 }
 EndUpdate( whichWindow );
 SetPort( savePort );
}

DoActivate( theEvent )
 EventRecord*theEvent;
{
 WindowPtrwhichWindow,
 theFrontWindow;
 WDHandle theData;
 INTEGERtheWKind,
 frontWKind;
 whichWindow = ( WindowPtr )theEvent->message;
 SetPort( whichWindow );
 theWKind = TGetWKind( whichWindow );
 if ( theWKind == userKind )
 {
 theData = (WDHandle )GetWRefCon( whichWindow );
 if ( theData != nil )
 {
 HLock( theData );
 if ( theEvent->modifiers & activeFlag )
 {
 ShowControl(( *theData )->scBar );
 
 DisableItem( EditMenu, UndoItem  );
 DisableItem( EditMenu, CutItem   );
 DisableItem( EditMenu, CopyItem  );
 DisableItem( EditMenu, PasteItem );
 DisableItem( EditMenu, ClearItem );
 }
 else
 {
 HideControl(( *theData )->scBar );
 
 theFrontWindow = FrontWindow();
 frontWKind     = TGetWKind( theFrontWindow );
 if ( frontWKind == systemKind )
 {
 EnableItem( EditMenu, UndoItem  );
 EnableItem( EditMenu, CutItem   );
 EnableItem( EditMenu, CopyItem  );
 EnableItem( EditMenu, PasteItem );
 EnableItem( EditMenu, ClearItem );
 }
 }
 HUnlock( theData );
 DrawGrowIcon( whichWindow );
 }
 }
}

INTEGER
ItemOf( whichWindow, whichMenu )
 WindowPtrwhichWindow;
 MenuHandle whichMenu;
{
 char   theTitle[ 255 ],
 theMenu [ 255 ];
 Booleandone;
 INTEGERtheItem;
 
 GetWTitle( whichWindow, theTitle );
 
 for ( done = false,
 theItem = 1;
   done == false;
   theItem++ )
 {
 GetItem( whichMenu, theItem, theMenu );
 if ( strcmp( theTitle, theMenu ) == 0 )
 {
 done = true;
 theItem--;
 }
 }
 
 return( theItem );
}

WindowPtr
WindowOf( whichMenu, whichItem )
 MenuHandle whichMenu;
 INTEGERwhichItem;
{
 char   theTitle[ 255 ],
 theMenu [ 255 ];
 Booleandone;
 INTEGERwantedKind,
 theWKind;
 WindowPeek loopWindow,
 wantedWindow;
 
 GetItem( whichMenu, whichItem, theMenu );
 if ( whichMenu == WindowsMenu )
 wantedKind = userKind;
 else
 wantedKind = toolKind;
 
 for ( done = false,
 loopWindow   =
 wantedWindow = windowList;
   done == false  &&  loopWindow != nil;
   loopWindow = loopWindow->nextWindow )
 {
 theWKind = TGetWKind( loopWindow );
 if ( theWKind == wantedKind )
 {
 GetWTitle( loopWindow, theTitle );
 if ( strcmp( theTitle, theMenu ) == 0 )
 {
 wantedWindow = loopWindow;
 done = true;
 }
 }
 }
 
 return(( WindowPtr )wantedWindow );
}

void
SetBold( theDialog, bolditem )
 DialogPtrtheDialog;
 short  bolditem;
{
 INTEGERitemType;
 Handle itemHandle;
 Rect   itemRect;
 pascal voidOutlineButton();

 /* Get the OK button’s display rectangle, enlarge the
    rectangle, and set up the useritem with this rectangle
    and a pointer to the OutlineButton function. */
    
 GetDItem( theDialog, okButton, &itemType, &itemHandle, &itemRect );
 InsetRect( &itemRect, -3, -3 );
 SetDItem( theDialog, bolditem, userItem,
   ( Handle )OutlineButton, &itemRect );
}

pascal void
OutlineButton( theDialog, theItem )
 DialogPtrtheDialog;
 INTEGERtheItem;
{
 INTEGERitemType;
 Handle itemHandle;
 Rect   itemRect;
 PenState savePen;
 
 /* Get the bounding rectangle of the OK button item
    and boldly outline it. */
 
 GetDItem( theDialog, okButton, &itemType, &itemHandle, &itemRect );
 GetPenState( &savePen );
 PenSize( 3,3 );
 InsetRect( &itemRect, -4, -4 );
 FrameRoundRect( &itemRect, 16, 16 );
 SetPenState( &savePen );
}
{5}
Listing:  TWindowTester.R

/*
 * TWindowTester.r
 *
 * Resources for a simple program to demonstrate usage of tool  windows
 * by using routines from the TWindow Manager. Tool windows are windows
 * that always float on top, typically for palettes and tools.
 *
 * Written in MPW C 2.0
 *
 * Thomas Fruin 1988
 *
 * fruin@hlerul5.BITNET   University of Leiden
 * thomas@uvabick.UUCP    University of Amsterdam
 * dibs@well.UUCP
 * hol0066.AppleLink
 * 2:508/15.FidoNetThe Netherlands
 *
 * TWindowTester is based on MiniEdit - Mini text editor,converted from
 * the listing in Macintosh Revealed, vol II. As little as possible was
 * modified in the original program, although most of the functionality
 * is no longer there.
 */

resource ‘MENU’ (1) {
 1,
 textMenuProc,
 0x7FFFFFFD,
 enabled,
 apple,
 { /* array: 2 elements */
 /* [1] */
 “About TWindowTester ...”, noIcon, “”, “”, plain,
 /* [2] */
 “-”, noIcon, “”, “”, plain
 }
};

resource ‘MENU’ (2) {
 2,
 textMenuProc,
 0x7FFFFFF3,
 enabled,
 “File”,
 { /* array: 5 elements */
 /* [1] */
 “New”, noIcon, “N”, “”, plain,
 /* [2] */
 “New Tool”, noIcon, “T”, “”, plain,
 /* [3] */
 “Close”, noIcon, “W”, “”, plain,
 /* [4] */
 “-”, noIcon, “”, “”, plain,
 /* [5] */
 “Quit”, noIcon, “Q”, “”, plain
 }
};

resource ‘MENU’ (3) {
 3,
 textMenuProc,
 0x7FFFFF80,
 enabled,
 “Edit”,
 { /* array: 7 elements */
 /* [1] */
 “Undo”, noIcon, “Z”, “”, plain,
 /* [2] */
 “-”, noIcon, “”, “”, plain,
 /* [3] */
 “Cut”, noIcon, “X”, “”, plain,
 /* [4] */
 “Copy”, noIcon, “C”, “”, plain,
 /* [5] */
 “Paste”, noIcon, “V”, “”, plain,
 /* [6] */
 “-”, noIcon, “”, “”, plain,
 /* [7] */
 “Clear”, noIcon, “B”, “”, plain
 }
};

resource ‘MENU’ (4) {
 4,
 textMenuProc,
 allEnabled,
 enabled,
 “Windows”,
 { /* array: 0 elements */
 }
};

resource ‘MENU’ (5) {
 5,
 textMenuProc,
 allEnabled,
 enabled,
 “Tools”,
 { /* array: 0 elements */
 }
};

resource ‘WIND’ (1000) {
 {50, 40, 300, 450},
 documentProc,
 invisible,
 goAway,
 0x0,
 “Document “
};

resource ‘WIND’ (1001) {
 {58, 533, 159, 606},
 altDBoxProc,
 invisible,
 noGoAway,
 0x0,
 “Tool “
};

resource ‘CNTL’ (1000) {
 {-1, 395, 236, 411},
 0,
 -1,
 0,
 0,
 scrollBarProc,
 0,
 “vertical scroll bar”
};

resource ‘PICT’ (128) {
 2616,
 {0, 5, 217, 188},
 $”1101 A000 82A0 3039 A000 8E01 000A 0000"
 $”0000 02D0 0240 9800 1800 0000 0000 8600"
 $”C000 0000 0500 8600 BC00 0000 0500 8600"
 $”BC00 0106 FC00 0030 EF00 06FC 0000 78EF”
 $”0006 FC00 0078 EF00 06FC 0000 32EF 0006"
 $”FC00 0007 EF00 06FC 0000 0EEF 0007 FC00"
 $”0104 C0F0 000A 0101 80FE 0001 01E0 F000"
 $”0A01 03C0 FE00 0101 E0F0 0009 0107 E0FD”
 $”0000 C8F0 0009 0107 E0FD 0000 1CF0 0009"
 $”0107 E0FD 0000 38F0 0009 0103 C4FD 0000"
 $”13F0 000A 0101 8EFD 0001 0780 F100 0A01"
 $”001E FD00 0107 80F1 000B 0700 3C00 0060"
 $”0003 20F1 000B 0700 38C0 00F0 0000 70F1"
 $”000B 0700 11E0 00F0 0000 E8F1 000B 0700"
 $”03F0 0060 0000 5CF1 000B 0700 03F0 000F”
 $”F000 38F1 000B 0700 03F0 003F FC00 13F1"
 $”000C 0800 01E0 00FF FF00 0780 F200 0C08"
 $”0000 C301 FFFF 8007 80F2 000B 0700 0007"
 $”83FF FFC0 03F1 000A 0600 000F C7FF FFE0"
 $”F000 0A06 0000 0FCF FFFF F0F0 000A 0600"
 $”000F CFFE FFF0 F000 0A06 0000 079F FE7F”
 $”F8F0 000A 0600 0003 1FFE 3FF8 F000 09FE”
 $”0003 3FFE 1FFC F000 09FE 0003 3FFE 3FFC”
 $”F000 09FE 0003 3FFC 7FFC F000 09FE 0003"
 $”3FFC 7BFC F000 09FE 0003 3FF8 F9FC F000"
 $”09FE 0003 3FF8 F8FC F000 09FE 0003 3FF9"
 $”E3FC F000 09FE 0003 3FF1 0FFC F000 09FE”
 $”0003 3FF0 7FFC F000 0AFE 0004 3FE1 FFFC”
 $”C0F1 000A FE00 041F F7FF F9E0 F100 0AFE”
 $”0004 1FFF FFF9 E0F1 000A FE00 040F FFFF”
 $”F0C8 F100 0AFE 0004 0FFF FFF0 1CF1 000A”
 $”FE00 0407 FFFF E038 F100 0AFE 0004 03FF”
 $”FFC0 13F1 000B FE00 0501 FFFF 8007 80F2"
 $”000A FD00 04FF FF00 0780 F200 09FD 0003"
 $”3FFC 0003 F100 07FD 0001 0FF0 EF00 02E9"
 $”0009 FB00 0330 000F F0F3 0009 FB00 0378"
 $”003F FCF3 0009 FB00 03F0 00FF FFF3 000B”
 $”FC00 0501 E201 FFFF 80F4 000B FC00 0501"
 $”C703 FFFF C0F4 000A FB00 048F 07FF FFE0"
 $”F400 0AFB 0004 1E0F FFFF F0F4 000A FB00"
 $”043C 0FFF FFF0 F400 0AFB 0004 181F FFFF”
 $”F8F4 0009 FA00 031F FFFF F8F4 0009 FA00"
 $”033F FFEF FCF4 0009 FA00 033F FF87 FCF4"
 $”0009 FA00 033F FC0F FCF4 000B FC00 0530"
 $”003F F08F FCF4 000B FC00 0578 003E 038F”
 $”FCF4 000B FC00 0578 003F 391F FCF4 000B”
 $”FC00 0532 003F BC1F FCF4 000B FC00 0507"
 $”003F FE1F FCF4 000B FC00 050E 003F FF3F”
 $”FCF4 000B FC00 0504 C03F FE3F FCF4 000C”
 $”FC00 0601 E01F F83F F8C0 F500 0CFC 0006"
 $”01E0 1FFC 7FF9 E0F5 000B FB00 05D8 0FFE”
 $”7FF1 E0F5 000B FB00 053C 0FFF 7FF0 C8F5"
 $”000B FB00 053C 07FF FFE0 1CF5 000B FB00"
 $”051B 03FF FFC0 3AF5 000B FB00 0507 81FF”
 $”FF80 17F5 000B FB00 0507 80FF FF00 0EF5"
 $”000C FB00 0603 603F FC00 04C0 F600 0BFA”
 $”0005 F00F F000 01E0 F600 0BFA 0000 F0FE”
 $”0001 01E0 F600 0CFA 0006 6C00 0020 00CF”
 $”F0F7 000C FA00 061E 0000 7000 3FFC F700"
 $”0CFA 0006 1E00 00F0 00FF FFF7 000D FA00"
 $”070C 8001 E001 FFFF 80F8 000D FA00 0701"
 $”C001 C603 FFFF C0F8 000D FA00 0703 8000"
 $”8F07 FFFF E0F8 000D FA00 0701 0000 1F8F”
 $”FDFF F0F8 000A F700 041F 8FFC FFF0 F800"
 $”0AF7 0004 1F9F F87F F8F8 000A F700 040F”
 $”1FF0 7FF8 F800 0DF7 0007 063F E4FF FC00"
 $”0030 FB00 0CF6 0006 3FC8 FFFC 0000 78FB”
 $”000C F600 063F 99FF FC00 0078 FB00 0CF6"
 $”0006 3F39 FBFC 0000 32FB 000C F600 063C”
 $”71F1 FC00 0007 FB00 0CF6 0006 3E73 C0FC”
 $”0000 0EFB 000D F600 073F 7330 FC00 0004"
 $”C0FC 000D F600 073F F0E3 FC00 0001 E0FC”
 $”000D F600 073F E3C7 FC00 0001 E0FC 000F”
 $”F800 0530 003F EF8F FCFE 0000 C8FC 000F”
 $”F800 0578 001F FF1F F8FE 0000 1CFC 000F”
 $”F800 0978 001F F83F F840 0000 38FC 000F”
 $”F800 0932 000F FC7F F0E0 0000 13FC 0010"
 $”F800 0A07 000F FE7F F1D0 0000 0780 FD00"
 $”10F8 000A 0E00 07FF 7FE0 B800 0007 80FD”
 $”0010 F800 0A04 C003 FFFF C074 0000 0360"
 $”FD00 10F8 0006 01E0 01FF FF80 2EFE 0000"
 $”F0FD 0010 F800 0601 E000 FFFF 001C FE00"
 $”00F0 FD00 0FF7 0009 D800 3FFC 0009 8000"
 $”006C FD00 0FF7 0009 3C00 0FF0 0003 C000"
 $”001E FD00 0EF7 0000 3CFD 0004 03C0 0000"
 $”1EFD 0010 F700 0A19 0000 0180 0180 0000"
 $”0D80 FE00 10F7 000A 0380 0003 C000 1FE0"
 $”0003 C0FE 0010 F700 0A07 4000 07E0 007F”
 $”F800 03C0 FE00 10F7 000A 02E0 0007 E001"
 $”FFFE 0001 80FE 000D F700 0701 C000 07E0"
 $”03FF FFFB 000D F600 0798 0003 C407 FFFF”
 $”80FC 000F F600 093C 0001 8E0F FFFF C000"
 $”01FE 0010 F600 0A3C 0000 1E1F FFFF E000"
 $”0380 FF00 10F6 000A 1900 003C 1FFF FFE0"
 $”0006 C0FF 0010 F600 0A03 8000 383F FFFF”
 $”F000 0C60 FF00 10F6 000A 0700 0010 3FFE”
 $”FFF0 0018 30FF 0010 F600 0002 FE00 067F”
 $”FE7F F800 3018 FF00 0CF2 0006 7FFC 3FF8"
 $”0060 0CFF 000C F200 067F F81F F800 C006"
 $”FF00 0CF2 0006 7FF0 6FF8 00C0 03FF 000C”
 $”F200 087F E0EF F800 E001 8000 0CF2 0008"
 $”7FC1 E7F8 00B0 00C0 000C F200 087F 81C7"
 $”F800 9800 6000 0CF2 0008 7E04 07F8 018C”
 $”0030 000C F200 087F 0E0F F803 C600 1800"
 $”0CF2 0008 7F9F 9FF8 0663 000C 000C F200"
 $”083F DFFF F0CC 3180 0600 9800 1800 8600"
 $”0000 D900 C000 8600 0500 D900 BC00 8600"
 $”0500 D900 BC00 010C F200 083F FFFF F1F8"
 $”1980 0300 0CF2 0008 1FFF FFE3 300F 0001"
 $”800C F200 081F FFFF E618 0600 00C0 0CF2"
 $”0008 0FFF FFCC 0C0C 0000 600C F200 0807"
 $”FFFF 9806 1800 0030 0CF2 0008 03FF FF30"
 $”0330 0000 700C F200 0801 FFFE 7001 E000"
 $”00D0 0BF1 0007 7FF8 5800 C000 0190 0BF1"
 $”0002 1FE0 4CFE 0001 0330 09EF 0000 66FE”
 $”0001 0660 0AF0 0001 01B3 FE00 010C C00A”
 $”F000 0603 D980 0000 1980 0AF0 0006 07EC”
 $”C000 0033 000A F000 0607 E660 0000 6600"
 $”0AF0 0006 07E3 3000 00CC 000A F000 0603"
 $”C598 0001 9800 0AF0 0006 018E CC00 0330"
 $”0009 EF00 051E E600 0670 0009 EF00 053D”
 $”B300 0CD8 0009 EF00 053B F980 19CC 0009"
 $”EF00 0517 FCC0 33E6 0009 EF00 050F FE60"
 $”67F3 0009 EF00 051B F330 CFF9 8009 EF00"
 $”0533 F199 9FFC C009 EF00 0567 E0CF 3FFE”
 $”6009 EF00 05CC C7E6 7FFF 300A F000 0601"
 $”980F F0FF FF30 0AF0 0006 03CC 0FF9 FFFE”
 $”700A F000 0606 660F FFFF FCD0 0AF0 0006"
 $”0433 0FFF FFF9 900A F000 0604 1987 FFFF”
 $”F310 0AF0 0006 040C C3FF FFE6 100A F000"
 $”0604 0663 FFFF CCD0 0AF0 0006 0403 37FF”
 $”FF99 D00A F000 0604 019F FFFF 33D0 0AF0"
 $”0006 0400 CFFF FE67 D00A F000 0604 0067"
 $”FFFC CF50 0AF0 0006 0400 33FF F99A 500A”
 $”F000 0604 0019 FFF3 3250 0AF0 0006 0400"
 $”0CFF E662 500A F000 0604 0006 7FCC C250"
 $”0AF0 0006 0400 033F 9982 500A F000 0604"
 $”0001 9F33 0250 0AF0 0006 0400 00CE 6602"
 $”500A F000 0604 0000 64CC 0250 0AF0 0006"
 $”0400 0031 9806 500A F000 0604 0000 1B30"
 $”0CD0 0AF0 0006 0400 000E 6019 900A F000"
 $”0604 0000 04C0 3310 0AF0 0000 04FE 0002"
 $”8066 500A F000 0004 FE00 0280 CCD0 0AF0"
 $”0000 04FE 0002 8199 900A F000 0004 FE00"
 $”0283 3330 0AF0 0000 04FE 0002 8666 600A”
 $”F000 0004 FE00 028C CCC0 0AF0 0000 04FE”
 $”0002 9999 C00A F000 0004 FE00 02B3 3340"
 $”0AF0 0000 04FE 0002 E626 400A F000 0004"
 $”FE00 02CC 0C40 0AF0 0000 04FE 0002 9818"
 $”C00A F000 0004 FE00 02B0 3180 0AF0 0000"
 $”06FE 0002 E063 000A F000 0003 FE00 02C0"
 $”C600 0AF0 0006 0180 0000 818C 0009 EF00"
 $”05C0 0000 0318 0009 EF00 0560 0000 0630"
 $”0009 EF00 0530 0000 0C60 0009 EF00 0518"
 $”0000 18C0 0009 EF00 050C 0000 3180 0009"
 $”EF00 0306 0000 63FF 0009 EF00 0303 0020"
 $”C6FF 0009 EF00 0301 8031 8CFF 0008 EE00"
 $”02C0 1B18 FF00 08EE 0002 600E 30FF 0008"
 $”EE00 0230 0460 FF00 08EE 0002 1800 C0FF”
 $”0008 EE00 020C 0180 FF00 07EE 0001 0603"
 $”FE00 07EE 0001 0306 FE00 07EE 0001 018C”
 $”FE00 06ED 0000 D8FE 0006 ED00 0070 FE00"
 $”06ED 0000 20FE 00A0 008F A000 83FF”
};

resource ‘DLOG’ (128) {
 {62, 77, 312, 437},
 dBoxProc,
 invisible,
 noGoAway,
 0x0,
 128,
 “About”
};

resource ‘DITL’ (128) {
 { /* array DITLarray: 7 elements */
 /* [1] */
 {27, 270, 48, 341},
 Button {
 enabled,
 “OK”
 },
 /* [2] */
 {56, 66, 73, 174},
 StaticText {
 disabled,
 “TWindowTester”
 },
 /* [3] */
 {189, 19, 226, 266},
 StaticText {
 disabled,
 “Originally written for VAMP, “
 “the Dutch Mac programmers group.”
 },
 /* [4] */
 {99, 60, 116, 172},
 StaticText {
 disabled,
 “by Thomas Fruin”
 },
 /* [5] */
 {117, 34, 135, 196},
 StaticText {
 disabled,
 “Leiden, The Netherlands”
 },
 /* [6] */
 {148, 68, 164, 169},
 StaticText {
 disabled,
 “20 April 1988”
 },
 /* [7] */
 {9, 158, 226, 341},
 Picture {
 disabled,
 128
 },
 /* [8] */
 {27, 270, 48, 341},
 UserItem {
 disabled
 }
 }
};

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

The Legend of Heroes: Trails of Cold Ste...
I adore game series that have connecting lore and stories, which of course means the Legend of Heroes is very dear to me, Trails lore has been building for two decades. Excitedly, the next stage is upon us as Userjoy has announced the upcoming... | Read more »
Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »

Price Scanner via MacPrices.net

Apple is offering significant discounts on 16...
Apple has a full line of 16″ M3 Pro and M3 Max MacBook Pros available, Certified Refurbished, starting at $2119 and ranging up to $600 off MSRP. Each model features a new outer case, shipping is free... Read more
Apple HomePods on sale for $30-$50 off MSRP t...
Best Buy is offering a $30-$50 discount on Apple HomePods this weekend on their online store. The HomePod mini is on sale for $69.99, $30 off MSRP, while Best Buy has the full-size HomePod on sale... Read more
Limited-time sale: 13-inch M3 MacBook Airs fo...
Amazon has the base 13″ M3 MacBook Air (8GB/256GB) in stock and on sale for a limited time for $989 shipped. That’s $110 off MSRP, and it’s the lowest price we’ve seen so far for an M3-powered... Read more
13-inch M2 MacBook Airs in stock today at App...
Apple has 13″ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Apple’s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
We’ve updated our iPhone Price Tracker with the latest carrier deals on Apple’s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more

Jobs Board

Operating Room Assistant - *Apple* Hill Sur...
Operating Room Assistant - Apple Hill Surgical Center - Day Location: WellSpan Health, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Read more
Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
…relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, 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
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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.