home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / RandomDot 1.1.0 / source / ZoomCode.c < prev   
Encoding:
C/C++ Source or Header  |  1996-07-07  |  11.1 KB  |  296 lines  |  [TEXT/KAHL]

  1. /* ZoomCode.c -
  2.     Note: this code is from develop magazine, issue 17. It is referreed by Apple and
  3.     may be freely used in Macintosh programs.
  4.  */
  5. #include "ZoomCode.h"
  6.  
  7. struct ZoomData {
  8.     GDHandle        screenWithLargestPartOfWindow;
  9.     unsigned long    largestArea;
  10.     Rect            windowBounds;
  11. };
  12. typedef struct ZoomData ZoomData, *ZoomDataPtr;
  13.  
  14.  
  15. enum {
  16.     kNudgeSlop    =    4,
  17.     kIconSpace    =    64
  18. };
  19.  
  20. RgnHandle GetWindowContentRegion(WindowPeek theWindow);
  21. RgnHandle GetWindowStructureRegion(WindowPeek theWindow);
  22. void GetWindowPortRect(WindowPeek theWindow, Rect *portRect);
  23.  
  24. static pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice,
  25.                                     long userData);
  26.  
  27. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint,
  28.                             short idealOnScreenStartPoint, short idealOnScreenEndPoint,
  29.                             short screenEdge1, short screenEdge2);
  30.  
  31. void ZoomTheWindow(WindowPeek theWindow, short zoomState,
  32.                     CalcIdealDocumentSizeProcPtr calcRoutine)
  33. {
  34.     ZoomData    zoomData;
  35.     Rect        newStandardRect;
  36.     Rect        scratchRect;
  37.     Rect        screenRect;
  38.     Rect        portRect;
  39.     Rect        contentRegionBoundingBox;
  40.     Rect        structureRegionBoundingBox;
  41.     Rect        deviceLoopRect;
  42.     GrafPtr        currentPort;
  43.     RgnHandle    scratchRegion;
  44.     RgnHandle    contentRegion;
  45.     RgnHandle    structureRegion;
  46.     GDHandle    mainDevice;
  47.     short        horizontalAmountOffScreen;
  48.     short        verticalAmountOffScreen;
  49.     short        windowFrameTopSize;
  50.     short        windowFrameLeftSize;
  51.     short        windowFrameRightSize;
  52.     short        windowFrameBottomSize;
  53.     static DeviceLoopDrawingUPP deviceLoopUpdate = nil;
  54.     
  55.  
  56.     GetPort(¤tPort);
  57.     SetPort((WindowPtr) theWindow);
  58.     contentRegion = GetWindowContentRegion(theWindow);
  59.     structureRegion = GetWindowStructureRegion(theWindow);
  60.     GetWindowPortRect(theWindow, &portRect);
  61.     contentRegionBoundingBox = (**contentRegion).rgnBBox;
  62.     structureRegionBoundingBox = (**structureRegion).rgnBBox;
  63.     
  64.     // Determine the size of the window frame
  65.     windowFrameTopSize = contentRegionBoundingBox.top - 
  66.                                     structureRegionBoundingBox.top;
  67.     windowFrameLeftSize = contentRegionBoundingBox.left - 
  68.                                     structureRegionBoundingBox.left;
  69.     windowFrameRightSize = structureRegionBoundingBox.right - 
  70.                                     contentRegionBoundingBox.right;
  71.     windowFrameBottomSize = structureRegionBoundingBox.bottom - 
  72.                                     contentRegionBoundingBox.bottom;
  73.                                     
  74.     // If the window is being zoomed into the standard state, calculate the best size
  75.     // to display the window’s information.
  76.     mainDevice = GetMainDevice();
  77.     if (zoomState == inZoomOut) {
  78.         zoomData.screenWithLargestPartOfWindow = mainDevice;
  79.         zoomData.largestArea = 0;
  80.     
  81.         // Usually, we would use the content region’s bounding box to determine the monitor
  82.         // with largest portion of the window’s area. However, if the entire content region
  83.         // of the window is not on any screen, the structure region should be used instead.
  84.         scratchRegion = NewRgn();
  85.         SectRgn(GetGrayRgn(), contentRegion, scratchRegion);
  86.         if (EmptyRgn(scratchRegion))
  87.             zoomData.windowBounds = structureRegionBoundingBox;
  88.         else
  89.             zoomData.windowBounds = contentRegionBoundingBox;
  90.     
  91.         // Use DeviceLoop to walk through all the active screens to find the one with the
  92.         // largest portion of the zoomed window
  93.         deviceLoopRect = zoomData.windowBounds;
  94.         GlobalToLocal((Point *)&deviceLoopRect);
  95.         GlobalToLocal((Point *)&deviceLoopRect.bottom);
  96.         RectRgn(scratchRegion, &deviceLoopRect);
  97.         if(nil == deviceLoopUpdate){
  98.             deviceLoopUpdate = NewDeviceLoopDrawingProc(CalcWindowAreaOnScreen);
  99.         }
  100.         DeviceLoop(scratchRegion, deviceLoopUpdate, (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. static pascal void    CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, long userData)
  208. {
  209.     ZoomDataPtr    zoomData;
  210.     long        windowAreaOnScreen;
  211.     Rect        windowPortionOnScreen;
  212.  
  213.     zoomData = (ZoomDataPtr) userData;
  214.  
  215.     // Find the rectangle that encloses the intersection of the window and this screen.
  216.     SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect), &windowPortionOnScreen);
  217.     
  218.     // Offset the rectangle so that it’s right and bottom are also it’s width and height.
  219.     OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left, -windowPortionOnScreen.top);
  220.     
  221.     // Calculate the area of the portion of the window that’s on this screen.
  222.     windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) windowPortionOnScreen.bottom;
  223.     
  224.     // If this is the largest portion of the window that has been encountered so far,
  225.     // remember this screen as the potential screen to zoom to.
  226.     if (windowAreaOnScreen > zoomData->largestArea) {
  227.         zoomData->largestArea = windowAreaOnScreen;
  228.         zoomData->screenWithLargestPartOfWindow = targetDevice;
  229.     }
  230. }
  231.  
  232. // Figure out how much we need to move the window to get it entirely on the monitor.  If
  233. // the window wouldn’t fit completely on the monitor anyway, don’t move it at all; we’ll
  234. // make it fit later on.
  235.  
  236. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint,
  237.                             short idealOnScreenEndPoint, short screenEdge1, short screenEdge2)
  238. {
  239.     short    offsetAmount;
  240.  
  241.     // First check to see if the window fits on the screen in this dimension.
  242.     if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2))
  243.         offsetAmount = 0;
  244.     else {
  245.     
  246.         // Find out how much of the window lies off this screen by subtracting the amount of the window
  247.         // that is on the screen from the size of the entire window in this dimension. If the window
  248.         // is completely offscreen, the offset amount is going to be the distance from the ideal
  249.         // starting point to the first edge of the screen.
  250.         if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0) {
  251.             // See if the window is lying to the left or above the screen
  252.             if (idealEndPoint < screenEdge1)
  253.                 offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop;
  254.             else
  255.             // Otherwise, it’s below or to the right of the screen
  256.                 offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop;
  257.         }
  258.         else {
  259.             // Window is already partially or completely on the screen
  260.             offsetAmount = (idealEndPoint - idealStartPoint) -
  261.                             (idealOnScreenEndPoint - idealOnScreenStartPoint);
  262.     
  263.             // If we are offscreen a little, move the window in a few more pixels from the edge of the screen.
  264.             if (offsetAmount != 0)
  265.                 offsetAmount += kNudgeSlop;
  266.             
  267.             // Check to see which side of the screen the window was falling off of, so that it can be
  268.             // nudged in the opposite direction.
  269.             if (idealEndPoint > screenEdge2)
  270.                 offsetAmount = -offsetAmount;
  271.         }
  272.     }
  273.     
  274.     return offsetAmount;
  275. }
  276.  
  277. /*
  278.     WindowRecord accessor functions
  279. */
  280.  
  281. RgnHandle GetWindowContentRegion(WindowPeek theWindow)
  282. {
  283.     return (theWindow->contRgn);
  284. }
  285.  
  286. RgnHandle GetWindowStructureRegion(WindowPeek theWindow)
  287. {
  288.     return (theWindow->strucRgn);
  289. }
  290.  
  291. void GetWindowPortRect(WindowPeek theWindow, Rect *portRect)
  292. {
  293.     *portRect = theWindow->port.portRect;
  294. }
  295.  
  296.