Corners
Volume Number: 5
Issue Number: 4
Column Tag: Advanced Mac'ing
Re-sizing with Window Corners

By Clifford Story, Goleta, CA
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
I suppose you’ve probably heard of the new Open Look interface for Unix. (Not so
new anymore, actually!) The thing looks pretty familiar -- windows, scroll bars,
menus. Everything has some kind of a twist on it, though. The menus are all pop-ups;
the scroll bars include the up and down arrows as part of the indicator; and the
windows can be re-sized from any corner, not just the lower right corner.
That’s an interesting idea, re-sizing from any corner. I wonder if you could do
that on the Mac... I held a long argument with myself on that topic, and the affirmative
finally won. You can do it on the Mac; here’s one way.
Before I get started, let me thank Tom Leonard, who encouraged me, and Don
Melton and Mike Ritter, whose article in the April MacTutor inspired me.
THE WINDOW
The first half of the problem is to draw the window with appropriate grow
brackets in the corners, and to have it return the “wInGrow” message when the user
clicks in one of them. This is done in a window definition procedure, or WDEF.
WDEF’s have been pretty well covered in MacTutor; I refer you to the Window
Manager chapter of Inside Macintosh, and to Melton and Ritter’s WDEF discussed in
their April article and published in May.
By the way, an earlier version used WDEF 0 to do most of the work. Apple,
however, has declared its intention to remove WDEF 0 from the System file (it hasn’t
done it yet). So I figured I had better do it myself. One thing led to another, and this
program now supports zoom windows on the old 64K ROM.
THE PATCH
If all I did was insert the WDEF, I’d get a nice looking window with some funny
behavior (try it! nothing bad will happen). I need to make the Window Manager
respond appropriately to mouse downs in a grow bracket.
So taking the hint from Melton and Ritter, I replaced the GrowWindow and
SizeWindow routines. And while I was at it, I made my new “sizethe window” routine a
replacement for ZoomWindow as well, and added a replacement for TrackBox, so I could
zoom windows as well as grow them.
Figure 1. Growing the window
BLOW-BY-BLOW
The program is contained in the file “Corners.p”, the replacement routines in
the unit “Patches.p”, and the WDEF in the file “WDEF.p” (Okay, so I lack
imagination!). There’s another unit “Common.p” that at one time was substantial but
now has only some minor type and constant declarations. The resource source file is
“Corners.r”.
When the user clicks in one of the corner brackets, the program calls
FindWindow, which in turn calls the WDEF. The WDEF returns wInGrow if the click
was close to any of the corners. Simple so far. FindWindow tells the program the click
was in the grow box, and the program calls the Patches routine “growthe window” in
place of the ROM’s GrowWindow.
Growthewindow has a lot of work to do. It must track the mouse, and draw a gray
outline of the re-sized window as it does so. When the mouse is released, it must
return not just the new dimensions of the window but the new location as well.
Growthewindow doesn’t actually draw the gray outline; that is done by the WDEF.
But it must tell the WDEF the rectangle to use when drawing. The basic method of
computing this rectangle is (1) find the fixed corner of the window (the one opposite
the corner being dragged), which I call the “pivot”; (2) track the mouse in a loop,
each time getting the current mouse position; (3) use the wonderful Toolbox routine
Pt2Rect to convert the mouse position and the pivot into a rectangle; and (4) call the
WDEF to draw the gray outline from that rectangle.
That sounds simple but there are a lot of small things that cloud the picture. For
example, the old gray outline must be erased before the new is drawn. And the mouse
will rarely start out at the corner of the portrect, so a correction must be applied.
And then there are various constraints on the rectangle, e.g., those imposed by the
SizeRect parameter. Also, the rectangle must be in global coordinates.
When all this is done, and the user releases the mouse button, the routine must
return the window’s new size and location. What could be easier than just returning
the last rectangle?
Now we’re back to the Corners program. It has the new portrect, in global
coordinates, as returned by growthe window. It calls another Patches routine,
“sizethe window”, which makes the actual changes in the window.
Sizethewindow does more than just re-size the window. Since the top left corner
may have moved, the window must be moved as well. My first version of this routine
called MoveWindow and SizeWindow but this resulted in two updates. Nesting the calls
within ShowHide calls solved that but... I wanted something that would look just as
smooth as the normal grow. ZoomWindow worked but limited the program to machines
with the new ROM. Finally I discovered the QD routine MovePortTo, which does not
update the screen! It is amazing what you can find, overlooked for years, in the QD
chapter!
When I made that discovery, I realized that my sizethewindow routine could
easily pinch-hit for ZoomWindow, giving me zoom windows on the old ROM. So for our
next adventure, we will ask the user to click on the zoom box...
BLOWS AGAINST THE ZOOM BOX
Once again, the program calls FindWindow, which calls the WDEF, which returns
wInZoomIn (I might note here that the WDEF could return anything I want, since this
is all done privately). The program than calls the Patches routine “trackthebox”.
Trackthebox is kinda rough; I think it could be slimmed down but why bother?
The time it takes is entirely determined by the user. Anyway, it goes into a loop,
getting the mouse location, calling the WDEF to find out where that is, and calling the
WDEF to highlight the zoom box if the mouse had just moved either in or out. When the
button is released, the routine returns true if the mouse is still in the zoom box, false
otherwise.
Now the program gets busy. First, note that one field of the record whose handle
is stored in the refcon field of the window record is called “zoomrect”. Suggestive,
huh? It gets the portrect of the window and compares it with a full screen portrect
(this is done in global coordinates, of course). If the window is not at full screen, then
its current portrect is saved in the zoomrect field, and sizethewindow zooms it to full
screen. If it is at full screen, then the zoomrect is used to size it back down.
PUTTING IT TOGETHER
To add four-cornered windows to your program, add the Patches unit to your uses
clause and the WDEF to the resource file. Then change the type of your windows from
zoomDocProc or whatever to 800. Finally, change calls to GrowWindow and
SizeWindow to “growthe window” and “sizethe window” (this will require more than
just a name change but not much more).
If you want to support zooming, you’ll have to hack a little further. Let the
description above be your guide.
Listing: Common.p
(************************************************************)
unit Common;
(************************************************************)
interface
(*********************************************************
key codes:
*************************************************************)
const
enterkey = 3;
backspace = 8;
tabkey = 9;
returnkey = 13;
clearkey = 27;
leftarrow = 28;
rightarrow = 29;
uparrow = 30;
downarrow = 31;
periodkey = 46;
(*************************************************************
Dialog items:
*************************************************************)
themask = 3;
(************************************************************
Low-memory globals:
*************************************************************)
applscratch = $A78;
bootdrive = $210;
curappname = $910;
curdirstore = $398;
currenta5 = $904;
findername = $2E0;
fsfcblen = $3F6;
grayrgn = $9EE;
iaznotify = $33C;
mbarheight = $BAA;
menuflash = $A24;
resload = $A5E;
rom85 = $28E;
sfsavedisk = $214;
sysmap = $A58;
windowlist = $9D6;
(**********************************************************
Standard types:
*************************************************************)
logical = boolean;
long = longint;