home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / macfe / utility / CSaveWindowStatus.cp < prev    next >
Encoding:
Text File  |  1998-04-08  |  13.7 KB  |  391 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*======================================================================================
  20.     AUTHOR:            Ted Morris <tmorris@netscape.com> - 08 NOV 96
  21.  
  22.     MODIFICATIONS:
  23.  
  24.     Date            Person            Description
  25.     ----            ------            -----------
  26. ======================================================================================*/
  27.  
  28.  
  29. /*====================================================================================*/
  30.     #pragma mark INCLUDE FILES
  31. /*====================================================================================*/
  32.  
  33. #include "CSaveWindowStatus.h"
  34.  
  35. #include "uprefd.h"
  36. #include "macutil.h"
  37.  
  38.  
  39. #pragma mark -
  40. /*====================================================================================*/
  41.     #pragma mark TYPEDEFS
  42. /*====================================================================================*/
  43.  
  44.  
  45. #pragma mark -
  46. /*====================================================================================*/
  47.     #pragma mark CONSTANTS
  48. /*====================================================================================*/
  49.  
  50.  
  51. #pragma mark -
  52. /*====================================================================================*/
  53.     #pragma mark INTERNAL FUNCTION PROTOTYPES
  54. /*====================================================================================*/
  55.  
  56.  
  57. #pragma mark -
  58. /*====================================================================================*/
  59.     #pragma mark INTERNAL CLASS DECLARATIONS
  60. /*====================================================================================*/
  61.  
  62.  
  63. #pragma mark -
  64. /*====================================================================================*/
  65.     #pragma mark CLASS IMPLEMENTATIONS
  66. /*====================================================================================*/
  67.  
  68. #pragma mark -
  69. /*======================================================================================
  70.     Find the global bounds for the pane.
  71. ======================================================================================*/
  72.  
  73. void CSaveWindowStatus::GetPaneGlobalBounds(LPane *inPane, Rect *outBounds) {
  74.  
  75.     inPane->CalcPortFrameRect(*outBounds);
  76.     inPane->PortToGlobalPoint(topLeft(*outBounds));
  77.     inPane->PortToGlobalPoint(botRight(*outBounds));
  78. }
  79.  
  80.  
  81. /*======================================================================================
  82.     Move this window to an alert position on the main screen.
  83. ======================================================================================*/
  84.  
  85. void CSaveWindowStatus::MoveWindowToAlertPosition(LWindow *inWindow) {
  86.  
  87.     Rect bounds;
  88.     
  89.     GetPaneGlobalBounds(inWindow, &bounds);
  90.     
  91.     GDHandle dominantDevice;
  92.     LWindow *frontWindowP = UDesktop::FetchTopRegular();
  93.     
  94.     if ( frontWindowP ) {
  95.         dominantDevice = UWindows::FindDominantDevice(
  96.                             UWindows::GetWindowStructureRect(frontWindowP->GetMacPort()));
  97.     } else {
  98.         dominantDevice = ::GetMainDevice();
  99.     }
  100.     
  101.     Rect screenRect = (**dominantDevice).gdRect;
  102.     if ( dominantDevice == ::GetMainDevice() ) {
  103.         screenRect.top += GetMBarHeight();
  104.     }
  105.  
  106.     ::OffsetRect(&bounds, screenRect.left + ((screenRect.right - screenRect.left - (bounds.right - bounds.left)) / 2) - bounds.left,
  107.                           screenRect.top + ((screenRect.bottom - screenRect.top - (bounds.bottom - bounds.top)) / 3) - bounds.top);
  108.     
  109.     inWindow->DoSetBounds(bounds);
  110. }
  111.  
  112.  
  113. /*======================================================================================
  114.     Move this window to a new position on the main screen.
  115. ======================================================================================*/
  116.  
  117. void CSaveWindowStatus::MoveWindowTo(LWindow *inWindow, Point inGlobalTopLeft) {
  118.  
  119.     Rect bounds;
  120.     GetPaneGlobalBounds(inWindow, &bounds);
  121.     
  122.     ::OffsetRect(&bounds, inGlobalTopLeft.h - bounds.left, 
  123.                           inGlobalTopLeft.v - bounds.top);
  124.     
  125.     VerifyWindowBounds(inWindow, &bounds);
  126.     inWindow->DoSetBounds(bounds);
  127. }
  128.  
  129.  
  130. /*======================================================================================
  131.     Verify the specified bounds against the window min/max sizes and the desktop
  132.     bounds.
  133. ======================================================================================*/
  134. static
  135. inline
  136. Int16
  137. pin( const Int16& lo, const Int16& x, const Int16& hi )
  138.     {
  139.         return (x<lo) ? lo : ((hi<x) ? hi : x);
  140.     }
  141.  
  142.  
  143. void CSaveWindowStatus::VerifyWindowBounds(LWindow *inWindow, Rect *ioGlobalBounds)
  144.     /*
  145.         Make sure the entire window is on-screen [for roughly rectangular desktops].
  146.  
  147.         |ioGlobalBounds| is the proposed content rect of the window, in global coordinates.  It
  148.         is not necessarily related to the current actual content rect of the window.
  149.     */
  150. {
  151.             /*
  152.                 First, calculate the rectangle within which the windows content rect must fit,
  153.                 for that window to be entirely on-screen.  Note: we explicitly don't handle the
  154.                 ╘funny desktop╒ case.  This may be required.
  155.             */
  156.         Int16 desktopMinTop, desktopMaxBottom, desktopMaxWidth;
  157.         Int16 desktopMinLeft, desktopMaxRight, desktopMaxHeight;
  158.         {
  159.                 // The bounding box of ╘the gray region╒ is the rectangle we want our window to fit entirely within.
  160.             Rect desktopRect = (**::GetGrayRgn()).rgnBBox;
  161.  
  162.                 // Calculate how much the windows structure region ╘pads╒ its content region...
  163.             Rect structureRect    = UWindows::GetWindowStructureRect(inWindow->GetMacPort());
  164.             Rect contentRect        = UWindows::GetWindowContentRect(inWindow->GetMacPort());
  165.  
  166.             Int16    topPadding        = contentRect.top - structureRect.top;
  167.             Int16 bottomPadding    = structureRect.bottom - contentRect.bottom;
  168.             Int16    leftPadding        = contentRect.left - structureRect.left;
  169.             Int16    rightPadding    = structureRect.right - contentRect.right;
  170.  
  171.                 // ...and shrink |desktopRect| accordingly (so it is relative to our content rect, instead of our structure rect).
  172.             desktopRect.top            += cWindowDesktopMargin + topPadding;
  173.             desktopRect.bottom    -= cWindowDesktopMargin + bottomPadding;
  174.             desktopRect.left        += cWindowDesktopMargin + leftPadding;
  175.             desktopRect.right        -= cWindowDesktopMargin + rightPadding;
  176.  
  177.             desktopMinTop            = desktopRect.top;                                        // windows content rect top must not be above this this
  178.             desktopMaxBottom    = desktopRect.bottom;                                    //    ...nor its bottom below this
  179.             desktopMaxHeight    = desktopMaxBottom - desktopMinTop;        //    ...nor its height greater than this
  180.  
  181.             desktopMinLeft        = desktopRect.left;                                        //    ...
  182.             desktopMaxRight        = desktopRect.right;
  183.             desktopMaxWidth        = desktopMaxRight - desktopMinLeft;
  184.         }
  185.  
  186.             /*
  187.                 Second, calculate the minimum and maximum size of the window based on its own min/max settings
  188.                 and the limits imposed by the screen.
  189.             */
  190.         Int16 minHeight, maxHeight, minWidth, maxWidth;
  191.         {
  192.             Rect windowLimits;
  193.             inWindow->GetMinMaxSize(windowLimits);
  194.  
  195.             minHeight = pin(0,                    windowLimits.top,            desktopMaxHeight);
  196.             maxHeight = pin(minHeight,    windowLimits.bottom,    desktopMaxHeight);
  197.             minWidth    = pin(0,                    windowLimits.left,        desktopMaxWidth);
  198.             maxWidth    = pin(minWidth,        windowLimits.right,        desktopMaxWidth);
  199.         }
  200.  
  201.             /*
  202.                 Third, pin the windows size to its calculated limits.
  203.             */
  204.         Int16 height    = pin(minHeight, ioGlobalBounds->bottom-ioGlobalBounds->top, maxHeight);
  205.         Int16 width        = pin(minWidth, ioGlobalBounds->right-ioGlobalBounds->left, maxWidth);
  206.  
  207.  
  208.             /*
  209.                 Fourth (and finally), now that we know its size, pin its location onto the screen, and
  210.                 stuff the calculated results back into |ioGlobalBounds|.
  211.             */
  212.         ioGlobalBounds->top            = pin(desktopMinTop, ioGlobalBounds->top, desktopMaxBottom - height);
  213.         ioGlobalBounds->left        = pin(desktopMinLeft, ioGlobalBounds->left, desktopMaxRight - width);
  214.  
  215.         ioGlobalBounds->bottom    = ioGlobalBounds->top + height;
  216.         ioGlobalBounds->right        = ioGlobalBounds->left + width;
  217. }
  218.  
  219.  
  220. /*======================================================================================
  221.     Better create window with saved status ID.
  222. ======================================================================================*/
  223.  
  224. LWindow *CSaveWindowStatus::CreateWindow(ResIDT inWindowID, LCommander *inSuperCommander) {
  225.  
  226.     LCommander::SetDefaultCommander(inSuperCommander);
  227.     LAttachable::SetDefaultAttachable(nil);
  228.  
  229.     LWindow    *theWindow = (LWindow *) UReanimator::ReadObjects('PPob', inWindowID);
  230.     
  231.     try
  232.     {
  233.         FailNIL_(theWindow);
  234.         theWindow->FinishCreate();
  235.         if ( theWindow->HasAttribute(windAttr_ShowNew) ) {
  236.             theWindow->Show();
  237.         }
  238.     }
  239.     catch(...)
  240.     {
  241.         delete theWindow;
  242.         throw;
  243.     } 
  244.     return theWindow;
  245. }
  246.  
  247. /*======================================================================================
  248.     Do basic initialization of the window. Should be called from the window's 
  249.     FinishCreateSelf().
  250. ======================================================================================*/
  251.  
  252. void CSaveWindowStatus::FinishCreateWindow()
  253. {
  254.     Handle statusInfoH = CPrefs::ReadWindowData(GetStatusResID());
  255.     Boolean doAdjustNIL = true;
  256.     
  257.     try {
  258.     
  259.         // Validate stored state info
  260.         if ( statusInfoH ) {
  261.             LHandleStream statusStream(statusInfoH);
  262.             UInt16 bogusVersion, version;
  263.             // As of 97/10/13, we are writing out zero as the first version data,
  264.             // because Communicator 4.0x was checking version >= GetValidStatusVersion(),
  265.             // and thus trying to decode future versions of the status, and crashing.
  266.             statusStream >> bogusVersion; // and discard this zero.
  267.             statusStream >> version;
  268.             if ( bogusVersion == 0 && version == GetValidStatusVersion() ) {
  269.                 doAdjustNIL = false;
  270.                 ReadWindowStatus(&statusStream);
  271.             }
  272.         }
  273.     }
  274.     catch ( ExceptionCode inErr) {
  275.         
  276.         Assert_(false); // Just catch it, don't do anything!
  277.     }
  278.     catch ( OSErr inErr ) { }        // probably bad status version. Again, do nothing
  279.     
  280.     if ( doAdjustNIL ) ReadWindowStatus(nil);
  281. }
  282.  
  283. void CSaveWindowStatus::FinishCreateWindow(CSaveWindowStatus* inTemplateWindow)
  284. {
  285.     ThrowIfNot_(inTemplateWindow->GetStatusResID() == GetStatusResID());
  286.     ThrowIfNot_(inTemplateWindow->GetValidStatusVersion() == GetValidStatusVersion());
  287.     LHandleStream statusStream;
  288.     inTemplateWindow->WriteWindowStatus(&statusStream);
  289.     statusStream.SetMarker(0, streamFrom_Start);
  290.     this->ReadWindowStatus(&statusStream);
  291.     // Stagger.
  292.     Rect bounds;
  293.     GetPaneGlobalBounds(mWindowSelf, &bounds);
  294.     OffsetRect(&bounds, 20, 20);
  295.     VerifyWindowBounds(mWindowSelf, &bounds);
  296.     mWindowSelf->DoSetBounds(bounds);
  297. }
  298.  
  299. /*======================================================================================
  300.     Try to close a Window as a result of direct user action. Save window status. Should
  301.     be called from the window's AttemptClose() or DoClose() method as follows:
  302.     
  303.     virtual void        AttemptClose() {
  304.                             AttemptCloseWindow();
  305.                             inherited::AttemptClose();
  306.                         }
  307.     virtual void        DoClose() {
  308.                             AttemptCloseWindow();
  309.                             inherited::DoClose();
  310.                         }
  311. ======================================================================================*/
  312.  
  313. void CSaveWindowStatus::AttemptCloseWindow() {
  314.  
  315.     Assert_(mWindowSelf->GetSuperCommander() != nil);
  316.     
  317.     if ( mWindowSelf->GetSuperCommander()->AllowSubRemoval(mWindowSelf) ) {
  318.         SaveStatusInfo();
  319.     }
  320. }
  321.  
  322.  
  323. /*======================================================================================
  324.     Adjust the window to stored preferences. This method should be overriden in subclasses to 
  325.     read data from the stream as stored preferences. Make sure to call this method
  326.     first.
  327. ======================================================================================*/
  328.  
  329. void CSaveWindowStatus::ReadWindowStatus(LStream *inStatusData)
  330. {
  331.     if ( inStatusData != nil )
  332.     {
  333.         mHasSavedStatus = true;            
  334.  
  335.         Rect bounds;
  336.         *inStatusData >> bounds;
  337.         VerifyWindowBounds(mWindowSelf, &bounds);
  338.         mWindowSelf->DoSetBounds(bounds);
  339.     }
  340.     // Don't center the window in the default case, because not all clients
  341.     // want this.
  342. }
  343.  
  344.  
  345. /*======================================================================================
  346.     Get window stored preferences. This method should be overriden in subclasses to 
  347.     write data to the stream that needs to be stored. Make sure to call this method
  348.     first.
  349. ======================================================================================*/
  350.  
  351. void CSaveWindowStatus::WriteWindowStatus(LStream *outStatusData)
  352. {
  353.     mHasSavedStatus = true;            
  354.     
  355.     Rect bounds;
  356.     GetPaneGlobalBounds(mWindowSelf, &bounds);
  357.  
  358.     *outStatusData << bounds;
  359. }
  360.  
  361.  
  362. /*======================================================================================
  363.     Store window preferences.
  364. ======================================================================================*/
  365.  
  366. void CSaveWindowStatus::SaveStatusInfo() {
  367.  
  368.     // Try to save the settings if the window is visible
  369.     
  370.     if ( !mCanSaveStatus || !mWindowSelf->IsVisible() ) return;
  371.     
  372.     try
  373.     {
  374.         LHandleStream statusStream;
  375.         UInt16 bogusVersion = 0; // so that Communicator 4.0x will not use the data.
  376.         statusStream << bogusVersion;
  377.         statusStream << GetValidStatusVersion();
  378.         
  379.         WriteWindowStatus(&statusStream);
  380.         
  381.         CPrefs::WriteWindowData(statusStream.GetDataHandle(), GetStatusResID());
  382.     }
  383.     catch(...)
  384.     {
  385.         
  386.         Assert_(false); // Just catch it, don't do anything!
  387.     }
  388. }
  389.  
  390.  
  391.