home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / µSim 1.1 / FabLibsƒ / ZoomCode.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-26  |  12.5 KB  |  370 lines  |  [TEXT/CWIE]

  1. #ifndef __TYPES__
  2. #include <Types.h>
  3. #endif
  4.  
  5. #ifndef __MENUS__
  6. #include <Menus.h>
  7. #endif
  8.  
  9. #ifndef __QUICKDRAW__
  10. #include <Quickdraw.h>
  11. #endif
  12.  
  13. #ifndef __WINDOWS__
  14. #include <Windows.h>
  15. #endif
  16.  
  17. #include    "ZoomCode.h"
  18.  
  19. /*
  20. Change History, Fabrizio Oddone
  21.  
  22.     97/04/15    Window macros accessors, FO
  23.  
  24.     97/04/11    UPP modification was buggy, FO
  25.                 Eliminated stuff conflicting with new Windows.h, FO
  26.  
  27.     12/07/94    modified by James E. Trudeau
  28.                 Now uses a UPP for call to DeviceLoop()
  29. */
  30.  
  31. enum {
  32.     kNudgeSlop    =    2,    // I like 2 better, FO
  33.     kIconSpace    =    64
  34. };
  35.  
  36. static pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice,
  37.                                     long userData);
  38. static short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint,
  39.                             short idealOnScreenStartPoint, short idealOnScreenEndPoint,
  40.                             short screenEdge1, short screenEdge2);
  41.  
  42. static void GetWindowPortRect(WindowPeek theWindow, Rect * const portRect);
  43. static void ContentRect2DefaultStructRect(Rect * const strucR);
  44.  
  45.  
  46. /*
  47.     WindowRecord accessor functions
  48. */
  49.  
  50. // macros instead of functions;
  51. // I didn't use the Windows.h accessors because
  52. // they call CopyRgn(), so you'd need to DisposeRgn() later -- FO
  53.  
  54. #define    GetWContentRegion(w)    (w->contRgn)
  55. #define    GetWStructureRegion(w)    (w->strucRgn)
  56.  
  57. void ZoomTheWindow(WindowPeek theWindow, short zoomState,
  58.                     CalcIdealDocumentSizeProcPtr calcRoutine,
  59.                     CalcIdealDocumentSizeProcPtr postProcess)
  60. {
  61. ZoomData    zoomData;
  62. Rect        newStandardRect;
  63. Rect        scratchRect;
  64. Rect        screenRect;
  65. Rect        portRect;
  66. Rect        contentRegionBoundingBox;
  67. Rect        structureRegionBoundingBox;
  68. GrafPtr        currentPort;
  69. short        horizontalAmountOffScreen;
  70. short        verticalAmountOffScreen;
  71. short        windowFrameTopSize;
  72. short        windowFrameLeftSize;
  73. short        windowFrameRightSize;
  74. short        windowFrameBottomSize;
  75.  
  76.  
  77. GetPort(¤tPort);
  78. SetPort((WindowPtr) theWindow);
  79. GetWindowPortRect(theWindow, &portRect);
  80.  
  81. if (nil == GetWContentRegion(theWindow) || EmptyRgn(GetWContentRegion(theWindow))) {
  82.     contentRegionBoundingBox = portRect;
  83.     LocalToGlobal(&topLeft(contentRegionBoundingBox));
  84.     LocalToGlobal(&botRight(contentRegionBoundingBox));
  85.     }
  86. else
  87.     contentRegionBoundingBox = (**GetWContentRegion(theWindow)).rgnBBox;
  88.  
  89. if (nil == GetWStructureRegion(theWindow) || EmptyRgn(GetWStructureRegion(theWindow))) {
  90.     structureRegionBoundingBox = contentRegionBoundingBox;
  91.     ContentRect2DefaultStructRect(&structureRegionBoundingBox);
  92.     }
  93. else
  94.     structureRegionBoundingBox = (**GetWStructureRegion(theWindow)).rgnBBox;
  95.  
  96. // Determine the size of the window frame
  97. windowFrameTopSize = contentRegionBoundingBox.top - 
  98.                                 structureRegionBoundingBox.top;
  99. windowFrameLeftSize = contentRegionBoundingBox.left - 
  100.                                 structureRegionBoundingBox.left;
  101. windowFrameRightSize = structureRegionBoundingBox.right - 
  102.                                 contentRegionBoundingBox.right;
  103. windowFrameBottomSize = structureRegionBoundingBox.bottom - 
  104.                                 contentRegionBoundingBox.bottom;
  105.                                 
  106. // If the window is being zoomed into the standard state, calculate the best size
  107. // to display the window’s information.
  108. if (zoomState == inZoomOut) {
  109.     GDHandle    mainDevice;
  110.  
  111.     mainDevice = GetParentScreenRect(theWindow, &zoomData, &screenRect);
  112. // Go figure out the perfect size for the window as if we had an infinitely large
  113. // screen
  114.     calcRoutine((WindowPtr) theWindow, &newStandardRect);
  115.     
  116. // Anchor the new rectangle at the window’s current top left corner
  117.     OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  118.     OffsetRect(&newStandardRect, contentRegionBoundingBox.left,
  119.                 contentRegionBoundingBox.top);
  120.     
  121. // newStandardRect is the ideal size for the content area. The window frame
  122. // needs to be accounted for when we see if the window needs to be moved,
  123. // or resized, so add in the dimensions of the window frame.
  124.     newStandardRect.top -= windowFrameTopSize;
  125.     newStandardRect.left -= windowFrameLeftSize;
  126.     newStandardRect.right += windowFrameRightSize;
  127.     newStandardRect.bottom += windowFrameBottomSize;
  128.     
  129. // If the new rectangle falls off the edge of the screen, nudge it so that it’s just
  130. // on the screen. CalculateOffsetAmount determines how much of the window is offscreen.
  131.     SectRect(&newStandardRect, &screenRect, &scratchRect);
  132.     if (!EqualRect(&newStandardRect, &scratchRect)) {
  133.         horizontalAmountOffScreen = CalculateOffsetAmount(newStandardRect.left,
  134.                                                            newStandardRect.right,
  135.                                                            scratchRect.left,
  136.                                                            scratchRect.right,
  137.                                                            screenRect.left,
  138.                                                            screenRect.right);
  139.         verticalAmountOffScreen = CalculateOffsetAmount(newStandardRect.top,
  140.                                                         newStandardRect.bottom,
  141.                                                         scratchRect.top,
  142.                                                         scratchRect.bottom,
  143.                                                         screenRect.top,
  144.                                                         screenRect.bottom);
  145.         OffsetRect(&newStandardRect, horizontalAmountOffScreen, verticalAmountOffScreen);
  146.         }
  147.  
  148. // If we’re still falling off the edge of the screen, that means that the perfect
  149. // size is larger than the screen, so we need to shrink down the standard size
  150.     SectRect(&newStandardRect, &screenRect, &scratchRect);
  151.     if (!EqualRect(&newStandardRect, &scratchRect)) {
  152.  
  153. // First shrink the width of the window. If the window is wider than the screen
  154. // it is zooming to, we can just pin the standard rectangle to the edges of the
  155. // screen, leaving some slop. If the window is narrower than the screen, we know
  156. // we just nudged it into position, so nothing needs to be done.
  157.         if ((newStandardRect.right - newStandardRect.left) >
  158.             (screenRect.right - screenRect.left)) {
  159.             newStandardRect.left = screenRect.left + kNudgeSlop;
  160.             newStandardRect.right = screenRect.right - kNudgeSlop;
  161.  
  162.             if ((zoomData.screenWithLargestPartOfWindow == mainDevice) &&
  163.                 (newStandardRect.right > (screenRect.right - kIconSpace)))
  164.                 newStandardRect.right = screenRect.right - kIconSpace;
  165.             }
  166.  
  167. // Move in the top. Like the width of the window, nothing needs to be done unless
  168. // the window is taller than the height of the screen.
  169.         if ((newStandardRect.bottom - newStandardRect.top) >
  170.             (screenRect.bottom - screenRect.top)) {
  171.             newStandardRect.top = screenRect.top + kNudgeSlop;
  172.             newStandardRect.bottom = screenRect.bottom - kNudgeSlop;
  173.             }
  174.         }
  175.  
  176. // We’ve got the best possible window position. Remove the
  177. // frame, slam it into the WStateData record and let ZoomWindow
  178. // take care of the rest.
  179.     newStandardRect.top += windowFrameTopSize;
  180.     newStandardRect.left += windowFrameLeftSize;
  181.     newStandardRect.right -= windowFrameRightSize;
  182.     newStandardRect.bottom -= windowFrameBottomSize;
  183.     if (postProcess)
  184.         postProcess((WindowPtr)theWindow, &newStandardRect);
  185.     SetWindowStandardState(theWindow, &newStandardRect);
  186.     }
  187. else
  188.     GetWindowUserState(theWindow, &newStandardRect);
  189.  
  190. // If the window is still anchored at the current location, then just resize it
  191. if ((newStandardRect.left == contentRegionBoundingBox.left) &&
  192.     (newStandardRect.top == contentRegionBoundingBox.top)) {
  193.     OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  194.     SizeWindow((WindowPtr) theWindow, newStandardRect.right, newStandardRect.bottom, true);
  195.     }
  196. else {
  197.     RgnHandle    scratchRegion;
  198.  
  199.     scratchRegion = NewRgn();
  200.     GetClip(scratchRegion);
  201.     ClipRect(&portRect);
  202.     EraseRect(&portRect);
  203.     ZoomWindow((WindowPtr) theWindow, zoomState, false);
  204.     SetClip(scratchRegion);
  205.     DisposeRgn(scratchRegion);
  206.     }
  207.  
  208. SetPort(currentPort);
  209. }
  210.  
  211. pascal void    CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, long userData)
  212. {
  213. #pragma unused (depth, deviceFlags)
  214. ZoomDataPtr    zoomData = (ZoomDataPtr) userData;
  215. long        windowAreaOnScreen;
  216. Rect        windowPortionOnScreen;
  217.  
  218. // Find the rectangle that encloses the intersection of the window and this screen.
  219. SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect), &windowPortionOnScreen);
  220.  
  221. // Offset the rectangle so that it’s right and bottom are also it’s width and height.
  222. OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left, -windowPortionOnScreen.top);
  223.  
  224. // Calculate the area of the portion of the window that’s on this screen.
  225. windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) windowPortionOnScreen.bottom;
  226.  
  227. // If this is the largest portion of the window that has been encountered so far,
  228. // remember this screen as the potential screen to zoom to.
  229. if (windowAreaOnScreen > zoomData->largestArea) {
  230.     zoomData->largestArea = windowAreaOnScreen;
  231.     zoomData->screenWithLargestPartOfWindow = targetDevice;
  232.     }
  233. }
  234.  
  235. // Figure out how much we need to move the window to get it entirely on the monitor.  If
  236. // the window wouldn’t fit completely on the monitor anyway, don’t move it at all; we’ll
  237. // make it fit later on.
  238.  
  239. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint,
  240.                             short idealOnScreenEndPoint, short screenEdge1, short screenEdge2)
  241. {
  242. short    offsetAmount;
  243.  
  244. // First check to see if the window fits on the screen in this dimension.
  245. if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2))
  246.     offsetAmount = 0;
  247. else {
  248.  
  249. // Find out how much of the window lies off this screen by subtracting the amount of the window
  250. // that is on the screen from the size of the entire window in this dimension. If the window
  251. // is completely offscreen, the offset amount is going to be the distance from the ideal
  252. // starting point to the first edge of the screen.
  253.     if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0) {
  254. // See if the window is lying to the left or above the screen
  255.         if (idealEndPoint < screenEdge1)
  256.             offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop;
  257.         else
  258. // Otherwise, it’s below or to the right of the screen
  259.             offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop;
  260.     }
  261.     else {
  262. // Window is already partially or completely on the screen
  263.         offsetAmount = (idealEndPoint - idealStartPoint) -
  264.                         (idealOnScreenEndPoint - idealOnScreenStartPoint);
  265.  
  266. // If we are offscreen a little, move the window in a few more pixels from the edge of the screen.
  267.         if (offsetAmount != 0)
  268.             offsetAmount += kNudgeSlop;
  269. // Check to see which side of the screen the window was falling off of, so that it can be
  270. // nudged in the opposite direction.
  271.         if (idealEndPoint > screenEdge2)
  272.             offsetAmount = -offsetAmount;
  273.     }
  274. }
  275.  
  276. return offsetAmount;
  277. }
  278.  
  279. /*
  280.     WindowRecord accessor functions
  281. */
  282.  
  283.  
  284. void GetWindowPortRect(WindowPeek theWindow, Rect * const portRect)
  285. {
  286. *portRect = theWindow->port.portRect;
  287. }
  288.  
  289.  
  290. GDHandle GetParentScreenRect(WindowPeek w, ZoomDataPtr zd, Rect * const screenRect)
  291. {
  292. GDHandle    mainDevice;
  293. RgnHandle    scratchRegion;
  294.  
  295. #if GENERATING68K
  296. long    Gresp;
  297. if (Gestalt(gestaltQuickdrawVersion, &Gresp) == noErr && Gresp >= gestalt32BitQD13) {
  298. #endif
  299. {    Rect    globalPortRect;
  300.  
  301.     mainDevice = GetMainDevice();
  302.     zd->screenWithLargestPartOfWindow = mainDevice;
  303.     zd->largestArea = 0;
  304.  
  305. // Usually, we would use the content region’s bounding box to determine the monitor
  306. // with largest portion of the window’s area. However, if the entire content region
  307. // of the window is not on any screen, the structure region should be used instead.
  308.     GetWindowPortRect(w, &globalPortRect);
  309.     LocalToGlobal(&topLeft(globalPortRect));
  310.     LocalToGlobal(&botRight(globalPortRect));
  311.  
  312.     scratchRegion = NewRgn();
  313.     RectRgn(scratchRegion, &globalPortRect);
  314.     SectRgn(GetGrayRgn(), scratchRegion, scratchRegion);
  315.     zd->windowBounds = globalPortRect;
  316. }
  317.     if (EmptyRgn(scratchRegion)) {
  318.         if (nil == GetWStructureRegion(w) || EmptyRgn(GetWStructureRegion(w))) {
  319.             ContentRect2DefaultStructRect(&zd->windowBounds);
  320.             }
  321.         else
  322.             zd->windowBounds = (*GetWStructureRegion(w))->rgnBBox;
  323.         }
  324.  
  325. // Use DeviceLoop to walk through all the active screens to find the one with the
  326. // largest portion of the zoomed window
  327. {    Rect    localPortRect;
  328.  
  329.     localPortRect = zd->windowBounds;
  330.     GlobalToLocal(&topLeft(localPortRect));
  331.     GlobalToLocal(&botRight(localPortRect));
  332.     RectRgn(scratchRegion, &localPortRect);
  333. }
  334. /* jt 12/7/94 for PPC, replace original call to DeviceLoop to use UPP  */
  335. // FO 97/04/11    fix UPP
  336.     {
  337.         DeviceLoopDrawingUPP myUPP =  NewDeviceLoopDrawingProc(CalcWindowAreaOnScreen);
  338.  
  339.         DeviceLoop(scratchRegion, myUPP, (long) zd, (DeviceLoopFlags) singleDevices);
  340.                 
  341.         DisposeRoutineDescriptor(myUPP);
  342.     }
  343.  
  344.     DisposeRgn(scratchRegion);
  345.     *screenRect = (*zd->screenWithLargestPartOfWindow)->gdRect;
  346. #if GENERATING68K
  347.     }
  348. else {
  349.     zd->screenWithLargestPartOfWindow = mainDevice = (GDHandle)-1;
  350.     *screenRect = qd.screenBits.bounds;    
  351.     }
  352. #endif
  353.  
  354.     
  355. // If the monitor being zoomed to is the main monitor, change the top of the
  356. // usable screen area to avoid putting the title bar underneath the menubar.
  357. if (zd->screenWithLargestPartOfWindow == mainDevice)
  358.     screenRect->top += GetMBarHeight();
  359. return mainDevice;
  360. }
  361.  
  362. void ContentRect2DefaultStructRect(Rect * const strucR)
  363. {
  364. strucR->top -= 19;
  365. strucR->left -= 1;
  366. strucR->bottom += 2;
  367. strucR->right += 2;
  368. }
  369.  
  370.