mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-04 16:15:25 +00:00
605 lines
18 KiB
C++
605 lines
18 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.
|
||
*/
|
||
|
||
|
||
/*====================================================================================*/
|
||
#pragma mark INCLUDE FILES
|
||
/*====================================================================================*/
|
||
|
||
#include "CGATabBox.h"
|
||
|
||
#include <LGAPushButton.h>
|
||
#include <UGraphicsUtilities.h>
|
||
#include <UGAColorRamp.h>
|
||
|
||
|
||
/*====================================================================================*/
|
||
#pragma mark TYPEDEFS
|
||
/*====================================================================================*/
|
||
|
||
typedef struct {
|
||
PaneIDT tabPaneID; // ID of the pane associated with the tab
|
||
PaneIDT latentCommanderID; // Last active commander ID, 0 if none
|
||
} TabLatentCommanderT;
|
||
|
||
|
||
/*====================================================================================*/
|
||
#pragma mark CONSTANTS
|
||
/*====================================================================================*/
|
||
|
||
static const Int16 cSelectedTabShiftH = 2;
|
||
static const Int16 cSelectedTabShiftV = 2;
|
||
|
||
|
||
/*====================================================================================*/
|
||
#pragma mark INTERNAL CLASS DECLARATIONS
|
||
/*====================================================================================*/
|
||
|
||
// CGATabBoxTab
|
||
|
||
class CGATabBoxTab : public LGAPushButton {
|
||
|
||
public:
|
||
|
||
enum { class_ID = 'TbBt' };
|
||
CGATabBoxTab(LStream *inStream) :
|
||
LGAPushButton(inStream),
|
||
mSelected(false) {
|
||
}
|
||
|
||
void RefreshHiddenBottom(void);
|
||
|
||
protected:
|
||
|
||
virtual void DrawSelf(void);
|
||
virtual void ClickSelf(const SMouseDownEvent &inMouseDown);
|
||
virtual void BroadcastValueMessage(void);
|
||
|
||
virtual void CalcTitleRect(Rect &outRect);
|
||
virtual void DrawButtonNormalColor(void);
|
||
|
||
Boolean IsSelectedTab(void) {
|
||
return (((CGATabBox *) mSuperView)->GetCurrentTabID() == mPaneID);
|
||
}
|
||
|
||
// Instance variables
|
||
|
||
Boolean mSelected;
|
||
};
|
||
|
||
|
||
|
||
/*====================================================================================*/
|
||
#pragma mark INTERNAL FUNCTION PROTOTYPES
|
||
/*====================================================================================*/
|
||
|
||
|
||
/*====================================================================================*/
|
||
#pragma mark CLASS IMPLEMENTATIONS
|
||
/*====================================================================================*/
|
||
|
||
#pragma mark -
|
||
|
||
/*======================================================================================
|
||
Register PP classes associated with this tab box.
|
||
======================================================================================*/
|
||
|
||
void CGATabBox::RegisterTabBoxClasses(void) {
|
||
|
||
RegisterClass_(CGATabBoxTab);
|
||
RegisterClass_(CGATabBox);
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Constructor.
|
||
======================================================================================*/
|
||
|
||
CGATabBox::CGATabBox(LStream *inStream) :
|
||
LGABox_fixes(inStream),
|
||
LListener(),
|
||
LBroadcaster(),
|
||
mCurrentTabID(0),
|
||
mCurrentPaneID(0),
|
||
mTabLatentCommanders(sizeof(TabLatentCommanderT)) {
|
||
|
||
mHasBorder = true;
|
||
mBorderStyle = borderStyleGA_EmbossedOneBorder;
|
||
mTitlePosition = titlePositionGA_None;
|
||
mTitle[0] = 0;
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Set the currently selected tab.
|
||
======================================================================================*/
|
||
|
||
void CGATabBox::SetCurrentTabID(PaneIDT inTabID) {
|
||
|
||
if ( mCurrentTabID == inTabID ) return;
|
||
Boolean updatePort = false;
|
||
|
||
CGATabBoxTab *lastTab = nil, *newTab = nil;
|
||
LTabGroup *theTabGroup = nil;
|
||
|
||
// Hide old view
|
||
|
||
if ( mCurrentTabID != 0 ) {
|
||
lastTab = GetTab(mCurrentTabID);
|
||
if ( lastTab != nil ) {
|
||
TabBoxCanChangeT canChangeRec = { this, true };
|
||
BroadcastMessage(msg_TabViewCanChange, &canChangeRec);
|
||
if ( canChangeRec.canChange ) {
|
||
if ( lastTab->GetValueMessage() != 0 ) {
|
||
LPane *thePane = FindPaneByID(lastTab->GetValueMessage());
|
||
if ( thePane != nil ) {
|
||
theTabGroup = FindTabGroup();
|
||
StoreLatentTabCommander(dynamic_cast<LView *>(thePane));
|
||
// Switch the window to the current commander so that a tab group
|
||
// doesn't swap commanders as they are hidden
|
||
if ( theTabGroup != nil ) {
|
||
LCommander::SwitchTarget(LWindow::FetchWindowObject(GetMacPort()));
|
||
}
|
||
thePane->Hide();
|
||
}
|
||
}
|
||
lastTab->SetTextTraitsID(GetTextTraitsID() + 1);
|
||
lastTab->RefreshHiddenBottom();
|
||
} else {
|
||
return; // Can't change
|
||
}
|
||
}
|
||
}
|
||
|
||
mCurrentTabID = inTabID;
|
||
|
||
if ( mCurrentTabID != 0 ) {
|
||
newTab = GetTab(mCurrentTabID);
|
||
if ( newTab != nil ) {
|
||
PaneIDT paneID = newTab->GetValueMessage();
|
||
if ( paneID != 0 ) {
|
||
LPane *thePane = FindPaneByID(paneID);
|
||
if ( thePane != nil ) {
|
||
thePane->Show();
|
||
if ( !RestoreLatentTabCommander(dynamic_cast<LView *>(thePane)) &&
|
||
(theTabGroup != nil) ) {
|
||
|
||
//StEmptyVisRgn emptyRgn(GetMacPort()); // Hide any flicker
|
||
LCommander::SwitchTarget(theTabGroup);
|
||
}
|
||
}
|
||
}
|
||
newTab->SetTextTraitsID(GetTextTraitsID());
|
||
newTab->RefreshHiddenBottom();
|
||
BroadcastMessage(msg_TabViewChanged, this);
|
||
}
|
||
}
|
||
|
||
if ( (lastTab != nil) || (newTab != nil) ) {
|
||
if ( lastTab != nil ) lastTab->Draw(nil);
|
||
if ( newTab != nil ) newTab->Draw(nil);
|
||
UpdatePort();
|
||
}
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Calculate the border rect, which will be offset by the bottom of the tabs.
|
||
======================================================================================*/
|
||
|
||
void CGATabBox::CalcBorderRect(Rect &outRect) {
|
||
|
||
LGABox_fixes::CalcBorderRect(outRect);
|
||
|
||
::InsetRect(&outRect, 1, 1); // For border
|
||
|
||
Rect tabRect;
|
||
CalcCurrentTabRect(tabRect);
|
||
|
||
outRect.top = tabRect.bottom - 4;
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Calculate the current tab rect in local coordinates.
|
||
======================================================================================*/
|
||
|
||
void CGATabBox::CalcCurrentTabRect(Rect &outRect) {
|
||
|
||
Assert_(mCurrentTabID != 0);
|
||
|
||
if ( mCurrentTabID ) {
|
||
CGATabBoxTab *theTab = GetTab(mCurrentTabID);
|
||
Assert_(theTab != nil);
|
||
if ( theTab != nil ) {
|
||
theTab->CalcPortFrameRect(outRect);
|
||
PortToLocalPoint(topLeft(outRect));
|
||
PortToLocalPoint(botRight(outRect));
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Finish creating the tab box.
|
||
======================================================================================*/
|
||
|
||
void CGATabBox::FinishCreateSelf(void) {
|
||
|
||
LGABox_fixes::FinishCreateSelf();
|
||
|
||
SetupTabs();
|
||
|
||
SetCurrentTabID(GetUserCon());
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Draw the box border.
|
||
======================================================================================*/
|
||
|
||
void CGATabBox::DrawBoxBorder(void) {
|
||
|
||
StColorPenState::Normalize();
|
||
|
||
Rect borderRect;
|
||
CalcBorderRect(borderRect);
|
||
::InsetRect(&borderRect, -1, -1);
|
||
|
||
::FrameRect(&borderRect);
|
||
|
||
LGABox_fixes::DrawBoxBorder();
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Respond to tab messages. Only tabs will broadcast messages to this class, so that's
|
||
all we need to be concerned with.
|
||
======================================================================================*/
|
||
|
||
void CGATabBox::ListenToMessage(MessageT inMessage, void */*ioParam*/) {
|
||
|
||
SetCurrentTabID((PaneIDT) inMessage);
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Called from FinishCreateSelf() to setup the tabs for the view.
|
||
======================================================================================*/
|
||
|
||
void CGATabBox::SetupTabs(void) {
|
||
|
||
// Loop through all subpanes to find the relevant tabs
|
||
|
||
LArrayIterator iterator(mSubPanes, LArrayIterator::from_Start);
|
||
LPane *theSub;
|
||
CGATabBoxTab *theTab = nil;
|
||
Boolean foundTabs = false;
|
||
while ( iterator.Next(&theSub) ) {
|
||
theTab = dynamic_cast<CGATabBoxTab *>(theSub);
|
||
if ( theTab != nil ) {
|
||
if ( theTab->GetValueMessage() != 0 ) {
|
||
PaneIDT paneID = theTab->GetValueMessage();
|
||
LPane *thePane = FindPaneByID(paneID);
|
||
if ( thePane != nil ) {
|
||
thePane->Hide();
|
||
LView *theView = dynamic_cast<LView *>(thePane);
|
||
if ( theView != nil ) {
|
||
// Initialize the last active commander for the tab's pane
|
||
TabLatentCommanderT rec = { paneID, 0 };
|
||
mTabLatentCommanders.InsertItemsAt(1, LArray::index_Last, &rec);
|
||
}
|
||
}
|
||
}
|
||
theTab->SetTextTraitsID(GetTextTraitsID() + 1);
|
||
theTab->AddListener(this);
|
||
foundTabs = true;
|
||
}
|
||
}
|
||
Assert_(foundTabs); // Should have found at least one tab!
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
A new tab view has just been activated. Try to find the last active commander and
|
||
make it the currently active commander. Return true if a valid commander was actually
|
||
found and stored.
|
||
======================================================================================*/
|
||
|
||
Boolean CGATabBox::StoreLatentTabCommander(LView *inTabView) {
|
||
|
||
if ( inTabView == nil ) return false;
|
||
|
||
const Int32 count = mTabLatentCommanders.GetCount();
|
||
if ( count < 1 ) return false;
|
||
|
||
PaneIDT paneID = inTabView->GetPaneID();
|
||
Int32 index = 1;
|
||
|
||
// Try to find the tab pane id in our list
|
||
do {
|
||
if ( ((TabLatentCommanderT *) mTabLatentCommanders.GetItemPtr(index))->tabPaneID == paneID ) {
|
||
break;
|
||
}
|
||
} while ( ++index < count );
|
||
|
||
paneID = 0;
|
||
|
||
if ( index < count ) {
|
||
LCommander *curTarget = LCommander::GetTarget();
|
||
|
||
if ( curTarget != nil ) {
|
||
LPane *thePane = dynamic_cast<LPane *>(curTarget);
|
||
if ( (thePane != nil) && thePane->IsEnabled() &&
|
||
inTabView->FindPaneByID(thePane->GetPaneID()) ) {
|
||
|
||
paneID = thePane->GetPaneID(); // We foud our commander!
|
||
}
|
||
}
|
||
|
||
((TabLatentCommanderT *) mTabLatentCommanders.GetItemPtr(index))->latentCommanderID = paneID;
|
||
}
|
||
|
||
return (paneID != 0);
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
A new tab view has just been activated. Try to find the last active commander and
|
||
make it the currently active commander. Return true if a valid commander was actually
|
||
found and restored.
|
||
======================================================================================*/
|
||
|
||
Boolean CGATabBox::RestoreLatentTabCommander(LView *inTabView) {
|
||
|
||
if ( inTabView == nil ) return false;
|
||
|
||
Int32 count = mTabLatentCommanders.GetCount();
|
||
if ( count < 1 ) return false;
|
||
|
||
PaneIDT paneID = inTabView->GetPaneID();
|
||
Int32 index = 1;
|
||
|
||
// Try to find the tab pane id in our list
|
||
do {
|
||
if ( ((TabLatentCommanderT *) mTabLatentCommanders.GetItemPtr(index))->tabPaneID == paneID ) {
|
||
break;
|
||
}
|
||
} while ( ++index < count );
|
||
|
||
LCommander *theCommander = nil;
|
||
|
||
if ( index < count ) {
|
||
PaneIDT latentCommanderID = ((TabLatentCommanderT *) mTabLatentCommanders.GetItemPtr(index))->latentCommanderID;
|
||
if ( latentCommanderID != 0 ) {
|
||
// We found our view, now see if there was an active commander
|
||
LPane *thePane = inTabView->FindPaneByID(latentCommanderID);
|
||
if ( (thePane != nil) && thePane->IsEnabled() ) {
|
||
theCommander = dynamic_cast<LCommander *>(thePane);
|
||
if ( theCommander != nil ) {
|
||
LCommander::SwitchTarget(theCommander);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return (theCommander != nil);
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Get the specified tab. Return nil if the tab cannot be found.
|
||
======================================================================================*/
|
||
|
||
CGATabBoxTab *CGATabBox::GetTab(PaneIDT inID) {
|
||
|
||
LArrayIterator iterator(mSubPanes, LArrayIterator::from_Start);
|
||
LPane *theSub;
|
||
while ( iterator.Next(&theSub) ) {
|
||
if ( theSub->GetPaneID() == inID ) {
|
||
CGATabBoxTab *theTab = dynamic_cast<CGATabBoxTab *>(theSub);
|
||
if ( theTab != nil ) return theTab;
|
||
}
|
||
}
|
||
|
||
Assert_(false); // Should have found tab!
|
||
return nil;
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Find the tab group for the window if it is currently active or nil if there is not
|
||
one.
|
||
======================================================================================*/
|
||
|
||
LTabGroup *CGATabBox::FindTabGroup(void) {
|
||
|
||
LCommander *theCommander = LCommander::GetTarget();
|
||
|
||
if ( !theCommander ) return nil;
|
||
|
||
LCommander *windowCommander = dynamic_cast<LCommander *>(LWindow::FetchWindowObject(GetMacPort()));
|
||
Assert_(windowCommander != nil);
|
||
|
||
if ( windowCommander == theCommander ) return nil;
|
||
|
||
LTabGroup *lastTabGroup = nil;
|
||
|
||
do {
|
||
LTabGroup *tabGroup = dynamic_cast<LTabGroup *>(theCommander);
|
||
if ( tabGroup != nil ) {
|
||
lastTabGroup = tabGroup;
|
||
}
|
||
} while ( ((theCommander = theCommander->GetSuperCommander()) != nil) &&
|
||
(theCommander != windowCommander) );
|
||
|
||
return lastTabGroup;
|
||
}
|
||
|
||
|
||
#pragma mark -
|
||
|
||
/*======================================================================================
|
||
Refresh just the bottom portion of the button.
|
||
======================================================================================*/
|
||
|
||
void CGATabBoxTab::RefreshHiddenBottom(void) {
|
||
|
||
Rect refreshRect;
|
||
if ( IsVisible() && CalcPortFrameRect(refreshRect) ) {
|
||
refreshRect.top = refreshRect.bottom - 5;
|
||
Rect superRevealed;
|
||
mSuperView->GetRevealedRect(superRevealed);
|
||
if ( ::SectRect(&refreshRect, &superRevealed, &refreshRect) ) {
|
||
InvalPortRect(&refreshRect);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Draw the button except for the bottom edge.
|
||
======================================================================================*/
|
||
|
||
void CGATabBoxTab::DrawSelf(void) {
|
||
|
||
Rect clipRect;
|
||
CalcLocalFrameRect(clipRect);
|
||
|
||
RgnHandle clipRgn = ::NewRgn();
|
||
|
||
if ( IsSelectedTab() ) {
|
||
RgnHandle tempRgn = ::NewRgn();
|
||
clipRect.bottom -= 4;
|
||
::RectRgn(clipRgn, &clipRect);
|
||
::SetRect(&clipRect, clipRect.left + 1, clipRect.bottom, clipRect.right - 4, clipRect.bottom + 1);
|
||
::RectRgn(tempRgn, &clipRect);
|
||
::UnionRgn(clipRgn, tempRgn, clipRgn);
|
||
::DisposeRgn(tempRgn);
|
||
} else {
|
||
clipRect.bottom -= 5;
|
||
::RectRgn(clipRgn, &clipRect);
|
||
}
|
||
|
||
StClipRgnState saveClip(clipRgn);
|
||
::DisposeRgn(clipRgn);
|
||
|
||
LGAPushButton::DrawSelf();
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Clicking atomatically selects.
|
||
======================================================================================*/
|
||
|
||
void CGATabBoxTab::ClickSelf(const SMouseDownEvent &/*inMouseDown*/) {
|
||
|
||
BroadcastValueMessage();
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Broadcast pane id instead of value message.
|
||
======================================================================================*/
|
||
|
||
void CGATabBoxTab::BroadcastValueMessage(void) {
|
||
|
||
MessageT valueMsg = mValueMessage;
|
||
BroadcastMessage(mPaneID, &mValueMessage);
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Offset since hiding a portion of button.
|
||
======================================================================================*/
|
||
|
||
void CGATabBoxTab::CalcTitleRect(Rect &outRect) {
|
||
|
||
LGAPushButton::CalcTitleRect(outRect);
|
||
|
||
if ( IsSelectedTab() ) {
|
||
::OffsetRect(&outRect, 0, -1);
|
||
} else {
|
||
::OffsetRect(&outRect, 0, -2);
|
||
}
|
||
}
|
||
|
||
|
||
/*======================================================================================
|
||
Draw darker if not selected. Direct copy/paste from LGAPushButton except for
|
||
IsSelectedTab() sections.
|
||
======================================================================================*/
|
||
|
||
void CGATabBoxTab::DrawButtonNormalColor(void) {
|
||
|
||
StColorPenState theColorPenState;
|
||
theColorPenState.Normalize();
|
||
|
||
Boolean isSelected = IsSelectedTab();
|
||
|
||
Rect localFrame;
|
||
CalcLocalFrameRect ( localFrame );
|
||
|
||
::RGBForeColor (&UGAColorRamp::GetBlackColor());
|
||
::FrameRoundRect (&localFrame, 8, 8);
|
||
|
||
if ( isSelected ) {
|
||
::RGBForeColor(&UGAColorRamp::GetColor(2));
|
||
} else {
|
||
::RGBForeColor(&UGAColorRamp::GetColor(3));
|
||
}
|
||
::InsetRect(&localFrame, 1, 1);
|
||
::PaintRoundRect(&localFrame, 4, 4);
|
||
::InsetRect(&localFrame, -1, -1);
|
||
|
||
if ( isSelected ) {
|
||
::RGBForeColor(&UGAColorRamp::GetWhiteColor());
|
||
} else {
|
||
::RGBForeColor(&UGAColorRamp::GetColor(1));
|
||
}
|
||
UGraphicsUtilities::TopLeftSide (&localFrame,
|
||
2, // TOP
|
||
2, // LEFT
|
||
3, // BOTTOM
|
||
3 ); // RIGHT
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.left + 3, localFrame.top + 3, &UGAColorRamp::GetWhiteColor());
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.left + 1, localFrame.top + 2, &UGAColorRamp::GetColor(4));
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.left + 2, localFrame.top + 1, &UGAColorRamp::GetColor(4));
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.left + 1, localFrame.bottom - 3, &UGAColorRamp::GetColor(4));
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.left + 2, localFrame.bottom - 2, &UGAColorRamp::GetColor(4));
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.right - 3, localFrame.top + 1, &UGAColorRamp::GetColor(4));
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.right - 2, localFrame.top + 2, &UGAColorRamp::GetColor(4));
|
||
|
||
// <20> SHADOW EDGES
|
||
::RGBForeColor ( &UGAColorRamp::GetColor(8));
|
||
::MoveTo ( localFrame.left + 3, localFrame.bottom - 2 );
|
||
::LineTo ( localFrame.right - 3, localFrame.bottom - 2 );
|
||
::MoveTo ( localFrame.right - 2, localFrame.bottom - 3 );
|
||
::LineTo ( localFrame.right - 2, localFrame.top + 3 );
|
||
::RGBForeColor ( &UGAColorRamp::GetColor(5));
|
||
UGraphicsUtilities::BottomRightSide ( &localFrame,
|
||
3, // TOP
|
||
3, // LEFT
|
||
2, // BOTTOM
|
||
2 ); // RIGHT
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.right - 3, localFrame.bottom - 3, &UGAColorRamp::GetColor(8));
|
||
UGraphicsUtilities::PaintColorPixel ( localFrame.right - 4, localFrame.bottom - 4, &UGAColorRamp::GetColor(5));
|
||
}
|
||
|
||
|