Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
193 | daniel-mar | 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 | } |