/* -*- 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. */ #include "CNavCenterSelectorPane.h" #include #include #include "CRDFCoordinator.h" #include "CContextMenuAttachment.h" #include "Netscape_constants.h" #include "CIconContext.h" #include "CIconCache.h" #include "UGAColorRamp.h" #include "net.h" #include "mimages.h" #pragma mark -- CNavCenterSelectorPane methods -- CNavCenterSelectorPane::CNavCenterSelectorPane( LStream* inStream ) : LView(inStream), LDragAndDrop ( GetMacPort(), this ), mIsEmbedded(false), mHTPane(nil), mTooltipIndex(-1), mMouseIsInsidePane(false), mImageMode(0L), mCellHeight(25), mCachedWorkspace(NULL), mDropRow(LArray::index_Last), mDropPreposition(kDropOn) { // nothing else to do here. } CNavCenterSelectorPane::~CNavCenterSelectorPane() { // nothing else to do here since all FE data is cleaned up by workspace remove events } // // SetActiveWorkspace // // Call this to change the current workspace. Passing in NULL will clear the selection. Most // of the work is done in NoticeActiveWorkspaceChanged() where the view is redrawn and the // view change is broadcast to the RDFCoordinator. This doesn't actually change HT's // currently selected view, as it is done by the coordinator. // void CNavCenterSelectorPane :: SetActiveWorkspace ( HT_View inNewWorkspace ) { HT_View oldWorkspace = mCachedWorkspace; mCachedWorkspace = inNewWorkspace; NoticeActiveWorkspaceChanged(oldWorkspace); } // SetActiveWorkspace // // DrawSelf // // Iterate over each view and draw it. We store the class that is responsible for drawing a // workspace in the FE data in HT. // void CNavCenterSelectorPane::DrawSelf() { Rect cellBounds; CalcLocalFrameRect(cellBounds); // erase the background StColorState saved; ::RGBBackColor(&UGAColorRamp::GetColor(colorRamp_Gray2)); ::EraseRect(&cellBounds); // find the bounds of the first cell cellBounds.top = 0; cellBounds.bottom = mCellHeight; #if DRAW_WITH_TITLE StTextState savedState; UTextTraits::SetPortTextTraits(130); #endif // iterate over workspaces, drawing each in turn. const listCount = HT_GetViewListCount(GetHTPane()); const HT_View selectedView = HT_GetSelectedView(GetHTPane()); for ( int i = 0; i < listCount; i++ ) { unsigned long drawMode = mImageMode; HT_View currView = HT_GetNthView(GetHTPane(), i); ViewFEData* viewData = static_cast(HT_GetViewFEData(currView)); if ( viewData ) { if ( currView == selectedView ) drawMode |= kSelected; if ( viewData->mSelector ) viewData->mSelector->workspaceImage->DrawInCurrentView(cellBounds, drawMode); } // prepare to draw next selector cellBounds.top += mCellHeight; cellBounds.bottom += mCellHeight; } // for each selector ResizeImageTo ( mFrameSize.width, cellBounds.top, true ); } // DrawSelf // // ClickSelf // // Handle when the user clicks in this pane. A click on a workspace icon will switch to that // view. Also handle opening and closing the shelf (if we're embedded) and context clicking. // void CNavCenterSelectorPane::ClickSelf( const SMouseDownEvent& inMouseDown ) { FocusDraw(); // to make the context menu stuff works CContextMenuAttachment::SExecuteParams params; params.inMouseDown = &inMouseDown; // do nothing if click not in any workspace except show context menu. HT_View clickedView = FindSelectorForPoint(inMouseDown.whereLocal); if ( !clickedView ) { ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms); return; } // if the user clicks on a workspace icon, open the shelf to display the workspace. If // they click on the same workspace icon as is already displayed, close the shelf. Don't // change the shelf state on a context click. if ( clickedView != GetActiveWorkspace() ) { HT_View oldSelection = GetActiveWorkspace(); SetActiveWorkspace(clickedView); ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms); if ( params.outResult != CContextMenuAttachment::eHandledByAttachment && mIsEmbedded ) { bool value = true; BroadcastMessage ( msg_ShelfStateShouldChange, &value ); // force open } else { // this combats the problem when there is no workspace and the user context clicks. We // select the one they clicked on, but don't open the shelf. We can't just leave the // item selected and the shelf closed, so we just reset the active workspace to none. // Icky code but it doesn't freak out the user. if ( !oldSelection ) SetActiveWorkspace(NULL); } } else { ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms); if ( params.outResult != CContextMenuAttachment::eHandledByAttachment && mIsEmbedded ) { bool value = false; BroadcastMessage ( msg_ShelfStateShouldChange, &value ); // force closed SetActiveWorkspace( NULL ); } } } // ClickSelf // // AdjustCursorSelf // // Use the context menu attachment to handle showing context menu cursor when cmd key is // down and mouse is over this view. // void CNavCenterSelectorPane::AdjustCursorSelf( Point /*inPoint*/, const EventRecord& inEvent ) { ExecuteAttachments(CContextMenuAttachment::msg_ContextMenuCursor, static_cast(const_cast(&inEvent))); } // AdjustCursorSelf // // FindTooltipForMouseLocation // // Provide the tooltip for the workspace that the mouse is hovering over // void CNavCenterSelectorPane :: FindTooltipForMouseLocation ( const EventRecord& inMacEvent, StringPtr outTip ) { Point temp = inMacEvent.where; GlobalToPortPoint(temp); PortToLocalPoint(temp); HT_View selector = FindSelectorForPoint ( temp ); if ( selector ) { // pull the name out of HT and stuff it into the Pascal string...DO NOT DELETE |buffer|. const char* buffer = HT_GetViewName ( selector ); outTip[0] = strlen(buffer); strcpy ( (char*) &outTip[1], buffer ); } else ::GetIndString ( outTip, 10506, 15); // supply a helpful message... } // FindTooltipForMouseLocation // // MouseLeave // // Called when the mouse leaves the view. Just update our "hot" cell to an invalid cell. // void CNavCenterSelectorPane :: MouseLeave ( ) { mTooltipIndex = -1; mMouseIsInsidePane = false; } // MouseLeave // // MouseWithin // // Called while the mouse moves w/in the pane. Find which workspace the mouse is // currently over and if it differs from the last workspace it was in, hide the // tooltip and remember the new cell. // void CNavCenterSelectorPane :: MouseWithin ( Point inPortPt, const EventRecord& /*inEvent*/ ) { mMouseIsInsidePane = true; size_t index = FindIndexForPoint ( inPortPt ); if ( mTooltipIndex != index ) { mTooltipIndex = index; ExecuteAttachments(msg_HideTooltip, this); // hide tooltip } } // MouseWithin // // NoticeActiveWorkspaceChanged // // Redraw the old and new workspace icons only when there is a change. Also tell the RDFCoordinator // to update HT's active workspace. // void CNavCenterSelectorPane :: NoticeActiveWorkspaceChanged ( HT_View inOldSel ) { BroadcastMessage(msg_ActiveSelectorChanged, GetActiveWorkspace()); FocusDraw(); #if DRAW_WITH_TITLE StTextState savedState; ::TextFont(kFontIDGeneva); ::TextSize(9); ::TextFace(0); #endif // redraw old Rect previous = { 0, 0, 0, 0 }; if ( inOldSel ) { CalcLocalFrameRect(previous); previous.top = HT_GetViewIndex(inOldSel) * mCellHeight; previous.bottom = previous.top + mCellHeight; ViewFEData* data = static_cast(HT_GetViewFEData(inOldSel)); if ( data && data->mSelector ) data->mSelector->workspaceImage->DrawInCurrentView ( previous, mImageMode ); } // redraw new Rect current = { 0, 0, 0, 0 }; if ( GetActiveWorkspace() ) { CalcLocalFrameRect(current); current.top = HT_GetViewIndex(GetActiveWorkspace()) * mCellHeight; current.bottom = current.top + mCellHeight; ViewFEData* data = static_cast(HT_GetViewFEData(GetActiveWorkspace())); if ( data && data->mSelector ) data->mSelector->workspaceImage->DrawInCurrentView ( current, mImageMode | kSelected ); } } // NoticeActiveWorkspaceChanged // // ItemIsAcceptable // // Just about anything should be acceptable because we just want to make sure // that the appropriate nav center view is open for the user to then drag into. Drops // can occur here, so I guess we have to make sure that it is something that we // recognize. // // If FindBestFlavor() finds an acceptable flavor, then this item can be accepted. If not, it // can't. We don't really care at this point what that flavor is. // Boolean CNavCenterSelectorPane :: ItemIsAcceptable ( DragReference inDragRef, ItemReference inItemRef ) { FlavorType ignored; return FindBestFlavor ( inDragRef, inItemRef, ignored ); } // ItemIsAcceptable // // EnterDropArea // // If the drag comes from an outside source, record when the mouse entered this pane // void CNavCenterSelectorPane :: EnterDropArea ( DragReference /* inDragRef */, Boolean inDragHasLeftSender ) { // if the drag did not originate here, go into "timed switching" mode if ( inDragHasLeftSender ) mDragAndDropTickCount = ::TickCount(); } // EnterDropArea // // LeaveDropArea // // Reset our counter // void CNavCenterSelectorPane :: LeaveDropArea ( DragReference inDragRef ) { if ( mDropRow != LArray::index_Last ) DrawDividingLine ( mDropRow, mDropPreposition ); mDragAndDropTickCount = 0L; mDropRow = LArray::index_Last; mDropPreposition = kDropOn; LDragAndDrop::LeaveDropArea ( inDragRef ); } // LeaveDropArea // // InsideDropArea // // Called when the mouse moves inside this pane on a drag and drop. // // There are two cases here: the drag originated here or it didn't. When it did, the user // is trying to rearrange the order of the views. When the drag comes from outside, we need // to do our fancy timed switching behavior. We can tell the difference between these two // modes by checking the value of |mDragAndDropTickCount| which will be non-zero if the // drag originated outside. // void CNavCenterSelectorPane :: InsideDropArea ( DragReference inDragRef ) { const Uint32 kPaneSwitchDelay = 60; // 1 second delay if ( mDragAndDropTickCount ) { FocusDraw(); Point mouseLoc; ::GetDragMouse(inDragRef, &mouseLoc, NULL); ::GlobalToLocal(&mouseLoc); // find which view the user is hovering over and what part of the cell the mouse is in. // If it is in the top or bottom regions, they want to do a drop to create a new workspace, // so do the drop feedback. If they are hovering over the middle, then proceed with // the pane switching behavior. EDropLocation newDropPrep; TableIndexT newDropRow; HT_View newSelection = FindSelectorForPoint(mouseLoc, newDropPrep, newDropRow); if ( newDropPrep != kDropOn ) { // do drag feedback if ( newDropRow != mDropRow || newDropPrep != mDropPreposition ) { // erase old line DrawDividingLine ( mDropRow, mDropPreposition ); mDropRow = newDropRow; mDropPreposition = newDropPrep; // draw new line DrawDividingLine ( newDropRow, newDropPrep ); } // if something has changed mDragAndDropTickCount = ::TickCount(); // see comment below // we don't want to continue through the rest of the routine because it is for // switching views on the fly.... return; } // if mouse not over middle of cell // clear out any old divider lines now that we're dropping on and set d&d members // for any drop on this workspace. DrawDividingLine ( mDropRow, mDropPreposition ); mDropRow = newDropRow; mDropPreposition = kDropOn; // if the user hasn't sat here long enough, don't do anything... if ( ::TickCount() - mDragAndDropTickCount < kPaneSwitchDelay ) return; // ...ok, they really want to switch panes... // make sure the shelf is visible (only necessary if in chrome). This // shouldn't be necessary, but things won't redraw correctly unless we do this. if ( mIsEmbedded ) { bool value = true; BroadcastMessage ( msg_ShelfStateShouldChange, &value ); // force open } // switch to the workspace the user is now hovering over if ( newSelection != GetActiveWorkspace() ) SetActiveWorkspace ( newSelection ); // we need to reset the timer so we don't get into the situation where the user // hovers over the selected item for a long time and then when they move to the next // selector, the time will be > |kPaneSwitchDelay| and it will instantly switch. // Keeping our timer up to date should avoid this and force the user to always // wait the delay. NOTE: we don't get here if they have yet to reach the delay amount // so we're not actually reseting the timer every time this is called. mDragAndDropTickCount = ::TickCount(); } // if we are tracking an outside drag. else { // do nothing as of yet... } } // InsideDropArea // // CycleCurrentWorkspace // // If the shelf is open, advance the current workspace, wrapping around if we get to the end // void CNavCenterSelectorPane :: CycleCurrentWorkspace ( ) { if ( GetActiveWorkspace() ) { HT_View newSelector = HT_GetNthView ( GetHTPane(), HT_GetViewIndex(GetActiveWorkspace()) + 1 ); if ( !newSelector ) newSelector = HT_GetNthView ( GetHTPane(), 0 ); SetActiveWorkspace(newSelector); } // if there is a selection } // CycleCurrentWorkspace // // FindIndexForPoint // // Given a point in image coordinates, find the index of the selector the mouse was over. Right // now, this is just an easy division because the selector regions all butt up against each // other. If there were ever any space between them, this would need to be more complicated. // // This index is 0-based, not 1-based like most other things in Powerplant. This is ok because // HT is also 0-based and we only use this index when talking to HT. // size_t CNavCenterSelectorPane :: FindIndexForPoint ( const Point & inMouseLoc ) const { return inMouseLoc.v / mCellHeight; } // FindIndexForPoint // // FindIndexForPoint // // Used to tell not only which cell the user is hovering over, but also what part of that cell. // This info can be combined with drag feedback to allow for inserting before/after the cell the // mouse is over. The cell is divided horizontally into 3 parts, with the middle being the largest // area. // size_t CNavCenterSelectorPane :: FindIndexForPoint ( const Point & inMouseLoc, EDropLocation & outWhere ) const { TableIndexT cell = inMouseLoc.v / mCellHeight; // remember: 0-based Uint32 distFromMouseToPrevCell = inMouseLoc.v % mCellHeight; // the cell is divided into 3 parts: 7 pixels at top & bottom, remaining space in the middle const Uint8 kCellDividerSize = 7; if ( distFromMouseToPrevCell <= kCellDividerSize ) outWhere = kDropBefore; else if ( distFromMouseToPrevCell >= mCellHeight - kCellDividerSize ) outWhere = kDropAfter; else outWhere = kDropOn; return cell; } // FindIndexForPoint // // FindSelectorForPoint // // Grabs the view where the user clicked from HT and returns it. // HT_View CNavCenterSelectorPane :: FindSelectorForPoint ( const Point & inMouseLoc ) const { return HT_GetNthView ( GetHTPane(), FindIndexForPoint(inMouseLoc) ); } // FindSelectorForPoint // // FindSelectorForPoint // // Returns the view, row#, and a relative indicator (before, after, middle) for where the // new workspace will be created if the drop is at the current mouse location. // HT_View CNavCenterSelectorPane :: FindSelectorForPoint ( const Point & inMouseLoc, EDropLocation & outWhere, TableIndexT & outRow ) const { outRow = FindIndexForPoint ( inMouseLoc, outWhere ); // If the row we've calculated is > then number of rows, the user has dragged below all the // existing workspaces, so translate this to a drop after the last row. Uint32 numViews = HT_GetViewListCount(GetHTPane()); if ( outRow > numViews - 1 ) { outRow = numViews - 1; outWhere = kDropAfter; } return HT_GetNthView ( GetHTPane(), outRow ); } // FindSelectorForPoint // // FindCommandStatus // void CNavCenterSelectorPane :: FindCommandStatus ( CommandT inCommand, Boolean &outEnabled, Boolean &outUsesMark, Char16 &outMark, Str255 outName) { if ( inCommand >= cmd_NavCenterBase && inCommand <= cmd_NavCenterCap ) outEnabled = HT_IsMenuCmdEnabled(GetHTPane(), (HT_MenuCmd)(inCommand - cmd_NavCenterBase)); else LCommander::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); } // FindCommandStatus // // DrawDividingLine // // Draws a horizontal black line before or after the given row in the bar (or undraws if one // is already there) depending on the value of |inPrep|. Used during drag and drop for drop // feedback. // void CNavCenterSelectorPane :: DrawDividingLine( TableIndexT inRow, EDropLocation inPrep ) { if ( inRow == LArray::index_Last || inPrep == kDropOn ) return; Uint32 numItems = HT_GetViewListCount(GetHTPane()); if ( !numItems ) // don't draw anything if toolbar empty return; // find the boundary of the line we are going to draw. This will be relative to // the given row as specified (before or after) by inPrep. Uint32 frameTop; frameTop = inRow * mCellHeight; if ( inPrep == kDropAfter ) frameTop += mCellHeight; // Focus the pane and get the table and cell frames. if ( FocusDraw() ) { StColorPenState theDrawState; // Setup the color and pen state then draw the line ::ForeColor( blackColor ); ::PenMode( patXor ); ::PenSize ( 2, 2 ); ::MoveTo( 0, frameTop ); ::LineTo( mFrameSize.width, frameTop ); } } // DrawDividingLine // // ReceiveDragItem // // Pass this along to the implementation in CURLDragMixin. // void CNavCenterSelectorPane :: ReceiveDragItem ( DragReference inDragRef, DragAttributes inDragAttrs, ItemReference inItemRef, Rect & inItemBounds ) { CHTAwareURLDragMixin::ReceiveDragItem(inDragRef, inDragAttrs, inItemRef, inItemBounds ); } // ReceiveDragItem // // HandleDropOfHTResource // // The user dropped something from HT onto the selector bar. If they dropped _on_ the // selector icon, drop it into that workspace. If they dropped it _between_ two workspaces, // get HT to create a new workspace for us with that data in it. If there are no // workspaces, we have to create our own and put the data in it. // void CNavCenterSelectorPane :: HandleDropOfHTResource ( HT_Resource inDropNode ) { if ( HT_GetViewListCount(GetHTPane()) ) { HT_View view = HT_GetNthView ( GetHTPane(), mDropRow ); HT_Resource viewTopNode = HT_TopNode(view); if ( mDropPreposition == kDropOn ) HT_DropHTROn ( viewTopNode, inDropNode ); else HT_DropHTRAtPos ( viewTopNode, inDropNode, mDropPreposition == kDropBefore ? PR_TRUE : PR_FALSE ); } else { // there are no existing workspaces to drop before/after, so we must create our own DebugStr("\pDrop when no workspaces present not implemented"); } } // HandleDropOfHTResource // // HandleDropOfPageProxy // // The user dropped something from navigator onto the selector bar. If they dropped _on_ the // selector icon, drop it into that workspace. If they dropped it _between_ two workspaces, // get HT to create a new workspace for us with that data in it. If there are no // workspaces, we have to create our own and put the data in it. // void CNavCenterSelectorPane :: HandleDropOfPageProxy ( const char* inURL, const char* inTitle ) { // cast away constness for HT char* url = const_cast(inURL); char* title = const_cast(inTitle); if ( HT_GetViewListCount(GetHTPane()) ) { HT_View view = HT_GetNthView ( GetHTPane(), mDropRow ); HT_Resource viewTopNode = HT_TopNode(view); if ( mDropPreposition == kDropOn ) HT_DropURLAndTitleOn ( viewTopNode, url, title ); else HT_DropURLAndTitleAtPos ( viewTopNode, url, title, mDropPreposition == kDropBefore ? PR_TRUE : PR_FALSE ); } else { // there are no existing workspaces to drop before/after, so we must create our own DebugStr("\pDrop when no workspaces present not implemented"); } } // HandleDropOfPageProxy // // HandleDropOfLocalFile // // The user dropped something from the Finder onto the selector bar. Since a file url is // just a url, cheat and use the same code as the page proxy. // void CNavCenterSelectorPane :: HandleDropOfLocalFile ( const char* inFileURL, const char* fileName, const HFSFlavor & /*inFileData*/ ) { HandleDropOfPageProxy ( inFileURL, fileName ); } // HandleDropOfLocalFile // // HandleDropOfText // // Called when user drops a text clipping onto the navCenter. Do nothing for now. // void CNavCenterSelectorPane :: HandleDropOfText ( const char* /*inTextData*/ ) { DebugStr("\pDropping TEXT here not implemented"); } // HandleDropOfText #pragma mark --- struct SelectorData --- SelectorData :: SelectorData ( HT_View inView, CNavCenterSelectorPane* inSelectorBar ) { const ResIDT cFolderIconID = -3999; // use the OS Finder folder icon char viewName[64]; #if DRAW_WITH_TITLE HT_ViewDisplayString ( inView, viewName, 8 ); viewName[8] = NULL; #else viewName[0] = NULL; #endif // Because of the way we get GIF images into the app (loading them into a 'Tang' resource), // we look for certain url's that we know are internal to the product and use the about:XXX.gif // hack on them. FE_AboutData() knows how to decode these images so we're set. If things aren't // of a form we recognize, they are probably a real url to a real icon somewhere on the net. char* iconURL = HT_GetWorkspaceSmallIconURL(inView); string aboutURL = "about:"; char workspace[500]; workspace[0] = '\0'; sscanf ( iconURL, "icon/small:workspace,%s", workspace ); if ( *workspace ) { aboutURL += workspace; // make into form: about:XXX.gif aboutURL += ".gif"; } else aboutURL = iconURL; // probably a url to something on the net, leave it alone workspaceImage = new TitleImage(viewName, cFolderIconID, aboutURL, inSelectorBar); } // constructor SelectorData :: ~SelectorData ( ) { delete workspaceImage; } #pragma mark -- class TitleImage -- #include "UGraphicGizmos.h" TitleImage :: TitleImage ( const LStr255 & inTitle, ResIDT inIconID, const string & inIconURL, CNavCenterSelectorPane* inSelectorBar ) : mTitle(inTitle), mIconID(inIconID), mSelector(inSelectorBar), SelectorImage(inIconURL) { Assert_(mSelector != NULL); #if DRAW_WITH_TITLE ::TextFont(kFontIDGeneva); ::TextSize(9); ::TextFace(0); mTextWidth = ::TextWidth( inTitle, 0, inTitle.Length() ) + 6; #endif } TitleImage :: ~TitleImage ( ) { } // // ListenToMessage // // Listen to messages from the icon cache so we can display the image when imageLib gives us // something to display. // void TitleImage :: ListenToMessage ( MessageT inMessage, void* inData ) { switch ( inMessage ) { case CIconContext::msg_ImageReadyToDraw: mSelector->Refresh(); //₯₯ do this better after it all works break; } } // ListenToMessage // // DrawInCurrentView // // Draw a selector. The code for drawing the title has been commented out for now because // it has not been hooked up to the text/graphics prefs. // void TitleImage :: DrawInCurrentView( const Rect& inBounds, unsigned long inMode ) const { StColorState saved; ::RGBBackColor(&UGAColorRamp::GetColor(colorRamp_Gray2)); ::EraseRect(&inBounds); Rect iconRect = inBounds; Rect textbg = { 0, 0, 0, 0 }; #if DRAW_WITH_TITLE if ( TextWidth() > inBounds.right - inBounds.left ) textbg.right = inBounds.right - inBounds.left; else textbg.right = TextWidth(); UGraphicGizmos::CenterRectOnRect(textbg, inBounds); textbg.bottom = inBounds.bottom; textbg.top = inBounds.bottom - 12; #endif iconRect.right = iconRect.left + 16; iconRect.bottom = iconRect.top + 16; UGraphicGizmos::CenterRectOnRect ( iconRect, inBounds ); SInt16 iconTransform = kTransformNone; if ( inMode & CNavCenterSelectorPane::kSelected ) { iconTransform = kTransformSelected; #if DRAW_WITH_TITLE ::BackColor(blackColor); ::EraseRect(&textbg); ::TextMode(srcXor); #endif #if DRAW_SELECTED_AS_TAB Rect temp = inBounds; temp.left += 2; StRegion boundsRgn(temp); DrawOneTab(boundsRgn); #endif } DrawImage ( *(Point*)&inBounds, iconTransform, 0, 0 ) ; #if DRAW_WITH_TITLE ::MoveTo( textbg.left + 2, inBounds.bottom - 2 ); ::DrawString ( Title() ); #endif } // DrawInCurrentView // // DrawStandby // // Override to just draw a folder icon when the appropriate image has not yet been loaded // void TitleImage :: DrawStandby ( const Point & inTopLeft, const IconTransformType inTransform ) const { Rect bounds = { inTopLeft.v + 3, inTopLeft.h + 3, inTopLeft.v + 19, inTopLeft.h + 19 } ; OSErr err = ::PlotIconID(&bounds, kAlignNone, inTransform, IconID()); } // DrawStandby #if DRAW_SELECTED_AS_TAB // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void TitleImage::DrawOneTabBackground( RgnHandle inRegion, Boolean inCurrentTab) const { SBevelColorDesc mActiveColors; SBevelColorDesc theTabColors; UGraphicGizmos::LoadBevelTraits(1000, theTabColors); Int16 thePaletteIndex = theTabColors.fillColor; // if (mTrackInside) // thePaletteIndex--; ::PmForeColor(thePaletteIndex); ::PaintRgn(inRegion); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void TitleImage::DrawOneTabFrame(RgnHandle inRegion, Boolean inCurrentTab) const { // Draw the tab frame SBevelColorDesc theTabColors; UGraphicGizmos::LoadBevelTraits(1000, theTabColors); Int16 thePaletteIndex = theTabColors.frameColor; ::PmForeColor(thePaletteIndex); ::FrameRgn(inRegion); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void TitleImage::DrawCurrentTabSideClip(RgnHandle inRegion) const { SBevelColorDesc theTabColors; UGraphicGizmos::LoadBevelTraits(1000, theTabColors); // Always only for the current tab ::SetClip(inRegion); ::PmForeColor(theTabColors.fillColor); // ::PaintRect(&mSideClipFrame); } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void TitleImage::DrawOneTab(RgnHandle inBounds) const { StClipRgnState theClipSaver; #if 0 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); } #endif // This draws the fill pattern in the tab DrawOneTabBackground(inBounds, true); // This just calls FrameRgn on inTab->mMask // DrawOneTabFrame(inBounds, true); StColorPenState thePenSaver; thePenSaver.Normalize(); DrawCurrentTabSideClip(inBounds); Uint8 mBevelDepth = 2; if (mBevelDepth > 0) { ::PenSize(mBevelDepth, mBevelDepth); bool isCurrentTab = true; 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(inBounds); } // Draw the bevel shadow // StRegion theShadeRgn(inTab->mShadeFrame); // ::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(inBounds, true); } } // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ // ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void TitleImage::DrawTopBevel(RgnHandle inBounds) const { const Rect& theTabFrame = (**inBounds).rgnBBox; enum { eNorthTab = 0, eEastTab, eSouthTab, eWestTab }; Uint8 mOrientation = eWestTab; Uint8 mCornerPixels = 5, mBevelDepth = 2; 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 TitleImage::DrawBottomBevel(RgnHandle inBounds, Boolean inCurrentTab) const { const Rect& theTabFrame = (**inBounds).rgnBBox; enum { eNorthTab = 0, eEastTab, eSouthTab, eWestTab }; Uint8 mOrientation = eWestTab; Uint8 mCornerPixels = 5, mBevelDepth = 2; 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; } } #endif