home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / PNL Libraries / MyWindows.p < prev    next >
Encoding:
Text File  |  1995-10-22  |  14.5 KB  |  404 lines  |  [TEXT/CWIE]

  1. unit MyWindows;
  2.  
  3. { Based heavilly on Dean Yu's Develop #17 code }
  4.  
  5. interface
  6.  
  7.     uses
  8.         Windows, QuickDraw;
  9.  
  10.     const
  11.         document_titlebar_height = 18;
  12.         
  13.     procedure ZoomTheWindow (theWindow: WindowPtr; zoomout: boolean; idealsize: point; var unzoomed: rect);
  14.     procedure ZoomWindowOut (theWindow: WindowPtr; titlebar_height:integer; idealsize: point);
  15.     procedure GetWindowRect (theWindow: WindowPtr; var r: rect);
  16.     procedure SetWindowRect (theWindow: WindowPtr; var r: rect);
  17.     procedure GetWindowPosition(theWindow:WindowPtr; var pos:Point);
  18.     procedure SetWindowPosition(theWindow:WindowPtr; pos:Point);
  19.     function GetWindowContentRegion (theWindow: WindowPtr): RgnHandle;
  20.     function GetWindowStructureRegion (theWindow: WindowPtr): RgnHandle;
  21.     procedure GetWindowPortRect (theWindow: WindowPtr; var portRect: rect);
  22.     function GetWindowVisible (theWindow: WindowPtr): boolean;
  23.     procedure GetWindowStandardState (theWindow: WindowPtr; var standardState: Rect);
  24.     procedure SetWindowStandardState (theWindow: WindowPtr; standardState: Rect);
  25.     procedure GetWindowUserState (theWindow: WindowPtr; var userState: Rect);
  26.     procedure SetWindowUserState (theWindow: WindowPtr; userState: Rect);
  27.     function TitleBarOnScreen (wp: WindowPtr): boolean;
  28.     procedure GetBestScreenRect(windowBounds: rect; var screenRect:Rect; var on_main_device:boolean);
  29.     function IsWindowShaded(wp: WindowPtr): boolean;
  30.     function WindowInWindowList (w: windowPtr): boolean;
  31.  
  32. implementation
  33.  
  34. { Based on code by Dean Yu in Develop 17 }
  35. { Changes: }
  36. { Converted to Pascal }
  37. { Pass in desired window size instead of a ProcPtr to return the desired window size }
  38. { Removed use of DeviceLoop }
  39. {   (DeviceLoop is System 7 dependent, and doesn't work in THINK Pascal anyway due to a bug in the interfaces }
  40. { Improved to handle zooming windows before they are made visible (since struct and content rgn's are empty) }
  41.  
  42.     uses
  43.         Script, LowMem, MyTypes, MySystemGlobals;
  44.  
  45.     const
  46.         kNudgeSlop = 4;
  47.         kIconSpace = 64;
  48.  
  49. { WindowRecord accessor functions }
  50.  
  51.     function GetWindowContentRegion (theWindow: WindowPtr): RgnHandle;
  52.     begin
  53.         GetWindowContentRegion := WindowPeek(theWindow)^.contRgn;
  54.     end;
  55.  
  56.     function GetWindowStructureRegion (theWindow: WindowPtr): RgnHandle;
  57.     begin
  58.         GetWindowStructureRegion := WindowPeek(theWindow)^.strucRgn;
  59.     end;
  60.  
  61.     procedure GetWindowPortRect (theWindow: WindowPtr; var portRect: rect);
  62.     begin
  63.         portRect := WindowPeek(theWindow)^.port.portRect;
  64.     end;
  65.  
  66.     function GetWindowVisible (theWindow: WindowPtr): boolean;
  67.     begin
  68.         GetWindowVisible := WindowPeek(theWindow)^.visible;
  69.     end;
  70.  
  71.     procedure GetWindowStandardState (theWindow: WindowPtr; var standardState: Rect);
  72.     begin
  73.         standardState := WStateDataHandle(WindowPeek(theWindow)^.dataHandle)^^.stdState;
  74.     end;
  75.  
  76.     procedure SetWindowStandardState (theWindow: WindowPtr; standardState: Rect);
  77.     begin
  78.         WStateDataHandle(WindowPeek(theWindow)^.dataHandle)^^.stdState := standardState;
  79.     end;
  80.  
  81.     procedure GetWindowUserState (theWindow: WindowPtr; var userState: Rect);
  82.     begin
  83.         userState := WStateDataHandle(WindowPeek(theWindow)^.dataHandle)^^.userState;
  84.     end;
  85.  
  86.     procedure SetWindowUserState (theWindow: WindowPtr; userState: Rect);
  87.     begin
  88.         WStateDataHandle(WindowPeek(theWindow)^.dataHandle)^^.userState := userState;
  89.     end;
  90.  
  91.     procedure GetWindowRect (theWindow: WindowPtr; var r: rect);
  92.     begin
  93.         SetPort(theWindow);
  94.         GetWindowPortRect(theWindow, r);
  95.         LocalToGlobal(r.topleft);
  96.         LocalToGlobal(r.botright);
  97.     end;
  98.  
  99.     procedure SetWindowRect (theWindow: WindowPtr; var r: rect);
  100.     begin
  101.         MoveWindow(theWindow,r.left,r.top,false);
  102.         SizeWindow(theWindow,r.right-r.left,r.bottom-r.top,true);
  103.     end;
  104.  
  105.     procedure GetWindowPosition(theWindow:WindowPtr; var pos:Point);
  106.         var
  107.             r:Rect;
  108.     begin
  109.         SetPort(theWindow);
  110.         GetWindowPortRect(theWindow, r);
  111.         pos:=r.topleft;
  112.         LocalToGlobal(pos);
  113.     end;
  114.     
  115.     procedure SetWindowPosition(theWindow:WindowPtr; pos:Point);
  116.         var
  117.             r:Rect;
  118.     begin
  119.         SetPort(theWindow);
  120.         GetWindowPortRect(theWindow, r);
  121.         OffsetRect(r,-r.left+pos.h,-r.top+pos.v);
  122.         SetWindowRect(theWindow,r);
  123.     end;
  124.     
  125.     function GetBestDevice (windowBounds: rect): GDHandle;
  126.         var
  127.             thisGD, bestGD: GDHandle;
  128.             thisArea, bestArea: longInt;
  129.             thisBounds: rect;
  130.             dummy: boolean;
  131.     begin
  132.         thisGD := GetDeviceList;
  133.         bestArea := 0;
  134.         bestGD := GetMainDevice;
  135.         while thisGD <> nil do begin
  136.             if TestDeviceAttribute(thisGD, screenDevice) & TestDeviceAttribute(thisGD, screenActive) then begin
  137.                 dummy := SectRect(windowBounds, thisGD^^.gdRect, thisBounds);
  138.                 thisArea := longInt(thisBounds.right - thisBounds.left) * longInt(thisBounds.bottom - thisBounds.top);
  139.                 if thisArea > bestArea then begin
  140.                     bestGD := thisGD;
  141.                     bestArea := thisArea;
  142.                 end;
  143.             end;
  144.             thisGD := GetNextDevice(thisGD);
  145.         end;
  146.         GetBestDevice := bestGD;
  147.     end;
  148.  
  149.     procedure GetBestScreenRect(windowBounds: rect; var screenRect:Rect; var on_main_device:boolean);
  150.     { NOTE: screenRect will not include the menu bar }
  151.         var
  152.             screenWithLargestPartOfWindow: GDHandle;        
  153.     begin
  154.         if has_colorQD then begin
  155.             screenWithLargestPartOfWindow := GetBestDevice(windowBounds);
  156.             screenRect := screenWithLargestPartOfWindow^^.gdRect;
  157.             on_main_device := GetMainDevice = screenWithLargestPartOfWindow;
  158.         end
  159.         else begin
  160.             screenRect := GetQDGlobals^.screenBits.bounds;
  161.             on_main_device := true;
  162.         end;
  163.         if on_main_device then begin
  164.             screenRect.top := screenRect.top + LMGetMBarHeight;
  165.         end;
  166.     end;
  167.  
  168. { Figure out how much we need to move the window to get it entirely on the monitor.  If }
  169. { the window wouldn’t fit completely on the monitor anyway, don’t move it at all; we’ll }
  170. { make it fit later on. }
  171.  
  172.     function CalculateOffsetAmount (idealStartPoint, idealEndPoint, idealOnScreenStartPoint, idealOnScreenEndPoint, screenEdge1, screenEdge2: integer): integer;
  173.         var
  174.             offsetAmount: integer;
  175.     begin
  176.     { First check to see if the window fits on the screen in this dimension. }
  177.         if (idealStartPoint < screenEdge1) & (idealEndPoint > screenEdge2) then begin
  178.             offsetAmount := 0;
  179.         end
  180.         else begin
  181.  
  182.         { Find out how much of the window lies off this screen by subtracting the amount of the window }
  183.         { that is on the screen from the size of the entire window in this dimension. If the window }
  184.         { is completely offscreen, the offset amount is going to be the distance from the ideal }
  185.         { starting point to the first edge of the screen. }
  186.             if idealOnScreenStartPoint - idealOnScreenEndPoint = 0 then begin
  187.             { See if the window is lying to the left or above the screen }
  188.                 if idealEndPoint < screenEdge1 then begin
  189.                     offsetAmount := screenEdge1 - idealStartPoint + kNudgeSlop;
  190.                 end
  191.                 else begin
  192.             { Otherwise, it’s below or to the right of the screen }
  193.                     offsetAmount := screenEdge2 - idealEndPoint - kNudgeSlop;
  194.                 end;
  195.             end
  196.             else begin
  197.             { Window is already partially or completely on the screen }
  198.                 offsetAmount := (idealEndPoint - idealStartPoint) - (idealOnScreenEndPoint - idealOnScreenStartPoint);
  199.  
  200.             { If we are offscreen a little, move the window in a few more pixels from the edge of the screen. }
  201.                 if offsetAmount <> 0 then begin
  202.                     offsetAmount := offsetAmount + kNudgeSlop;
  203.                 end;
  204.  
  205.             { Check to see which side of the screen the window was falling off of, so that it can be }
  206.             { nudged in the opposite direction. }
  207.                 if idealEndPoint > screenEdge2 then begin
  208.                     offsetAmount := -offsetAmount;
  209.                 end;
  210.             end;
  211.         end;
  212.  
  213.         CalculateOffsetAmount := offsetAmount;
  214.     end;
  215.  
  216.     procedure AddRect (r1, r2: rect; var r: rect);
  217.     begin
  218.         r.top := r1.top + r2.top;
  219.         r.bottom := r1.bottom + r2.bottom;
  220.         r.left := r1.left + r2.left;
  221.         r.right := r1.right + r2.right;
  222.     end;
  223.  
  224.     procedure SubRect (r1, r2: rect; var r: rect);
  225.     begin
  226.         r.top := r1.top - r2.top;
  227.         r.bottom := r1.bottom - r2.bottom;
  228.         r.left := r1.left - r2.left;
  229.         r.right := r1.right - r2.right;
  230.     end;
  231.  
  232.     procedure ZoomWindowOut (theWindow: WindowPtr; titlebar_height:integer; idealsize: point);
  233.         var
  234.             windowBounds: rect;
  235.             newStandardRect: rect;
  236.             scratchRect: rect;
  237.             screenRect: rect;
  238.             portRect: rect;
  239.             contentRegionBoundingBox: rect;
  240.             structureRegionBoundingBox: rect;
  241.             scratchRegion: RgnHandle;
  242.             structureRegion: RgnHandle;
  243.             contentRegion: RgnHandle;
  244.             on_main_device: boolean;
  245.             horizontalAmountOffScreen: integer;
  246.             verticalAmountOffScreen: integer;
  247.             windowFrame: rect;
  248.             dummy: boolean;
  249.             orgrect: rect;
  250.     begin
  251.         SetPort(theWindow);
  252.  
  253.         GetWindowRect(theWindow, orgrect);
  254.  
  255.         contentRegion := GetWindowContentRegion(theWindow);
  256.         structureRegion := GetWindowStructureRegion(theWindow);
  257.         GetWindowPortRect(theWindow, portRect);
  258.  
  259. { If the window is invisible (or at least initially before it is ever made visible), then the content and structure }
  260. { regions will be empty.  In this case, we fake it out by using the portRect as the content region and 18 (hardcoded) }
  261. { as the titlebar height }
  262.         if EmptyRgn(structureRegion) then begin
  263.             scratchRect := portRect;
  264.             LocalToGlobal(scratchRect.topleft);
  265.             LocalToGlobal(scratchRect.botright);
  266.             contentRegionBoundingBox := scratchRect;
  267.             scratchRect.top := scratchRect.top - titlebar_height; { No other way of figuring out the window frame }
  268.             structureRegionBoundingBox := scratchRect;
  269.         end
  270.         else begin
  271.             contentRegionBoundingBox := contentRegion^^.rgnBBox;
  272.             structureRegionBoundingBox := structureRegion^^.rgnBBox;
  273.         end;
  274.  
  275.     { Determine the size of the window frame }
  276.         windowFrame.top := structureRegionBoundingBox.top - contentRegionBoundingBox.top;
  277.         windowFrame.left := structureRegionBoundingBox.left - contentRegionBoundingBox.left;
  278.         windowFrame.right := structureRegionBoundingBox.right - contentRegionBoundingBox.right;
  279.         windowFrame.bottom := structureRegionBoundingBox.bottom - contentRegionBoundingBox.bottom;
  280.  
  281.     { If the window is being zoomed into the standard state, calculate the best size }
  282.     { to display the window’s information. }
  283.         { Usually, we would use the content region’s bounding box to determine the monitor }
  284.         { with largest portion of the window’s area. However, if the entire content region }
  285.         { of the window is not on any screen, the structure region should be used instead. }
  286.         windowBounds := contentRegionBoundingBox;
  287.         scratchRegion := NewRgn;
  288.         RectRgn(scratchRegion, windowBounds);
  289.         SectRgn(GetGrayRgn, scratchRegion, scratchRegion);
  290.         if EmptyRgn(scratchRegion) then begin
  291.             windowBounds := structureRegionBoundingBox;
  292.         end;
  293.         DisposeRgn(scratchRegion);
  294.  
  295.         GetBestScreenRect(windowBounds,screenRect,on_main_device);
  296.  
  297.         { Go figure out the perfect size for the window as if we had an infinitely large }
  298.         { screen }
  299.     {    (*calcRoutine)((WindowPtr) theWindow, &newStandardRect);}
  300.         SetRect(newStandardRect, 0, 0, idealsize.h, idealsize.v);
  301.  
  302.         { Anchor the new rectangle at the window’s current top left corner }
  303.         { OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top); }
  304.         OffsetRect(newStandardRect, orgrect.left, orgrect.top);
  305.  
  306.         { newStandardRect is the ideal size for the content area. The window frame }
  307.         { needs to be accounted for when we see if the window needs to be moved, }
  308.         { or resized, so add in the dimensions of the window frame.}
  309.         AddRect(newStandardRect, windowFrame, newStandardRect);
  310.  
  311. {        { If the new rectangle falls off the edge of the screen, nudge it so that it’s just }
  312.         { on the screen. CalculateOffsetAmount determines how much of the window is offscreen. }
  313.         dummy := SectRect(newStandardRect, screenRect, scratchRect);
  314.         if not EqualRect(newStandardRect, scratchRect) then begin
  315.             horizontalAmountOffScreen := CalculateOffsetAmount(newStandardRect.left, newStandardRect.right, scratchRect.left, scratchRect.right, screenRect.left, screenRect.right);
  316.             verticalAmountOffScreen := CalculateOffsetAmount(newStandardRect.top, newStandardRect.bottom, scratchRect.top, scratchRect.bottom, screenRect.top, screenRect.bottom);
  317.             OffsetRect(newStandardRect, horizontalAmountOffScreen, verticalAmountOffScreen);
  318.         end;
  319.  
  320.         { If we’re still falling off the edge of the screen, that means that the perfect }
  321.         { size is larger than the screen, so we need to shrink down the standard size }
  322.         dummy := SectRect(newStandardRect, screenRect, scratchRect);
  323.         if not EqualRect(newStandardRect, scratchRect) then begin
  324.  
  325.         { First shrink the width of the window. If the window is wider than the screen }
  326.         { it is zooming to, we can just pin the standard rectangle to the edges of the }
  327.         { screen, leaving some slop. If the window is narrower than the screen, we know }
  328.         { we just nudged it into position, so nothing needs to be done. }
  329.             if newStandardRect.right - newStandardRect.left > screenRect.right - screenRect.left then begin
  330.                 newStandardRect.left := screenRect.left + kNudgeSlop;
  331.  
  332.                 if (on_main_device) then begin
  333.                     newStandardRect.right := screenRect.right - kIconSpace;
  334.                 end
  335.                 else begin
  336.                     newStandardRect.right := screenRect.right - kNudgeSlop;
  337.                 end;
  338.             end;
  339.  
  340.             { Move in the top. Like the width of the window, nothing needs to be done unless }
  341.             { the window is taller than the height of the screen. }
  342.             if newStandardRect.bottom - newStandardRect.top > screenRect.bottom - screenRect.top then begin
  343.                 newStandardRect.top := screenRect.top + kNudgeSlop;
  344.                 newStandardRect.bottom := screenRect.bottom - kNudgeSlop;
  345.             end;
  346.         end;
  347.  
  348.         { We’ve got the best possible window position. Remove the }
  349.         { frame, slam it into the WStateData record and let ZoomWindow }
  350.         { take care of the rest. }
  351.         SubRect(newStandardRect, windowFrame, newStandardRect);
  352.  
  353.         if (newStandardRect.left = orgrect.left) & (newStandardRect.top = orgrect.top) then begin
  354.             SizeWindow(theWindow, newStandardRect.right - newStandardRect.left, newStandardRect.bottom - newStandardRect.top, true);
  355.         end
  356.         else begin
  357.             SetWindowRect(theWindow, newStandardRect);
  358.         end;
  359. { If the window is still anchored at the current location, then just resize it }
  360.     end;
  361.  
  362.     procedure ZoomTheWindow (theWindow: WindowPtr; zoomout: boolean; idealsize: point; var unzoomed: rect);
  363.     begin
  364.         SetPort(theWindow);
  365.         if zoomout then begin
  366.             GetWindowRect(theWindow, unzoomed);
  367.             ZoomWindowOut(theWindow, document_titlebar_height, idealsize);
  368.         end
  369.         else begin
  370.             SetWindowRect(theWindow, unzoomed);
  371.         end;
  372.     end;
  373.  
  374.     function TitleBarOnScreen (wp: WindowPtr): boolean;
  375.         var
  376.             rgn: RgnHandle;
  377.     begin
  378.         rgn := NewRgn;
  379.         CopyRgn(GetWindowStructureRegion(wp), rgn);
  380.         DiffRgn(rgn, GetWindowContentRegion(wp), rgn);
  381.         SectRgn(rgn, GetGrayRgn, rgn);
  382.         TitleBarOnScreen := not EmptyRgn(rgn);
  383.         DisposeRgn(rgn);
  384.     end;
  385.  
  386.     function IsWindowShaded(wp: WindowPtr): boolean;
  387.     begin
  388.         IsWindowShaded := EmptyRgn(GetWindowContentRegion(wp));
  389.     end;
  390.     
  391.     function WindowInWindowList (w: windowPtr): boolean;
  392.         type
  393.             windowPtrPtr = ^windowPtr;
  394.         var
  395.             nw: windowPtr;
  396.     begin
  397.         nw := windowPtrPtr($9D6)^;
  398.         while (nw <> nil) & (w <> nw) do begin
  399.             nw := windowPtr(windowPeek(nw)^.nextwindow);
  400.         end;
  401.         WindowInWindowList := nw <> nil;
  402.     end;
  403.  
  404. end.