/* -*- 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. */ #ifdef PowerPlant_PCH #include PowerPlant_PCH #endif #include #include #include #include #include #include #include #include #include #include "CTabControl.h" #include "UStdBevels.h" #include "UGraphicGizmos.h" // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ CTabControl::CTabControl(LStream* inStream) : LControl(inStream), mTabs(sizeof(CTabInstance*)) { *inStream >> mOrientation; *inStream >> mCornerPixels; *inStream >> mBevelDepth; *inStream >> mSpacing; *inStream >> mLeadPixels; *inStream >> mRisePixels; ResIDT theBevelTraitsID; *inStream >> theBevelTraitsID; UGraphicGizmos::LoadBevelTraits(theBevelTraitsID, mActiveColors); *inStream >> theBevelTraitsID; UGraphicGizmos::LoadBevelTraits(theBevelTraitsID, mOtherColors); *inStream >> mTabDescID; *inStream >> mTitleTraitsID; mControlMask = ::NewRgn(); ThrowIfNULL_(mControlMask); mCurrentTab = NULL; mTrackInside = false; mBevelSides.left = true; mBevelSides.top = true; mBevelSides.right = true; mBevelSides.bottom = true; mMinimumSize.h = mMinimumSize.v = -1; } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ CTabControl::~CTabControl() { if (mControlMask != NULL) ::DisposeRgn(mControlMask); CTabInstance* theTab; LArrayIterator theIter(mTabs, LArrayIterator::from_Start); while (theIter.Next(&theTab)) delete theTab; } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::FinishCreateSelf(void) { LControl::FinishCreateSelf(); if (mTabDescID != 0) DoLoadTabs(mTabDescID); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DoLoadTabs(ResIDT inTabDescResID) { Assert_(inTabDescResID != 0); CTabInstance* theTab; LArrayIterator theIter(mTabs, LArrayIterator::from_Start); while (theIter.Next(&theTab)) delete theTab; mTabDescID = inTabDescResID; StResource theTabRes(ResType_TabDescList, mTabDescID); { StHandleLocker theLock(theTabRes); LDataStream theTabStream(*theTabRes.mResourceH, ::GetHandleSize(theTabRes)); Int16 bevelSubCount = theTabStream.GetLength() / sizeof(STabDescriptor); for (Int32 index = 0; index < bevelSubCount; index++) { STabDescriptor theDesc; theTabStream.ReadData(&theDesc, sizeof(STabDescriptor)); CTabInstance* theTab; if (theDesc.width == -2) theTab = new CIconTabInstance(theDesc); else theTab = new CTextTabInstance(theDesc); mTabs.InsertItemsAt(1, LArray::index_Last, &theTab); } } Recalc(); // don't call SetMinValue and SetMaxValue as they may do range-checking which may // not work if the resources haven't set good initial values // this is ok since we are setting all 3 values at the same time mMinValue = 1; mMaxValue = mTabs.GetCount(); #if 0 SetMaxValue(mTabs.GetCount()); SetMinValue(1); #endif SetValue(mValue); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::SetTabEnable(ResIDT inPageResID, Boolean inEnable) { CTabInstance* theTab; LArrayIterator theIter(mTabs, LArrayIterator::from_Start); while (theIter.Next(&theTab)) { if (theTab->mMessage == inPageResID) { if (theTab->mEnabled != inEnable) { theTab->mEnabled = inEnable; FocusDraw(); DrawSelf(); // because it's not sufficient to DrawOneTab(theTab) break; } } } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::Draw(RgnHandle inSuperDrawRgnH) { Rect theFrame; if ((mVisible == triState_On) && CalcPortFrameRect(theFrame) && ((inSuperDrawRgnH == nil) || RectInRgn(&theFrame, inSuperDrawRgnH)) && FocusDraw()) { PortToLocalPoint(topLeft(theFrame)); // Get Frame in Local coords PortToLocalPoint(botRight(theFrame)); if (ExecuteAttachments(msg_DrawOrPrint, &theFrame)) { Boolean bDidDraw = false; StColorPenState thePenSaver; StColorPenState::Normalize(); // Fail safe offscreen drawing StValueChanger okayToFail(gDebugThrow, debugAction_Nothing); try { LGWorld theOffWorld(theFrame, 0, useTempMem); if (!theOffWorld.BeginDrawing()) throw memFullErr; DrawSelf(); theOffWorld.EndDrawing(); theOffWorld.CopyImage(GetMacPort(), theFrame, srcCopy, mControlMask); bDidDraw = true; } catch (...) { Assert_(false); // & draw onscreen } if (!bDidDraw) DrawSelf(); } } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DrawSelf(void) { CTabInstance* theTab; LArrayIterator theIter(mTabs, LArrayIterator::from_Start); while (theIter.Next(&theTab)) { if (theTab != mCurrentTab) // FIX ME!!! we could check to see if the tab's mask intersects // the local update region DrawOneTab(theTab); } Rect theControlFrame; CalcLocalFrameRect(theControlFrame); // Need to draw the current tab last otherwise the // single pixel black border lines won't get drawn right DrawOneTab(mCurrentTab); SetClipForDrawingSides(); DrawSides(); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DrawOneTabBackground( RgnHandle inRegion, Boolean inCurrentTab) { SBevelColorDesc& theTabColors = inCurrentTab ? mActiveColors : mOtherColors; Int16 thePaletteIndex = theTabColors.fillColor; if (mTrackInside) thePaletteIndex--; ::PmForeColor(thePaletteIndex); ::PaintRgn(inRegion); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DrawOneTabFrame(RgnHandle inRegion, Boolean inCurrentTab) { // Draw the tab frame SBevelColorDesc& theTabColors = inCurrentTab ? mActiveColors : mOtherColors; Int16 thePaletteIndex = theTabColors.frameColor; ::PmForeColor(thePaletteIndex); ::FrameRgn(inRegion); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DrawCurrentTabSideClip(RgnHandle inRegion) { // Always only for the current tab ::SetClip(inRegion); ::PmForeColor(mActiveColors.fillColor); ::PaintRect(&mSideClipFrame); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DrawOneTab(CTabInstance* inTab) { StClipRgnState theClipSaver; Rect theControlFrame; CalcLocalFrameRect(theControlFrame); Boolean isCurrentTab = (inTab == mCurrentTab); if (isCurrentTab) { StRegion theTempMask(inTab->mMask); StRegion theSideMask(mSideClipFrame); ::DiffRgn(theTempMask, theSideMask, theTempMask); ::SetClip(theTempMask); } else { StRegion theTempMask(inTab->mMask); ::DiffRgn(theTempMask, mCurrentTab->mMask, theTempMask); ::SetClip(theTempMask); } // This draws the fill pattern in the tab DrawOneTabBackground(inTab->mMask, isCurrentTab); // This just calls FrameRgn on inTab->mMask DrawOneTabFrame(inTab->mMask, isCurrentTab); StColorPenState thePenSaver; thePenSaver.Normalize(); if (isCurrentTab) DrawCurrentTabSideClip(inTab->mMask); // Draw the title before the bevel because the bevel needs to futz // with the clip inTab->DrawTitle(mCurrentTab, mTitleTraitsID); if (mBevelDepth > 0) { ::PenSize(mBevelDepth, mBevelDepth); if (isCurrentTab) { // Draw the top bevel RGBColor theAddColor = {0x4000, 0x4000, 0x4000}; ::RGBForeColor(&theAddColor); ::OpColor(&UGraphicGizmos::sLighter); ::PenMode(subPin); // This runs the pen around the top and left sides DrawTopBevel(inTab); } // Draw the bevel shadow StRegion theShadeRgn(inTab->mShadeFrame); if (inTab != mCurrentTab) ::DiffRgn(theShadeRgn, mCurrentTab->mMask, theShadeRgn); ::SetClip(theShadeRgn); // Set up the colors for the bevel shadow RGBColor theSubColor = {0x4000, 0x4000, 0x4000}; ::RGBForeColor(&theSubColor); ::OpColor(&UGraphicGizmos::sDarker); ::PenMode(subPin); // This runs the pen around the bottom and right sides DrawBottomBevel(inTab, isCurrentTab); } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::Recalc(void) { FocusDraw(); ::SetEmptyRgn(mControlMask); Rect theControlFrame; CalcLocalFrameRect(theControlFrame); mSideClipFrame = theControlFrame; Int16 theExtendFactor = mBevelDepth + mRisePixels; Point theBasePoint; switch (mOrientation) { case eNorthTab: theBasePoint.h = theControlFrame.left + mLeadPixels; theBasePoint.v = theControlFrame.bottom; mBevelSides.bottom = false; mSideClipFrame.top = mSideClipFrame.bottom - theExtendFactor; mSideClipFrame.bottom += mBevelDepth; ::InsetRect(&mSideClipFrame, 1, 0); break; case eEastTab: theBasePoint.h = theControlFrame.left; theBasePoint.v = theControlFrame.top + mLeadPixels; mBevelSides.left = false; mSideClipFrame.right = mSideClipFrame.left + theExtendFactor; mSideClipFrame.left -= mBevelDepth; ::InsetRect(&mSideClipFrame, 0, 1); break; case eSouthTab: theBasePoint.h = theControlFrame.left + mLeadPixels; theBasePoint.v = theControlFrame.top; mBevelSides.top = false; mSideClipFrame.bottom = mSideClipFrame.top + theExtendFactor; mSideClipFrame.top -= mBevelDepth; ::InsetRect(&mSideClipFrame, 1, 0); break; case eWestTab: theBasePoint.h = theControlFrame.right; theBasePoint.v = theControlFrame.top + mLeadPixels; mBevelSides.right = false; mSideClipFrame.left = mSideClipFrame.right - theExtendFactor; mSideClipFrame.right += mBevelDepth; ::InsetRect(&mSideClipFrame, 0, 1); break; } // Set the Title traits becuase we may need to calculate width based on // title dimensions UTextTraits::SetPortTextTraits(mTitleTraitsID); CTabInstance* theTab; LArrayIterator theIter(mTabs, LArrayIterator::from_Start); while (theIter.Next(&theTab)) { theTab->mFrame = theControlFrame; if (theTab->mMask == NULL) theTab->mMask = ::NewRgn(); ThrowIfNULL_(theTab->mMask); Int16 theWidth; // Size to title if we are in a norh or south orientation, and the width is flagged appropriately if (((mOrientation == eNorthTab) || (mOrientation == eSouthTab)) && (theTab->mWidth == -1)) { theWidth = ::StringWidth(theTab->mTitle) + (4 * ::CharWidth('W')); } else theWidth = theTab->mWidth; switch (mOrientation) { case eNorthTab: theTab->mFrame.left = theBasePoint.h; theTab->mFrame.right = theTab->mFrame.left + theWidth; theTab->mFrame.bottom -= mBevelDepth + mRisePixels; theBasePoint.h = theTab->mFrame.right + mSpacing; theTab->mShadeFrame = theTab->mFrame; theTab->mShadeFrame.left = theTab->mShadeFrame.right - (mCornerPixels + mBevelDepth); break; case eEastTab: theTab->mFrame.top = theBasePoint.v; theTab->mFrame.bottom = theTab->mFrame.top + theWidth; // or in this case height theTab->mFrame.left += mBevelDepth + mRisePixels; theBasePoint.v = theTab->mFrame.bottom + mSpacing; theTab->mShadeFrame = theTab->mFrame; theTab->mShadeFrame.top += mCornerPixels + (mBevelDepth - 1); break; case eSouthTab: theTab->mFrame.left = theBasePoint.h; theTab->mFrame.top += mBevelDepth + mRisePixels; theTab->mFrame.right = theTab->mFrame.left + theWidth; theBasePoint.h = theTab->mFrame.right + mSpacing; theTab->mShadeFrame = theTab->mFrame; theTab->mShadeFrame.left += mCornerPixels + (mBevelDepth - 1); break; case eWestTab: theTab->mFrame.top = theBasePoint.v; theTab->mFrame.bottom = theTab->mFrame.top + theWidth; // or in this case height theTab->mFrame.right -= mBevelDepth + mRisePixels; theBasePoint.v = theTab->mFrame.bottom + mSpacing; theTab->mShadeFrame = theTab->mFrame; theTab->mShadeFrame.top = theTab->mShadeFrame.bottom - (mCornerPixels + mBevelDepth); break; } CalcTabMask(theControlFrame, theTab->mFrame, theTab->mMask); ::UnionRgn(mControlMask, theTab->mMask, mControlMask); // Here we conpensate for the 1 pixel frame. Since drawing is done below and to // the left of a pixel, we need to adjust the right and bottom of the frame so // the frame relative drawing will be inside the tab mask region. theTab->mFrame.right--; theTab->mFrame.bottom--; } if ( theTab ) switch ( mOrientation ) { case eNorthTab: case eSouthTab: mMinimumSize.h = theTab->mFrame.right + 8; mMinimumSize.v = theTab->mFrame.bottom - theTab->mFrame.top; break; case eEastTab: case eWestTab: mMinimumSize.h = theTab->mFrame.right - theTab->mFrame.left + 8; mMinimumSize.v = theTab->mFrame.bottom; break; } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::CalcTabMask( const Rect& inControlFrame, const Rect& inTabFrame, RgnHandle ioTabMask) { ::RectRgn(ioTabMask, &inControlFrame); StRegion theTempRgn1, theTempRgn2; // when we subtract 1 from the tab frame, it's to accomodate for the frame // which draws to the left and below the actual pixel switch (mOrientation) { case eNorthTab: ::SetRectRgn(theTempRgn1, inControlFrame.left, inControlFrame.top, inTabFrame.left, inTabFrame.bottom - 1); ::SetRectRgn(theTempRgn2, inTabFrame.right, inControlFrame.top, inControlFrame.right, inTabFrame.bottom - 1); break; case eEastTab: ::SetRectRgn(theTempRgn1, inTabFrame.left + 1, inControlFrame.top, inControlFrame.right, inTabFrame.top); ::SetRectRgn(theTempRgn2, inTabFrame.left + 1, inTabFrame.bottom, inControlFrame.right, inControlFrame.bottom); break; case eSouthTab: ::SetRectRgn(theTempRgn1, inControlFrame.left, inTabFrame.top + 1, inTabFrame.left, inControlFrame.bottom); ::SetRectRgn(theTempRgn2, inTabFrame.right, inTabFrame.top + 1, inControlFrame.right, inControlFrame.bottom); break; case eWestTab: ::SetRectRgn(theTempRgn1, inControlFrame.left, inControlFrame.top, inTabFrame.right - 1, inTabFrame.top); ::SetRectRgn(theTempRgn2, inControlFrame.left, inTabFrame.bottom, inTabFrame.right - 1, inControlFrame.bottom); break; } ::DiffRgn(ioTabMask, theTempRgn1, ioTabMask); ::DiffRgn(ioTabMask, theTempRgn2, ioTabMask); // Knock off the corners of the tab ::OpenRgn(); if ((mOrientation == eWestTab) || (mOrientation == eNorthTab)) { ::MoveTo(inTabFrame.left, inTabFrame.top); ::LineTo(inTabFrame.left + mCornerPixels, inTabFrame.top); ::LineTo(inTabFrame.left, inTabFrame.top + mCornerPixels); ::LineTo(inTabFrame.left, inTabFrame.top); } if ((mOrientation == eNorthTab) || (mOrientation == eEastTab)) { ::MoveTo(inTabFrame.right, inTabFrame.top); ::LineTo(inTabFrame.right, inTabFrame.top + mCornerPixels); ::LineTo(inTabFrame.right - (mCornerPixels + 1), inTabFrame.top); ::LineTo(inTabFrame.right, inTabFrame.top); } if ((mOrientation == eEastTab) || (mOrientation == eSouthTab)) { ::MoveTo(inTabFrame.right, inTabFrame.bottom); ::LineTo(inTabFrame.right - (mCornerPixels + 1), inTabFrame.bottom); ::LineTo(inTabFrame.right, inTabFrame.bottom - (mCornerPixels + 1)); ::LineTo(inTabFrame.right, inTabFrame.bottom); } if ((mOrientation == eSouthTab) || (mOrientation == eWestTab)) { ::MoveTo(inTabFrame.left, inTabFrame.bottom); ::LineTo(inTabFrame.left, inTabFrame.bottom - mCornerPixels); ::LineTo(inTabFrame.left + mCornerPixels, inTabFrame.bottom); ::LineTo(inTabFrame.left, inTabFrame.bottom); } ::CloseRgn(theTempRgn1); ::DiffRgn(ioTabMask, theTempRgn1, ioTabMask); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DrawTopBevel(CTabInstance* inTab) { const Rect& theTabFrame = inTab->mFrame; switch (mOrientation) { case eNorthTab: { ::MoveTo(theTabFrame.left + 1, theTabFrame.bottom + 1); ::LineTo(theTabFrame.left + 1, theTabFrame.top + mCornerPixels); ::LineTo(theTabFrame.left + mCornerPixels, theTabFrame.top + 1); ::LineTo(theTabFrame.right - (mCornerPixels + mBevelDepth) + 1, theTabFrame.top + 1); } break; case eEastTab: { // I'm not sure whether this is quite right... ::MoveTo(theTabFrame.left - 1, theTabFrame.top + 1); ::LineTo(theTabFrame.right - (mCornerPixels + mBevelDepth) + 1, theTabFrame.top + 1); ::LineTo(theTabFrame.right - mBevelDepth, theTabFrame.top + mCornerPixels); } break; case eSouthTab: { // I'm not sure whether this is quite right... ::MoveTo(theTabFrame.left + 1, theTabFrame.top - 1); ::LineTo(theTabFrame.left + 1, theTabFrame.bottom - (mCornerPixels + mBevelDepth) + 1); ::LineTo(theTabFrame.left + mCornerPixels, theTabFrame.bottom - mBevelDepth); } break; case eWestTab: { ::MoveTo(theTabFrame.right + 1, theTabFrame.top + 1); ::LineTo(theTabFrame.left + mCornerPixels, theTabFrame.top + 1); ::LineTo(theTabFrame.left + 1, theTabFrame.top + mCornerPixels); ::LineTo(theTabFrame.left + 1, theTabFrame.bottom - (mCornerPixels + mBevelDepth) + 1); } break; } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DrawBottomBevel // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DrawBottomBevel(CTabInstance* inTab, Boolean /* inCurrentTab */) { const Rect& theTabFrame = inTab->mFrame; switch (mOrientation) { case eNorthTab: { ::MoveTo(theTabFrame.right - mBevelDepth, theTabFrame.top + mCornerPixels); ::LineTo(theTabFrame.right - mBevelDepth, theTabFrame.bottom + 1); } break; case eEastTab: { // I'm not sure whether this is quite right... ::MoveTo(theTabFrame.right - mBevelDepth, theTabFrame.top + mCornerPixels); ::LineTo(theTabFrame.right - mBevelDepth, theTabFrame.bottom - (mCornerPixels + mBevelDepth) + 1); ::LineTo(theTabFrame.right - (mCornerPixels + mBevelDepth) + 1, theTabFrame.bottom - mBevelDepth); ::LineTo(theTabFrame.left - 1, theTabFrame.bottom - mBevelDepth); } break; case eSouthTab: { // I'm not sure whether this is quite right... ::MoveTo(theTabFrame.left + mCornerPixels, theTabFrame.bottom - mBevelDepth); ::LineTo(theTabFrame.right - (mCornerPixels + mBevelDepth) + 1, theTabFrame.bottom - mBevelDepth); ::LineTo(theTabFrame.right - mBevelDepth, theTabFrame.bottom - (mCornerPixels + mBevelDepth) + 1); ::LineTo(theTabFrame.right - mBevelDepth, theTabFrame.top - 1); } break; case eWestTab: { ::MoveTo(theTabFrame.left + mCornerPixels, theTabFrame.bottom - mBevelDepth); ::LineTo(theTabFrame.right + 1, theTabFrame.bottom - mBevelDepth); } break; } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::SetClipForDrawingSides(void) { Assert_(mCurrentTab != NULL); Rect theTempTabFrame = mCurrentTab->mFrame; Int16 theExtendFactor = mBevelDepth + mRisePixels; switch (mOrientation) { case eNorthTab: theTempTabFrame.bottom += theExtendFactor; theTempTabFrame.right -= mBevelDepth; theTempTabFrame.left += mBevelDepth + 1; break; case eEastTab: theTempTabFrame.left -= theExtendFactor; theTempTabFrame.top += mBevelDepth + 1; theTempTabFrame.bottom -= mBevelDepth; break; case eSouthTab: theTempTabFrame.top -= theExtendFactor; theTempTabFrame.right -= mBevelDepth; theTempTabFrame.left += mBevelDepth + 1; break; case eWestTab: theTempTabFrame.right += theExtendFactor; theTempTabFrame.top += mBevelDepth + 1; theTempTabFrame.bottom -= mBevelDepth; break; } StRegion theTabFrameMask(theTempTabFrame); StRegion theSideClipMask(mSideClipFrame); ::DiffRgn(theSideClipMask, theTabFrameMask, theSideClipMask); ::SectRgn(theSideClipMask, mCurrentTab->mMask, theSideClipMask); ::SetClip(theSideClipMask); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DrawSides(void) { UGraphicGizmos::BevelTintRect(mSideClipFrame, mBevelDepth, 0x4000, 0x4000); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ Int16 CTabControl::FindHotSpot(Point inPoint) const { Assert_(mCurrentTab != NULL); // Since the regions overlap, check the currently selected tab's region // before checking the others. Int16 theHotSpot = 0; if (::PtInRgn(inPoint, mCurrentTab->mMask)) { theHotSpot = mValue; } else { CTabInstance* theTab; LArrayIterator theIter(mTabs, LArrayIterator::from_Start); while (theIter.Next(&theTab)) { if (theTab == mCurrentTab) continue; if (! theTab->mEnabled) { ::SysBeep(1); break; } if (::PtInRgn(inPoint, theTab->mMask)) { theHotSpot = mTabs.FetchIndexOf(&theTab); break; } } } return theHotSpot; } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ Boolean CTabControl::PointInHotSpot(Point inPoint, Int16 inHotSpot) const { Assert_(mCurrentTab != NULL); Boolean bInHotSpot = false; // If the hot spot that we're checking is not the current tab // then the tab is behind the current and is only partially // exposed. We need to mask out the hidden area. if ((GetValue() != inHotSpot)) { CTabInstance* theTab; mTabs.FetchItemAt(inHotSpot, &theTab); StRegion theTempMask(theTab->mMask); ::DiffRgn(theTempMask, mCurrentTab->mMask, theTempMask); bInHotSpot = ::PtInRgn(inPoint, theTempMask); } else bInHotSpot = ::PtInRgn(inPoint, mCurrentTab->mMask); return bInHotSpot; } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::HotSpotAction( Int16 inHotSpot, Boolean inCurrInside, Boolean inPrevInside) { Assert_(mCurrentTab != NULL); // We only want to draw track feedback if the tab is not currently selected if ((GetValue() != inHotSpot) && (inCurrInside != inPrevInside)) { mTrackInside = inCurrInside; CTabInstance* theTab; mTabs.FetchItemAt(inHotSpot, &theTab); // This is not the proper way to draw feed back // 1) it flickers badly. If you hold down the mouse button on a control you can see // the lines that make up the fram disappear; // 2) If you click on a tab and then move the mouse out of the tab it doesn't // unhighlight the tab. // DrawOneTab( theTab ); } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::HotSpotResult(Int16 inHotSpot) { // We got here because we successfully tracked, // therefore changing value will undo tab hilighting mTrackInside = false; SetValue(inHotSpot); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::SetValue(Int32 inValue) { if ((inValue != mValue) || (mCurrentTab == NULL)) { if (inValue < mMinValue) { // Enforce min/max range inValue = mMinValue; } else if (inValue > mMaxValue) { inValue = mMaxValue; } mTabs.FetchItemAt(inValue, &mCurrentTab); SetValueMessage(mCurrentTab->mMessage); // setting the value broadcasts msg_TabSwitched with the tab's // message in the ioParam LControl::SetValue(inValue); Draw(NULL); // Draws offscreen if possible } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ MessageT CTabControl::GetMessageForValue(Int32 inValue) { MessageT theMessage = msg_Nothing; if ((inValue >= mMinValue) && (inValue <= mMaxValue)) { CTabInstance* theTab; mTabs.FetchItemAt(inValue, &theTab); theMessage = theTab->mMessage; } return theMessage; } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::BroadcastValueMessage() { if (mValueMessage != cmd_Nothing) { CTabInstance* theTab; mTabs.FetchItemAt(mValue, &theTab); BroadcastMessage(msg_TabSwitched, &mValueMessage); } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::ResizeFrameBy( Int16 inWidthDelta, Int16 inHeightDelta, Boolean inRefresh) { LControl::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh); Recalc(); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::ActivateSelf(void) { FocusDraw(); Draw(NULL); ::ValidRgn(mControlMask); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void CTabControl::DeactivateSelf(void) { FocusDraw(); Draw(NULL); ::ValidRgn(mControlMask); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ CTabInstance::CTabInstance() { mMask = NULL; mEnabled = true; } CTextTabInstance::CTextTabInstance(const STabDescriptor& inDesc) { mMessage = inDesc.valueMessage; mWidth = inDesc.width; mTitle = inDesc.title; mMask = NULL; } CTabInstance::~CTabInstance() { if (mMask != NULL) ::DisposeRgn(mMask); } void CTextTabInstance::DrawTitle(CTabInstance* inCurrentTab, ResIDT inTitleTraitsID) { if (mTitle.Length() >0) { StColorPenState::Normalize(); TextTraitsH theTraitsHandle = UTextTraits::LoadTextTraits(inTitleTraitsID); Rect theFrame = mFrame; if (this == inCurrentTab) { (**theTraitsHandle).style |= bold; ::OffsetRect(&theFrame, 0, 1); } if (! mEnabled) (**theTraitsHandle).mode |= grayishTextOr; { StHandleLocker theLocker((Handle)theTraitsHandle); UTextTraits::SetPortTextTraits(*theTraitsHandle); UGraphicGizmos::CenterStringInRect(mTitle, theFrame); } ::ReleaseResource((Handle)theTraitsHandle); } } CIconTabInstance::CIconTabInstance(const STabDescriptor& inDesc) { mMessage = inDesc.valueMessage; mWidth = 24; // Really should calc the size of the icon ::StringToNum(inDesc.title, &mIconID); mMask = NULL; } void CIconTabInstance::DrawTitle(CTabInstance* inCurrentTab, ResIDT /*inTitleTraitsID*/) { StColorPenState::Normalize(); Rect theFrame = mFrame; theFrame.left += 4;// And you thought the Instance was a hack theFrame.right = theFrame.left + 16; theFrame.top += 4; theFrame.bottom = theFrame.top + 16; IconTransformType transform = kTransformNone; if (this == inCurrentTab) { ::OffsetRect(&theFrame, 0, 1); } if (! mEnabled) transform |= kTransformDisabled; ::PlotIconID(&theFrame, kAlignAbsoluteCenter, transform, mIconID); }