mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
389 lines
13 KiB
C++
389 lines
13 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||
*
|
||
* The contents of this file are subject to the Netscape Public License
|
||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||
* http://www.mozilla.org/NPL/
|
||
*
|
||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||
* for the specific language governing rights and limitations under the
|
||
* NPL.
|
||
*
|
||
* The Initial Developer of this code under the NPL is Netscape
|
||
* Communications Corporation. Portions created by Netscape are
|
||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||
* Reserved.
|
||
*/
|
||
|
||
/*======================================================================================
|
||
AUTHOR: Ted Morris <tmorris@netscape.com> - 08 NOV 96
|
||
|
||
MODIFICATIONS:
|
||
|
||
Date Person Description
|
||
---- ------ -----------
|
||
======================================================================================*/
|
||
|
||
|
||
/*====================================================================================*/
|
||
#pragma mark INCLUDE FILES
|
||
/*====================================================================================*/
|
||
|
||
#include "CSaveWindowStatus.h"
|
||
|
||
#include "uprefd.h"
|
||
#include "macutil.h"
|
||
|
||
|
||
#pragma mark -
|
||
/*====================================================================================*/
|
||
#pragma mark TYPEDEFS
|
||
/*====================================================================================*/
|
||
|
||
|
||
#pragma mark -
|
||
/*====================================================================================*/
|
||
#pragma mark CONSTANTS
|
||
/*====================================================================================*/
|
||
|
||
|
||
#pragma mark -
|
||
/*====================================================================================*/
|
||
#pragma mark INTERNAL FUNCTION PROTOTYPES
|
||
/*====================================================================================*/
|
||
|
||
|
||
#pragma mark -
|
||
/*====================================================================================*/
|
||
#pragma mark INTERNAL CLASS DECLARATIONS
|
||
/*====================================================================================*/
|
||
|
||
|
||
#pragma mark -
|
||
/*====================================================================================*/
|
||
#pragma mark CLASS IMPLEMENTATIONS
|
||
/*====================================================================================*/
|
||
|
||
#pragma mark -
|
||
/*======================================================================================
|
||
Find the global bounds for the pane.
|
||
======================================================================================*/
|
||
|
||
void CSaveWindowStatus::GetPaneGlobalBounds(LPane *inPane, Rect *outBounds) {
|
||
|
||
inPane->CalcPortFrameRect(*outBounds);
|
||
inPane->PortToGlobalPoint(topLeft(*outBounds));
|
||
inPane->PortToGlobalPoint(botRight(*outBounds));
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Move this window to an alert position on the main screen.
|
||
======================================================================================*/
|
||
|
||
void CSaveWindowStatus::MoveWindowToAlertPosition(LWindow *inWindow) {
|
||
|
||
Rect bounds;
|
||
|
||
GetPaneGlobalBounds(inWindow, &bounds);
|
||
|
||
GDHandle dominantDevice;
|
||
LWindow *frontWindowP = UDesktop::FetchTopRegular();
|
||
|
||
if ( frontWindowP ) {
|
||
dominantDevice = UWindows::FindDominantDevice(
|
||
UWindows::GetWindowStructureRect(frontWindowP->GetMacPort()));
|
||
} else {
|
||
dominantDevice = ::GetMainDevice();
|
||
}
|
||
|
||
Rect screenRect = (**dominantDevice).gdRect;
|
||
if ( dominantDevice == ::GetMainDevice() ) {
|
||
screenRect.top += GetMBarHeight();
|
||
}
|
||
|
||
::OffsetRect(&bounds, screenRect.left + ((screenRect.right - screenRect.left - (bounds.right - bounds.left)) / 2) - bounds.left,
|
||
screenRect.top + ((screenRect.bottom - screenRect.top - (bounds.bottom - bounds.top)) / 3) - bounds.top);
|
||
|
||
inWindow->DoSetBounds(bounds);
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Move this window to a new position on the main screen.
|
||
======================================================================================*/
|
||
|
||
void CSaveWindowStatus::MoveWindowTo(LWindow *inWindow, Point inGlobalTopLeft) {
|
||
|
||
Rect bounds;
|
||
GetPaneGlobalBounds(inWindow, &bounds);
|
||
|
||
::OffsetRect(&bounds, inGlobalTopLeft.h - bounds.left,
|
||
inGlobalTopLeft.v - bounds.top);
|
||
|
||
VerifyWindowBounds(inWindow, &bounds);
|
||
inWindow->DoSetBounds(bounds);
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Verify the specified bounds against the window min/max sizes and the desktop
|
||
bounds.
|
||
======================================================================================*/
|
||
static
|
||
inline
|
||
Int16
|
||
pin( const Int16& lo, const Int16& x, const Int16& hi )
|
||
{
|
||
return (x<lo) ? lo : ((hi<x) ? hi : x);
|
||
}
|
||
|
||
|
||
void CSaveWindowStatus::VerifyWindowBounds(LWindow *inWindow, Rect *ioGlobalBounds)
|
||
/*
|
||
Make sure the entire window is on-screen [for roughly rectangular desktops].
|
||
|
||
|ioGlobalBounds| is the proposed content rect of the window, in global coordinates. It
|
||
is not necessarily related to the current actual content rect of the window.
|
||
*/
|
||
{
|
||
/*
|
||
First, calculate the rectangle within which the windows content rect must fit,
|
||
for that window to be entirely on-screen. Note: we explicitly don't handle the
|
||
<09>funny desktop<6F> case. This may be required.
|
||
*/
|
||
Int16 desktopMinTop, desktopMaxBottom, desktopMaxWidth;
|
||
Int16 desktopMinLeft, desktopMaxRight, desktopMaxHeight;
|
||
{
|
||
// The bounding box of <20>the gray region<6F> is the rectangle we want our window to fit entirely within.
|
||
Rect desktopRect = (**::GetGrayRgn()).rgnBBox;
|
||
|
||
// Calculate how much the windows structure region <20>pads<64> its content region...
|
||
Rect structureRect = UWindows::GetWindowStructureRect(inWindow->GetMacPort());
|
||
Rect contentRect = UWindows::GetWindowContentRect(inWindow->GetMacPort());
|
||
|
||
Int16 topPadding = contentRect.top - structureRect.top;
|
||
Int16 bottomPadding = structureRect.bottom - contentRect.bottom;
|
||
Int16 leftPadding = contentRect.left - structureRect.left;
|
||
Int16 rightPadding = structureRect.right - contentRect.right;
|
||
|
||
// ...and shrink |desktopRect| accordingly (so it is relative to our content rect, instead of our structure rect).
|
||
desktopRect.top += cWindowDesktopMargin + topPadding;
|
||
desktopRect.bottom -= cWindowDesktopMargin + bottomPadding;
|
||
desktopRect.left += cWindowDesktopMargin + leftPadding;
|
||
desktopRect.right -= cWindowDesktopMargin + rightPadding;
|
||
|
||
desktopMinTop = desktopRect.top; // windows content rect top must not be above this this
|
||
desktopMaxBottom = desktopRect.bottom; // ...nor its bottom below this
|
||
desktopMaxHeight = desktopMaxBottom - desktopMinTop; // ...nor its height greater than this
|
||
|
||
desktopMinLeft = desktopRect.left; // ...
|
||
desktopMaxRight = desktopRect.right;
|
||
desktopMaxWidth = desktopMaxRight - desktopMinLeft;
|
||
}
|
||
|
||
/*
|
||
Second, calculate the minimum and maximum size of the window based on its own min/max settings
|
||
and the limits imposed by the screen.
|
||
*/
|
||
Int16 minHeight, maxHeight, minWidth, maxWidth;
|
||
{
|
||
Rect windowLimits;
|
||
inWindow->GetMinMaxSize(windowLimits);
|
||
|
||
minHeight = pin(0, windowLimits.top, desktopMaxHeight);
|
||
maxHeight = pin(minHeight, windowLimits.bottom, desktopMaxHeight);
|
||
minWidth = pin(0, windowLimits.left, desktopMaxWidth);
|
||
maxWidth = pin(minWidth, windowLimits.right, desktopMaxWidth);
|
||
}
|
||
|
||
/*
|
||
Third, pin the windows size to its calculated limits.
|
||
*/
|
||
Int16 height = pin(minHeight, ioGlobalBounds->bottom-ioGlobalBounds->top, maxHeight);
|
||
Int16 width = pin(minWidth, ioGlobalBounds->right-ioGlobalBounds->left, maxWidth);
|
||
|
||
|
||
/*
|
||
Fourth (and finally), now that we know its size, pin its location onto the screen, and
|
||
stuff the calculated results back into |ioGlobalBounds|.
|
||
*/
|
||
ioGlobalBounds->top = pin(desktopMinTop, ioGlobalBounds->top, desktopMaxBottom - height);
|
||
ioGlobalBounds->left = pin(desktopMinLeft, ioGlobalBounds->left, desktopMaxRight - width);
|
||
|
||
ioGlobalBounds->bottom = ioGlobalBounds->top + height;
|
||
ioGlobalBounds->right = ioGlobalBounds->left + width;
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Better create window with saved status ID.
|
||
======================================================================================*/
|
||
|
||
LWindow *CSaveWindowStatus::CreateWindow(ResIDT inWindowID, LCommander *inSuperCommander) {
|
||
|
||
LCommander::SetDefaultCommander(inSuperCommander);
|
||
LAttachable::SetDefaultAttachable(nil);
|
||
|
||
LWindow *theWindow = (LWindow *) UReanimator::ReadObjects('PPob', inWindowID);
|
||
|
||
try
|
||
{
|
||
FailNIL_(theWindow);
|
||
theWindow->FinishCreate();
|
||
if ( theWindow->HasAttribute(windAttr_ShowNew) ) {
|
||
theWindow->Show();
|
||
}
|
||
}
|
||
catch(...)
|
||
{
|
||
delete theWindow;
|
||
throw;
|
||
}
|
||
return theWindow;
|
||
}
|
||
|
||
/*======================================================================================
|
||
Do basic initialization of the window. Should be called from the window's
|
||
FinishCreateSelf().
|
||
======================================================================================*/
|
||
|
||
void CSaveWindowStatus::FinishCreateWindow()
|
||
{
|
||
Handle statusInfoH = CPrefs::ReadWindowData(GetStatusResID());
|
||
Boolean doAdjustNIL = true;
|
||
|
||
try {
|
||
|
||
// Validate stored state info
|
||
if ( statusInfoH ) {
|
||
LHandleStream statusStream(statusInfoH);
|
||
UInt16 bogusVersion, version;
|
||
// As of 97/10/13, we are writing out zero as the first version data,
|
||
// because Communicator 4.0x was checking version >= GetValidStatusVersion(),
|
||
// and thus trying to decode future versions of the status, and crashing.
|
||
statusStream >> bogusVersion; // and discard this zero.
|
||
statusStream >> version;
|
||
if ( bogusVersion == 0 && version == GetValidStatusVersion() ) {
|
||
doAdjustNIL = false;
|
||
ReadWindowStatus(&statusStream);
|
||
}
|
||
}
|
||
}
|
||
catch ( ExceptionCode inErr) {
|
||
|
||
Assert_(false); // Just catch it, don't do anything!
|
||
}
|
||
catch ( OSErr inErr ) { } // probably bad status version. Again, do nothing
|
||
|
||
if ( doAdjustNIL ) ReadWindowStatus(nil);
|
||
}
|
||
|
||
void CSaveWindowStatus::FinishCreateWindow(CSaveWindowStatus* inTemplateWindow)
|
||
{
|
||
ThrowIfNot_(inTemplateWindow->GetStatusResID() == GetStatusResID());
|
||
ThrowIfNot_(inTemplateWindow->GetValidStatusVersion() == GetValidStatusVersion());
|
||
LHandleStream statusStream;
|
||
inTemplateWindow->WriteWindowStatus(&statusStream);
|
||
statusStream.SetMarker(0, streamFrom_Start);
|
||
this->ReadWindowStatus(&statusStream);
|
||
// Stagger.
|
||
Rect bounds;
|
||
GetPaneGlobalBounds(mWindowSelf, &bounds);
|
||
OffsetRect(&bounds, 20, 20);
|
||
VerifyWindowBounds(mWindowSelf, &bounds);
|
||
mWindowSelf->DoSetBounds(bounds);
|
||
}
|
||
|
||
/*======================================================================================
|
||
Try to close a Window as a result of direct user action. Save window status. Should
|
||
be called from the window's AttemptClose() or DoClose() method as follows:
|
||
|
||
virtual void AttemptClose() {
|
||
AttemptCloseWindow();
|
||
inherited::AttemptClose();
|
||
}
|
||
virtual void DoClose() {
|
||
AttemptCloseWindow();
|
||
inherited::DoClose();
|
||
}
|
||
======================================================================================*/
|
||
|
||
void CSaveWindowStatus::AttemptCloseWindow() {
|
||
|
||
Assert_(mWindowSelf->GetSuperCommander() != nil);
|
||
|
||
if ( mWindowSelf->GetSuperCommander()->AllowSubRemoval(mWindowSelf) ) {
|
||
SaveStatusInfo();
|
||
}
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Adjust the window to stored preferences. This method should be overriden in subclasses to
|
||
read data from the stream as stored preferences. Make sure to call this method
|
||
first.
|
||
======================================================================================*/
|
||
|
||
void CSaveWindowStatus::ReadWindowStatus(LStream *inStatusData)
|
||
{
|
||
if ( inStatusData != nil )
|
||
{
|
||
mHasSavedStatus = true;
|
||
|
||
Rect bounds;
|
||
*inStatusData >> bounds;
|
||
VerifyWindowBounds(mWindowSelf, &bounds);
|
||
mWindowSelf->DoSetBounds(bounds);
|
||
}
|
||
// Don't center the window in the default case, because not all clients
|
||
// want this.
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Get window stored preferences. This method should be overriden in subclasses to
|
||
write data to the stream that needs to be stored. Make sure to call this method
|
||
first.
|
||
======================================================================================*/
|
||
|
||
void CSaveWindowStatus::WriteWindowStatus(LStream *outStatusData)
|
||
{
|
||
mHasSavedStatus = true;
|
||
|
||
Rect bounds;
|
||
GetPaneGlobalBounds(mWindowSelf, &bounds);
|
||
|
||
*outStatusData << bounds;
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Store window preferences.
|
||
======================================================================================*/
|
||
|
||
void CSaveWindowStatus::SaveStatusInfo()
|
||
{
|
||
if ( !mCanSaveStatus ) return;
|
||
|
||
try
|
||
{
|
||
LHandleStream statusStream;
|
||
UInt16 bogusVersion = 0; // so that Communicator 4.0x will not use the data.
|
||
statusStream << bogusVersion;
|
||
statusStream << GetValidStatusVersion();
|
||
|
||
WriteWindowStatus(&statusStream);
|
||
|
||
CPrefs::WriteWindowData(statusStream.GetDataHandle(), GetStatusResID());
|
||
}
|
||
catch(...)
|
||
{
|
||
|
||
Assert_(false); // Just catch it, don't do anything!
|
||
}
|
||
}
|
||
|
||
|