#include <Types.h>
#include <Menus.h>
#include <Quickdraw.h>
#include <Windows.h>
struct ZoomData {
GDHandle screenWithLargestPartOfWindow;
unsigned long largestArea;
Rect windowBounds;
};
typedef struct ZoomData ZoomData, *ZoomDataPtr;
typedef void (*CalcIdealDocumentSizeProcPtr)(WindowPtr theWindow, Rect *idealContentSize);
enum {
kNudgeSlop = 4,
kIconSpace = 64
};
void ZoomTheWindow(WindowPeek theWindow, short zoomState,
CalcIdealDocumentSizeProcPtr calcRoutine);
pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice,
long userData);
short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint,
short idealOnScreenStartPoint, short idealOnScreenEndPoint,
short screenEdge1, short screenEdge2);
static RgnHandle GetWindowContentRegion(WindowPeek theWindow);
static RgnHandle GetWindowStructureRegion(WindowPeek theWindow);
static void GetWindowPortRect(WindowPeek theWindow, Rect *portRect);
static void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState);
static void GetWindowUserState(WindowPeek theWindow, Rect *userState);
void ZoomTheWindow(WindowPeek theWindow, short zoomState,
CalcIdealDocumentSizeProcPtr calcRoutine)
{
ZoomData zoomData;
Rect newStandardRect;
Rect scratchRect;
Rect screenRect;
Rect portRect;
Rect contentRegionBoundingBox;
Rect structureRegionBoundingBox;
Rect deviceLoopRect;
GrafPtr currentPort;
RgnHandle scratchRegion;
RgnHandle contentRegion;
RgnHandle structureRegion;
GDHandle mainDevice;
short horizontalAmountOffScreen;
short verticalAmountOffScreen;
short windowFrameTopSize;
short windowFrameLeftSize;
short windowFrameRightSize;
short windowFrameBottomSize;
GetPort(¤tPort);
SetPort((WindowPtr) theWindow);
contentRegion = GetWindowContentRegion(theWindow);
structureRegion = GetWindowStructureRegion(theWindow);
GetWindowPortRect(theWindow, &portRect);
contentRegionBoundingBox = (**contentRegion).rgnBBox;
structureRegionBoundingBox = (**structureRegion).rgnBBox;
// Determine the size of the window frame
windowFrameTopSize = contentRegionBoundingBox.top -
structureRegionBoundingBox.top;
windowFrameLeftSize = contentRegionBoundingBox.left -
structureRegionBoundingBox.left;
windowFrameRightSize = structureRegionBoundingBox.right -
contentRegionBoundingBox.right;
windowFrameBottomSize = structureRegionBoundingBox.bottom -
contentRegionBoundingBox.bottom;
// If the window is being zoomed into the standard state, calculate the best size
// to display the windowÕs information.
mainDevice = GetMainDevice();
if (zoomState == inZoomOut) {
zoomData.screenWithLargestPartOfWindow = mainDevice;
zoomData.largestArea = 0;
// Usually, we would use the content regionÕs bounding box to determine the monitor
// with largest portion of the windowÕs area. However, if the entire content region
// of the window is not on any screen, the structure region should be used instead.
scratchRegion = NewRgn();
SectRgn(GetGrayRgn(), contentRegion, scratchRegion);
if (EmptyRgn(scratchRegion))
zoomData.windowBounds = structureRegionBoundingBox;
else
zoomData.windowBounds = contentRegionBoundingBox;
// Use DeviceLoop to walk through all the active screens to find the one with the
// largest portion of the zoomed window
deviceLoopRect = zoomData.windowBounds;
GlobalToLocal((Point *)&deviceLoopRect);
GlobalToLocal((Point *)&deviceLoopRect.bottom);
RectRgn(scratchRegion, &deviceLoopRect);
DeviceLoop(scratchRegion, &CalcWindowAreaOnScreen, (long) &zoomData,
(DeviceLoopFlags) singleDevices);
DisposeRgn(scratchRegion);
screenRect = (**(zoomData.screenWithLargestPartOfWindow)).gdRect;
// If the monitor being zoomed to is the main monitor, change the top of the
// useable screen area to avoid putting the title bar underneath the menubar.
if (zoomData.screenWithLargestPartOfWindow == mainDevice)
screenRect.top += GetMBarHeight();
// Go figure out the perfect size for the window as if we had an infinitely large
// screen
(*calcRoutine)((WindowPtr) theWindow, &newStandardRect);
// Anchor the new rectangle at the windowÕs current top left corner
OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
OffsetRect(&newStandardRect, contentRegionBoundingBox.left,
contentRegionBoundingBox.top);
// newStandardRect is the ideal size for the content area. The window frame
// needs to be accounted for when we see if the window needs to be moved,
// or resized, so add in the dimensions of the window frame.
newStandardRect.top -= windowFrameTopSize;
newStandardRect.left -= windowFrameLeftSize;
newStandardRect.right += windowFrameRightSize;
newStandardRect.bottom += windowFrameBottomSize;
// If the new rectangle falls off the edge of the screen, nudge it so that itÕs just
// on the screen. CalculateOffsetAmount determines how much of the window is offscreen.
SectRect(&newStandardRect, &screenRect, &scratchRect);
if (!EqualRect(&newStandardRect, &scratchRect)) {
horizontalAmountOffScreen = CalculateOffsetAmount(newStandardRect.left,
newStandardRect.right,
scratchRect.left,
scratchRect.right,
screenRect.left,
screenRect.right);
verticalAmountOffScreen = CalculateOffsetAmount(newStandardRect.top,
newStandardRect.bottom,
scratchRect.top,
scratchRect.bottom,
screenRect.top,
screenRect.bottom);
OffsetRect(&newStandardRect, horizontalAmountOffScreen,
verticalAmountOffScreen);
}
// If weÕre still falling off the edge of the screen, that means that the perfect
// size is larger than the screen, so we need to shrink down the standard size
SectRect(&newStandardRect, &screenRect, &scratchRect);
if (!EqualRect(&newStandardRect, &scratchRect)) {
// First shrink the width of the window. If the window is wider than the screen
// it is zooming to, we can just pin the standard rectangle to the edges of the
// screen, leaving some slop. If the window is narrower than the screen, we know
// we just nudged it into position, so nothing needs to be done.
if ((newStandardRect.right - newStandardRect.left) >
(screenRect.right - screenRect.left)) {
newStandardRect.left = screenRect.left + kNudgeSlop;
newStandardRect.right = screenRect.right - kNudgeSlop;
if ((zoomData.screenWithLargestPartOfWindow == mainDevice) &&
(newStandardRect.right > (screenRect.right - kIconSpace)))
newStandardRect.right = screenRect.right - kIconSpace;
}
// Move in the top. Like the width of the window, nothing needs to be done unless
// the window is taller than the height of the screen.
if ((newStandardRect.bottom - newStandardRect.top) >
(screenRect.bottom - screenRect.top)) {
newStandardRect.top = screenRect.top + kNudgeSlop;
newStandardRect.bottom = screenRect.bottom - kNudgeSlop;
}
}
// WeÕve got the best possible window position. Remove the
// frame, slam it into the WStateData record and let ZoomWindow
// take care of the rest.
newStandardRect.top += windowFrameTopSize;
newStandardRect.left += windowFrameLeftSize;
newStandardRect.right -= windowFrameRightSize;
newStandardRect.bottom -= windowFrameBottomSize;
SetWindowStandardState(theWindow, &newStandardRect);
}
else
GetWindowUserState(theWindow, &newStandardRect);
// If the window is still anchored at the current location, then just resize it
if ((newStandardRect.left == contentRegionBoundingBox.left) &&
(newStandardRect.top == contentRegionBoundingBox.top)) {
OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
SizeWindow((WindowPtr) theWindow, newStandardRect.right, newStandardRect.bottom,
true);
}
else {
scratchRegion = NewRgn();
GetClip(scratchRegion);
ClipRect(&portRect);
EraseRect(&portRect);
ZoomWindow((WindowPtr) theWindow, zoomState, false);
SetClip(scratchRegion);
DisposeRgn(scratchRegion);
}
SetPort(currentPort);
}
pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, long userData)
{
#pragma unused (depth, deviceFlags)
ZoomDataPtr zoomData = (ZoomDataPtr) userData;
long windowAreaOnScreen;
Rect windowPortionOnScreen;
// Find the rectangle that encloses the intersection of the window and this screen.
SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect), &windowPortionOnScreen);
// Offset the rectangle so that itÕs right and bottom are also itÕs width and height.
OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left, -windowPortionOnScreen.top);
// Calculate the area of the portion of the window thatÕs on this screen.
windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) windowPortionOnScreen.bottom;
// If this is the largest portion of the window that has been encountered so far,
// remember this screen as the potential screen to zoom to.
if (windowAreaOnScreen > zoomData->largestArea) {
zoomData->largestArea = windowAreaOnScreen;
zoomData->screenWithLargestPartOfWindow = targetDevice;
}
}
// Figure out how much we need to move the window to get it entirely on the monitor. If
// the window wouldnÕt fit completely on the monitor anyway, donÕt move it at all; weÕll
// make it fit later on.
short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint,
short idealOnScreenEndPoint, short screenEdge1, short screenEdge2)
{
short offsetAmount;
// First check to see if the window fits on the screen in this dimension.
if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2))
offsetAmount = 0;
else {
// Find out how much of the window lies off this screen by subtracting the amount of the window
// that is on the screen from the size of the entire window in this dimension. If the window
// is completely offscreen, the offset amount is going to be the distance from the ideal
// starting point to the first edge of the screen.
if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0) {
// See if the window is lying to the left or above the screen
if (idealEndPoint < screenEdge1)
offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop;
else
// Otherwise, itÕs below or to the right of the screen
offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop;
}
else {
// Window is already partially or completely on the screen
offsetAmount = (idealEndPoint - idealStartPoint) -
(idealOnScreenEndPoint - idealOnScreenStartPoint);
// If we are offscreen a little, move the window in a few more pixels from the edge of the screen.
if (offsetAmount != 0)
offsetAmount += kNudgeSlop;
// Check to see which side of the screen the window was falling off of, so that it can be
// nudged in the opposite direction.
if (idealEndPoint > screenEdge2)
offsetAmount = -offsetAmount;
}
}
return offsetAmount;
}
/*
WindowRecord accessor functions
*/
RgnHandle GetWindowContentRegion(WindowPeek theWindow)
{
return (theWindow->contRgn);
}
RgnHandle GetWindowStructureRegion(WindowPeek theWindow)
{
return (theWindow->strucRgn);
}
void GetWindowPortRect(WindowPeek theWindow, Rect *portRect)
{
*portRect = theWindow->port.portRect;
}
void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState)
{
(**((WStateDataHandle) theWindow->dataHandle)).stdState = *standardState;
}
void GetWindowUserState(WindowPeek theWindow, Rect *userState)
{
*userState = (**((WStateDataHandle) theWindow->dataHandle)).userState;
}