Rev 193 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 193 | Rev 259 | ||
---|---|---|---|
Line 1... | Line -... | ||
1 | #include <Types.h> |
- | |
2 | #include <Menus.h> |
1 | #include <Types.h> |
- | 2 | #include <Menus.h> |
|
- | 3 | #include <Quickdraw.h> |
|
- | 4 | #include <Windows.h> |
|
- | 5 | ||
- | 6 | struct ZoomData { |
|
- | 7 | GDHandle screenWithLargestPartOfWindow; |
|
- | 8 | unsigned long largestArea; |
|
- | 9 | Rect windowBounds; |
|
- | 10 | }; |
|
- | 11 | typedef struct ZoomData ZoomData, *ZoomDataPtr; |
|
- | 12 | ||
- | 13 | typedef void (*CalcIdealDocumentSizeProcPtr)(WindowPtr theWindow, Rect *idealContentSize); |
|
- | 14 | ||
- | 15 | enum { |
|
- | 16 | kNudgeSlop = 4, |
|
- | 17 | kIconSpace = 64 |
|
- | 18 | }; |
|
- | 19 | ||
- | 20 | void ZoomTheWindow(WindowPeek theWindow, short zoomState, |
|
- | 21 | CalcIdealDocumentSizeProcPtr calcRoutine); |
|
- | 22 | pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, |
|
- | 23 | long userData); |
|
- | 24 | short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, |
|
- | 25 | short idealOnScreenStartPoint, short idealOnScreenEndPoint, |
|
- | 26 | short screenEdge1, short screenEdge2); |
|
- | 27 | ||
- | 28 | static RgnHandle GetWindowContentRegion(WindowPeek theWindow); |
|
- | 29 | static RgnHandle GetWindowStructureRegion(WindowPeek theWindow); |
|
- | 30 | static void GetWindowPortRect(WindowPeek theWindow, Rect *portRect); |
|
- | 31 | static void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState); |
|
- | 32 | static void GetWindowUserState(WindowPeek theWindow, Rect *userState); |
|
- | 33 | ||
- | 34 | ||
- | 35 | void ZoomTheWindow(WindowPeek theWindow, short zoomState, |
|
- | 36 | CalcIdealDocumentSizeProcPtr calcRoutine) |
|
- | 37 | { |
|
- | 38 | ZoomData zoomData; |
|
- | 39 | Rect newStandardRect; |
|
- | 40 | Rect scratchRect; |
|
- | 41 | Rect screenRect; |
|
- | 42 | Rect portRect; |
|
- | 43 | Rect contentRegionBoundingBox; |
|
- | 44 | Rect structureRegionBoundingBox; |
|
- | 45 | Rect deviceLoopRect; |
|
- | 46 | GrafPtr currentPort; |
|
- | 47 | RgnHandle scratchRegion; |
|
- | 48 | RgnHandle contentRegion; |
|
- | 49 | RgnHandle structureRegion; |
|
- | 50 | GDHandle mainDevice; |
|
- | 51 | short horizontalAmountOffScreen; |
|
- | 52 | short verticalAmountOffScreen; |
|
- | 53 | short windowFrameTopSize; |
|
- | 54 | short windowFrameLeftSize; |
|
- | 55 | short windowFrameRightSize; |
|
- | 56 | short windowFrameBottomSize; |
|
- | 57 | ||
- | 58 | ||
- | 59 | GetPort(¤tPort); |
|
- | 60 | SetPort((WindowPtr) theWindow); |
|
- | 61 | contentRegion = GetWindowContentRegion(theWindow); |
|
- | 62 | structureRegion = GetWindowStructureRegion(theWindow); |
|
- | 63 | GetWindowPortRect(theWindow, &portRect); |
|
- | 64 | contentRegionBoundingBox = (**contentRegion).rgnBBox; |
|
- | 65 | structureRegionBoundingBox = (**structureRegion).rgnBBox; |
|
- | 66 | ||
- | 67 | // Determine the size of the window frame |
|
- | 68 | windowFrameTopSize = contentRegionBoundingBox.top - |
|
- | 69 | structureRegionBoundingBox.top; |
|
- | 70 | windowFrameLeftSize = contentRegionBoundingBox.left - |
|
- | 71 | structureRegionBoundingBox.left; |
|
- | 72 | windowFrameRightSize = structureRegionBoundingBox.right - |
|
- | 73 | contentRegionBoundingBox.right; |
|
- | 74 | windowFrameBottomSize = structureRegionBoundingBox.bottom - |
|
- | 75 | contentRegionBoundingBox.bottom; |
|
- | 76 | ||
- | 77 | // If the window is being zoomed into the standard state, calculate the best size |
|
- | 78 | // to display the windowÕs information. |
|
- | 79 | mainDevice = GetMainDevice(); |
|
- | 80 | if (zoomState == inZoomOut) { |
|
- | 81 | zoomData.screenWithLargestPartOfWindow = mainDevice; |
|
- | 82 | zoomData.largestArea = 0; |
|
- | 83 | ||
- | 84 | // Usually, we would use the content regionÕs bounding box to determine the monitor |
|
- | 85 | // with largest portion of the windowÕs area. However, if the entire content region |
|
- | 86 | // of the window is not on any screen, the structure region should be used instead. |
|
- | 87 | scratchRegion = NewRgn(); |
|
- | 88 | SectRgn(GetGrayRgn(), contentRegion, scratchRegion); |
|
- | 89 | if (EmptyRgn(scratchRegion)) |
|
- | 90 | zoomData.windowBounds = structureRegionBoundingBox; |
|
- | 91 | else |
|
- | 92 | zoomData.windowBounds = contentRegionBoundingBox; |
|
- | 93 | ||
- | 94 | // Use DeviceLoop to walk through all the active screens to find the one with the |
|
- | 95 | // largest portion of the zoomed window |
|
- | 96 | deviceLoopRect = zoomData.windowBounds; |
|
- | 97 | GlobalToLocal((Point *)&deviceLoopRect); |
|
- | 98 | GlobalToLocal((Point *)&deviceLoopRect.bottom); |
|
- | 99 | RectRgn(scratchRegion, &deviceLoopRect); |
|
- | 100 | DeviceLoop(scratchRegion, &CalcWindowAreaOnScreen, (long) &zoomData, |
|
- | 101 | (DeviceLoopFlags) singleDevices); |
|
- | 102 | DisposeRgn(scratchRegion); |
|
- | 103 | screenRect = (**(zoomData.screenWithLargestPartOfWindow)).gdRect; |
|
- | 104 | ||
- | 105 | // If the monitor being zoomed to is the main monitor, change the top of the |
|
- | 106 | // useable screen area to avoid putting the title bar underneath the menubar. |
|
- | 107 | if (zoomData.screenWithLargestPartOfWindow == mainDevice) |
|
- | 108 | screenRect.top += GetMBarHeight(); |
|
- | 109 | ||
- | 110 | // Go figure out the perfect size for the window as if we had an infinitely large |
|
- | 111 | // screen |
|
- | 112 | (*calcRoutine)((WindowPtr) theWindow, &newStandardRect); |
|
- | 113 | ||
- | 114 | // Anchor the new rectangle at the windowÕs current top left corner |
|
- | 115 | OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top); |
|
- | 116 | OffsetRect(&newStandardRect, contentRegionBoundingBox.left, |
|
- | 117 | contentRegionBoundingBox.top); |
|
- | 118 | ||
- | 119 | // newStandardRect is the ideal size for the content area. The window frame |
|
- | 120 | // needs to be accounted for when we see if the window needs to be moved, |
|
- | 121 | // or resized, so add in the dimensions of the window frame. |
|
- | 122 | newStandardRect.top -= windowFrameTopSize; |
|
- | 123 | newStandardRect.left -= windowFrameLeftSize; |
|
- | 124 | newStandardRect.right += windowFrameRightSize; |
|
- | 125 | newStandardRect.bottom += windowFrameBottomSize; |
|
- | 126 | ||
- | 127 | // If the new rectangle falls off the edge of the screen, nudge it so that itÕs just |
|
- | 128 | // on the screen. CalculateOffsetAmount determines how much of the window is offscreen. |
|
- | 129 | SectRect(&newStandardRect, &screenRect, &scratchRect); |
|
- | 130 | if (!EqualRect(&newStandardRect, &scratchRect)) { |
|
- | 131 | horizontalAmountOffScreen = CalculateOffsetAmount(newStandardRect.left, |
|
- | 132 | newStandardRect.right, |
|
- | 133 | scratchRect.left, |
|
- | 134 | scratchRect.right, |
|
- | 135 | screenRect.left, |
|
- | 136 | screenRect.right); |
|
- | 137 | verticalAmountOffScreen = CalculateOffsetAmount(newStandardRect.top, |
|
- | 138 | newStandardRect.bottom, |
|
- | 139 | scratchRect.top, |
|
- | 140 | scratchRect.bottom, |
|
- | 141 | screenRect.top, |
|
- | 142 | screenRect.bottom); |
|
- | 143 | OffsetRect(&newStandardRect, horizontalAmountOffScreen, |
|
- | 144 | verticalAmountOffScreen); |
|
- | 145 | } |
|
- | 146 | ||
- | 147 | // If weÕre still falling off the edge of the screen, that means that the perfect |
|
- | 148 | // size is larger than the screen, so we need to shrink down the standard size |
|
- | 149 | SectRect(&newStandardRect, &screenRect, &scratchRect); |
|
- | 150 | if (!EqualRect(&newStandardRect, &scratchRect)) { |
|
- | 151 | ||
- | 152 | // First shrink the width of the window. If the window is wider than the screen |
|
- | 153 | // it is zooming to, we can just pin the standard rectangle to the edges of the |
|
- | 154 | // screen, leaving some slop. If the window is narrower than the screen, we know |
|
- | 155 | // we just nudged it into position, so nothing needs to be done. |
|
- | 156 | if ((newStandardRect.right - newStandardRect.left) > |
|
- | 157 | (screenRect.right - screenRect.left)) { |
|
- | 158 | newStandardRect.left = screenRect.left + kNudgeSlop; |
|
- | 159 | newStandardRect.right = screenRect.right - kNudgeSlop; |
|
- | 160 | ||
- | 161 | if ((zoomData.screenWithLargestPartOfWindow == mainDevice) && |
|
- | 162 | (newStandardRect.right > (screenRect.right - kIconSpace))) |
|
- | 163 | newStandardRect.right = screenRect.right - kIconSpace; |
|
- | 164 | } |
|
- | 165 | ||
- | 166 | // Move in the top. Like the width of the window, nothing needs to be done unless |
|
- | 167 | // the window is taller than the height of the screen. |
|
- | 168 | if ((newStandardRect.bottom - newStandardRect.top) > |
|
- | 169 | (screenRect.bottom - screenRect.top)) { |
|
- | 170 | newStandardRect.top = screenRect.top + kNudgeSlop; |
|
- | 171 | newStandardRect.bottom = screenRect.bottom - kNudgeSlop; |
|
- | 172 | } |
|
- | 173 | } |
|
- | 174 | ||
- | 175 | // WeÕve got the best possible window position. Remove the |
|
- | 176 | // frame, slam it into the WStateData record and let ZoomWindow |
|
- | 177 | // take care of the rest. |
|
- | 178 | newStandardRect.top += windowFrameTopSize; |
|
- | 179 | newStandardRect.left += windowFrameLeftSize; |
|
- | 180 | newStandardRect.right -= windowFrameRightSize; |
|
- | 181 | newStandardRect.bottom -= windowFrameBottomSize; |
|
- | 182 | SetWindowStandardState(theWindow, &newStandardRect); |
|
- | 183 | } |
|
- | 184 | else |
|
- | 185 | GetWindowUserState(theWindow, &newStandardRect); |
|
- | 186 | ||
- | 187 | // If the window is still anchored at the current location, then just resize it |
|
- | 188 | if ((newStandardRect.left == contentRegionBoundingBox.left) && |
|
- | 189 | (newStandardRect.top == contentRegionBoundingBox.top)) { |
|
- | 190 | OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top); |
|
- | 191 | SizeWindow((WindowPtr) theWindow, newStandardRect.right, newStandardRect.bottom, |
|
- | 192 | true); |
|
- | 193 | } |
|
- | 194 | else { |
|
- | 195 | scratchRegion = NewRgn(); |
|
- | 196 | GetClip(scratchRegion); |
|
- | 197 | ClipRect(&portRect); |
|
- | 198 | EraseRect(&portRect); |
|
- | 199 | ZoomWindow((WindowPtr) theWindow, zoomState, false); |
|
- | 200 | SetClip(scratchRegion); |
|
- | 201 | DisposeRgn(scratchRegion); |
|
- | 202 | } |
|
- | 203 | ||
- | 204 | SetPort(currentPort); |
|
- | 205 | } |
|
- | 206 | ||
- | 207 | pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, long userData) |
|
- | 208 | { |
|
- | 209 | #pragma unused (depth, deviceFlags) |
|
- | 210 | ZoomDataPtr zoomData = (ZoomDataPtr) userData; |
|
- | 211 | long windowAreaOnScreen; |
|
- | 212 | Rect windowPortionOnScreen; |
|
- | 213 | ||
- | 214 | // Find the rectangle that encloses the intersection of the window and this screen. |
|
- | 215 | SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect), &windowPortionOnScreen); |
|
- | 216 | ||
- | 217 | // Offset the rectangle so that itÕs right and bottom are also itÕs width and height. |
|
- | 218 | OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left, -windowPortionOnScreen.top); |
|
- | 219 | ||
- | 220 | // Calculate the area of the portion of the window thatÕs on this screen. |
|
- | 221 | windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) windowPortionOnScreen.bottom; |
|
- | 222 | ||
- | 223 | // If this is the largest portion of the window that has been encountered so far, |
|
- | 224 | // remember this screen as the potential screen to zoom to. |
|
- | 225 | if (windowAreaOnScreen > zoomData->largestArea) { |
|
- | 226 | zoomData->largestArea = windowAreaOnScreen; |
|
- | 227 | zoomData->screenWithLargestPartOfWindow = targetDevice; |
|
- | 228 | } |
|
- | 229 | } |
|
- | 230 | ||
- | 231 | // Figure out how much we need to move the window to get it entirely on the monitor. If |
|
- | 232 | // the window wouldnÕt fit completely on the monitor anyway, donÕt move it at all; weÕll |
|
- | 233 | // make it fit later on. |
|
- | 234 | ||
- | 235 | short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint, |
|
- | 236 | short idealOnScreenEndPoint, short screenEdge1, short screenEdge2) |
|
- | 237 | { |
|
- | 238 | short offsetAmount; |
|
- | 239 | ||
- | 240 | // First check to see if the window fits on the screen in this dimension. |
|
- | 241 | if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2)) |
|
- | 242 | offsetAmount = 0; |
|
- | 243 | else { |
|
- | 244 | ||
- | 245 | // Find out how much of the window lies off this screen by subtracting the amount of the window |
|
- | 246 | // that is on the screen from the size of the entire window in this dimension. If the window |
|
- | 247 | // is completely offscreen, the offset amount is going to be the distance from the ideal |
|
- | 248 | // starting point to the first edge of the screen. |
|
- | 249 | if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0) { |
|
- | 250 | // See if the window is lying to the left or above the screen |
|
- | 251 | if (idealEndPoint < screenEdge1) |
|
- | 252 | offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop; |
|
- | 253 | else |
|
- | 254 | // Otherwise, itÕs below or to the right of the screen |
|
- | 255 | offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop; |
|
- | 256 | } |
|
- | 257 | else { |
|
- | 258 | // Window is already partially or completely on the screen |
|
- | 259 | offsetAmount = (idealEndPoint - idealStartPoint) - |
|
- | 260 | (idealOnScreenEndPoint - idealOnScreenStartPoint); |
|
- | 261 | ||
- | 262 | // If we are offscreen a little, move the window in a few more pixels from the edge of the screen. |
|
- | 263 | if (offsetAmount != 0) |
|
- | 264 | offsetAmount += kNudgeSlop; |
|
- | 265 | ||
- | 266 | // Check to see which side of the screen the window was falling off of, so that it can be |
|
- | 267 | // nudged in the opposite direction. |
|
- | 268 | if (idealEndPoint > screenEdge2) |
|
- | 269 | offsetAmount = -offsetAmount; |
|
- | 270 | } |
|
- | 271 | } |
|
- | 272 | ||
- | 273 | return offsetAmount; |
|
- | 274 | } |
|
- | 275 | ||
- | 276 | /* |
|
- | 277 | WindowRecord accessor functions |
|
- | 278 | */ |
|
- | 279 | ||
- | 280 | RgnHandle GetWindowContentRegion(WindowPeek theWindow) |
|
- | 281 | { |
|
- | 282 | return (theWindow->contRgn); |
|
- | 283 | } |
|
- | 284 | ||
- | 285 | RgnHandle GetWindowStructureRegion(WindowPeek theWindow) |
|
- | 286 | { |
|
- | 287 | return (theWindow->strucRgn); |
|
- | 288 | } |
|
- | 289 | ||
- | 290 | void GetWindowPortRect(WindowPeek theWindow, Rect *portRect) |
|
- | 291 | { |
|
- | 292 | *portRect = theWindow->port.portRect; |
|
- | 293 | } |
|
- | 294 | ||
- | 295 | void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState) |
|
- | 296 | { |
|
- | 297 | (**((WStateDataHandle) theWindow->dataHandle)).stdState = *standardState; |
|
- | 298 | } |
|
- | 299 | ||
- | 300 | void GetWindowUserState(WindowPeek theWindow, Rect *userState) |
|
- | 301 | { |
|
- | 302 | *userState = (**((WStateDataHandle) theWindow->dataHandle)).userState; |
|
- | 303 | } |
|
- | 304 |