| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sc.hxx" |
| |
| |
| #include <vcl/svapp.hxx> |
| #include <vcl/taskpanelist.hxx> |
| |
| #include "olinewin.hxx" |
| #include "olinetab.hxx" |
| #include "document.hxx" |
| #include "dbfunc.hxx" |
| #include "sc.hrc" |
| |
| // ============================================================================ |
| |
| const long SC_OL_BITMAPSIZE = 12; |
| const long SC_OL_POSOFFSET = 2; |
| |
| const size_t SC_OL_NOLEVEL = static_cast< size_t >( -1 ); |
| const size_t SC_OL_HEADERENTRY = static_cast< size_t >( -1 ); |
| |
| const sal_uInt16 SC_OL_IMAGE_PLUS = 9; |
| const sal_uInt16 SC_OL_IMAGE_MINUS = SC_OL_IMAGE_PLUS + 1; |
| const sal_uInt16 SC_OL_IMAGE_NOTPRESSED = SC_OL_IMAGE_MINUS + 1; |
| const sal_uInt16 SC_OL_IMAGE_PRESSED = SC_OL_IMAGE_NOTPRESSED + 1; |
| |
| // ============================================================================ |
| |
| ScOutlineWindow::ScOutlineWindow( Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) : |
| Window( pParent ), |
| mrViewData( *pViewData ), |
| meWhich( eWhich ), |
| mbHoriz( eMode == SC_OUTLINE_HOR ), |
| mbMirrorEntries( false ), // updated in SetHeaderSize |
| mbMirrorLevels( false ), // updated in SetHeaderSize |
| mpSymbols( NULL ), |
| maLineColor( COL_BLACK ), |
| mnHeaderSize( 0 ), |
| mnHeaderPos( 0 ), |
| mnMainFirstPos( 0 ), |
| mnMainLastPos( 0 ), |
| mbMTActive( false ), |
| mbMTPressed( false ), |
| mnFocusLevel( 0 ), |
| mnFocusEntry( SC_OL_HEADERENTRY ), |
| mbDontDrawFocus( false ) |
| { |
| EnableRTL( sal_False ); // mirroring is done manually |
| |
| InitSettings(); |
| maFocusRect.SetEmpty(); |
| SetHeaderSize( 0 ); |
| |
| // insert the window into task pane list for "F6 cycling" |
| if( SystemWindow* pSysWin = GetSystemWindow() ) |
| if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() ) |
| pTaskPaneList->AddWindow( this ); |
| } |
| |
| ScOutlineWindow::~ScOutlineWindow() |
| { |
| // remove the window from task pane list |
| if( SystemWindow* pSysWin = GetSystemWindow() ) |
| if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() ) |
| pTaskPaneList->RemoveWindow( this ); |
| } |
| |
| void ScOutlineWindow::SetHeaderSize( long nNewSize ) |
| { |
| sal_Bool bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() ); |
| mbMirrorEntries = bLayoutRTL && mbHoriz; |
| mbMirrorLevels = bLayoutRTL && !mbHoriz; |
| |
| bool bNew = (nNewSize != mnHeaderSize); |
| mnHeaderSize = nNewSize; |
| mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0; |
| mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize; |
| mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1; |
| if ( bNew ) |
| Invalidate(); |
| } |
| |
| long ScOutlineWindow::GetDepthSize() const |
| { |
| long nSize = GetLevelCount() * SC_OL_BITMAPSIZE; |
| if ( nSize > 0 ) |
| nSize += 2 * SC_OL_POSOFFSET + 1; |
| return nSize; |
| } |
| |
| void ScOutlineWindow::ScrollPixel( long nDiff ) |
| { |
| HideFocus(); |
| mbDontDrawFocus = true; |
| |
| long nStart = mnMainFirstPos; |
| long nEnd = mnMainLastPos; |
| |
| long nInvStart, nInvEnd; |
| if (nDiff < 0) |
| { |
| nStart -= nDiff; |
| nInvStart = nEnd + nDiff; |
| nInvEnd = nEnd; |
| } |
| else |
| { |
| nEnd -= nDiff; |
| nInvStart = nStart; |
| nInvEnd = nStart + nDiff; |
| } |
| |
| ScrollRel( nDiff, nStart, nEnd ); |
| Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) ); |
| Update(); |
| |
| // if focus becomes invisible, move it to next visible button |
| ImplMoveFocusToVisible( nDiff < 0 ); |
| |
| mbDontDrawFocus = false; |
| ShowFocus(); |
| } |
| |
| void ScOutlineWindow::ScrollRel( long nEntryDiff, long nEntryStart, long nEntryEnd ) |
| { |
| Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) ); |
| if ( mbHoriz ) |
| Scroll( nEntryDiff, 0, aRect ); |
| else |
| Scroll( 0, nEntryDiff, aRect ); |
| } |
| |
| // internal ------------------------------------------------------------------- |
| |
| void ScOutlineWindow::InitSettings() |
| { |
| const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); |
| SetBackground( rStyleSettings.GetFaceColor() ); |
| maLineColor = rStyleSettings.GetButtonTextColor(); |
| mpSymbols = ScGlobal::GetOutlineSymbols( rStyleSettings.GetHighContrastMode() ); |
| Invalidate(); |
| } |
| |
| const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const |
| { |
| const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() ); |
| if ( !pTable ) return NULL; |
| return mbHoriz ? pTable->GetColArray() : pTable->GetRowArray(); |
| } |
| |
| const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const |
| { |
| const ScOutlineArray* pArray = GetOutlineArray(); |
| return pArray ? pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ) : NULL; |
| } |
| |
| bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const |
| { |
| return mbHoriz ? |
| GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) : |
| GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab()); |
| } |
| |
| bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const |
| { |
| // columns cannot be filtered |
| return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() ); |
| } |
| |
| bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const |
| { |
| bool bAllHidden = true; |
| for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos ) |
| bAllHidden = IsHidden( nPos ); |
| return bAllHidden; |
| } |
| |
| void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const |
| { |
| if ( mbHoriz ) |
| { |
| rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) ); |
| rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) ); |
| } |
| else |
| { |
| rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) ); |
| rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) ); |
| } |
| |
| // include collapsed columns/rows in front of visible range |
| while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) ) |
| --rnColRowStart; |
| } |
| |
| Point ScOutlineWindow::GetPoint( long nLevelPos, long nEntryPos ) const |
| { |
| return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos ); |
| } |
| |
| Rectangle ScOutlineWindow::GetRectangle( |
| long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd ) const |
| { |
| return Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) ); |
| } |
| |
| long ScOutlineWindow::GetOutputSizeLevel() const |
| { |
| Size aSize( GetOutputSizePixel() ); |
| return mbHoriz ? aSize.Height() : aSize.Width(); |
| } |
| |
| long ScOutlineWindow::GetOutputSizeEntry() const |
| { |
| Size aSize( GetOutputSizePixel() ); |
| return mbHoriz ? aSize.Width() : aSize.Height(); |
| } |
| |
| size_t ScOutlineWindow::GetLevelCount() const |
| { |
| const ScOutlineArray* pArray = GetOutlineArray(); |
| size_t nLevelCount = pArray ? pArray->GetDepth() : 0; |
| return nLevelCount ? (nLevelCount + 1) : 0; |
| } |
| |
| long ScOutlineWindow::GetLevelPos( size_t nLevel ) const |
| { |
| // #i51970# must always return the *left* edge of the area used by a level |
| long nPos = static_cast< long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE ); |
| return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos; |
| } |
| |
| size_t ScOutlineWindow::GetLevelFromPos( long nLevelPos ) const |
| { |
| if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1; |
| long nStart = SC_OL_POSOFFSET; |
| if ( nLevelPos < nStart ) return SC_OL_NOLEVEL; |
| size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE ); |
| return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL; |
| } |
| |
| long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const |
| { |
| long nDocPos = mbHoriz ? |
| mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, sal_True ).X() : |
| mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, sal_True ).Y(); |
| return mnMainFirstPos + nDocPos; |
| } |
| |
| long ScOutlineWindow::GetHeaderEntryPos() const |
| { |
| return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2; |
| } |
| |
| bool ScOutlineWindow::GetEntryPos( |
| size_t nLevel, size_t nEntry, |
| long& rnStartPos, long& rnEndPos, long& rnImagePos ) const |
| { |
| const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry ); |
| if ( !pEntry || !pEntry->IsVisible() ) |
| return false; |
| |
| SCCOLROW nStart = pEntry->GetStart(); |
| SCCOLROW nEnd = pEntry->GetEnd(); |
| |
| long nEntriesSign = mbMirrorEntries ? -1 : 1; |
| |
| // --- common calculation --- |
| |
| rnStartPos = GetColRowPos( nStart ); |
| rnEndPos = GetColRowPos( nEnd + 1 ); |
| |
| bool bHidden = IsHidden( nStart ); |
| rnImagePos = bHidden ? |
| (rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) : |
| rnStartPos + nEntriesSign; |
| long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign + |
| ( mbMirrorEntries ? 1 : 0 )) / 2L; |
| rnImagePos = mbMirrorEntries ? Max( rnImagePos, nCenter ) : Min( rnImagePos, nCenter ); |
| |
| // --- refinements --- |
| |
| // do not cut leftmost/topmost image |
| if ( bHidden && IsFirstVisible( nStart ) ) |
| rnImagePos = rnStartPos; |
| |
| // do not cover previous collapsed image |
| if ( !bHidden && nEntry ) |
| { |
| const ScOutlineEntry* pPrevEntry = GetOutlineEntry( nLevel, nEntry - 1 ); |
| SCCOLROW nPrevEnd = pPrevEntry->GetEnd(); |
| if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) ) |
| { |
| if ( IsFirstVisible( pPrevEntry->GetStart() ) ) |
| rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign; |
| else |
| rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign; |
| rnImagePos = rnStartPos; |
| } |
| } |
| |
| // restrict rnStartPos...rnEndPos to valid area |
| rnStartPos = std::max( rnStartPos, mnMainFirstPos ); |
| rnEndPos = std::max( rnEndPos, mnMainFirstPos ); |
| |
| if ( mbMirrorEntries ) |
| rnImagePos -= SC_OL_BITMAPSIZE - 1; // start pos aligns with right edge of bitmap |
| |
| // --- all rows filtered? --- |
| |
| bool bVisible = true; |
| if ( !mbHoriz ) |
| { |
| bVisible = false; |
| for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow ) |
| bVisible = !IsFiltered( nRow ); |
| } |
| return bVisible; |
| } |
| |
| bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const |
| { |
| bool bRet = nLevel < GetLevelCount(); |
| if ( bRet ) |
| { |
| long nLevelPos = GetLevelPos( nLevel ); |
| if ( nEntry == SC_OL_HEADERENTRY ) |
| rPos = GetPoint( nLevelPos, GetHeaderEntryPos() ); |
| else |
| { |
| long nStartPos, nEndPos, nImagePos; |
| bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ); |
| rPos = GetPoint( nLevelPos, nImagePos ); |
| } |
| } |
| return bRet; |
| } |
| |
| bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const |
| { |
| bool bRet = false; |
| if ( nEntry == SC_OL_HEADERENTRY ) |
| bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount()); |
| else |
| { |
| const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry ); |
| if ( pEntry && pEntry->IsVisible() ) |
| { |
| SCCOLROW nStart, nEnd; |
| GetVisibleRange( nStart, nEnd ); |
| bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd); |
| } |
| } |
| return bRet; |
| } |
| |
| bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const |
| { |
| const ScOutlineArray* pArray = GetOutlineArray(); |
| if ( !pArray ) return false; |
| |
| SCCOLROW nStartIndex, nEndIndex; |
| GetVisibleRange( nStartIndex, nEndIndex ); |
| |
| size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() ); |
| if ( nLevel == SC_OL_NOLEVEL ) |
| return false; |
| |
| // long nLevelPos = GetLevelPos( nLevel ); |
| long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y(); |
| |
| // --- level buttons --- |
| |
| if ( mnHeaderSize > 0 ) |
| { |
| long nImagePos = GetHeaderEntryPos(); |
| if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) ) |
| { |
| rnLevel = nLevel; |
| rnEntry = SC_OL_HEADERENTRY; |
| rbButton = true; |
| return true; |
| } |
| } |
| |
| // --- expand/collapse buttons and expanded lines --- |
| |
| // search outline entries backwards |
| size_t nEntry = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) ); |
| while ( nEntry ) |
| { |
| --nEntry; |
| |
| const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), |
| sal::static_int_cast<sal_uInt16>(nEntry) ); |
| SCCOLROW nStart = pEntry->GetStart(); |
| SCCOLROW nEnd = pEntry->GetEnd(); |
| |
| if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) ) |
| { |
| long nStartPos, nEndPos, nImagePos; |
| if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) ) |
| { |
| rnLevel = nLevel; |
| rnEntry = nEntry; |
| |
| // button? |
| if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) ) |
| { |
| rbButton = true; |
| return true; |
| } |
| |
| // line? |
| if ( mbMirrorEntries ) |
| ::std::swap( nStartPos, nEndPos ); // in RTL mode, nStartPos is the larger value |
| if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) ) |
| { |
| rbButton = false; |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const |
| { |
| bool bButton; |
| bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton ); |
| return bRet && bButton; |
| } |
| |
| bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const |
| { |
| bool bButton; |
| bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton ); |
| return bRet && !bButton; |
| } |
| |
| void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const |
| { |
| ScDBFunc& rFunc = *mrViewData.GetView(); |
| if ( nEntry == SC_OL_HEADERENTRY ) |
| rFunc.SelectLevel( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel) ); |
| else |
| { |
| const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry ); |
| if ( pEntry ) |
| { |
| if ( pEntry->IsHidden() ) |
| rFunc.ShowOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ); |
| else |
| rFunc.HideOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ); |
| } |
| } |
| } |
| |
| void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const |
| { |
| const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry ); |
| if ( pEntry && pEntry->IsHidden() ) |
| DoFunction( nLevel, nEntry ); |
| } |
| |
| void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const |
| { |
| const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry ); |
| if ( pEntry && !pEntry->IsHidden() ) |
| DoFunction( nLevel, nEntry ); |
| } |
| |
| void ScOutlineWindow::Resize() |
| { |
| Window::Resize(); |
| SetHeaderSize( mnHeaderSize ); // recalculates header/group positions |
| if ( !IsFocusButtonVisible() ) |
| { |
| HideFocus(); |
| ShowFocus(); // calculates valid position |
| } |
| } |
| |
| void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt ) |
| { |
| if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && |
| (rDCEvt.GetFlags() & SETTINGS_STYLE) ) |
| { |
| InitSettings(); |
| Invalidate(); |
| } |
| Window::DataChanged( rDCEvt ); |
| } |
| |
| // drawing -------------------------------------------------------------------- |
| |
| void ScOutlineWindow::SetEntryAreaClipRegion() |
| { |
| SetClipRegion( Rectangle( |
| GetPoint( 0, mnMainFirstPos ), |
| GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ) ) ); |
| } |
| |
| void ScOutlineWindow::DrawLineRel( |
| long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd ) |
| { |
| DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) ); |
| } |
| |
| void ScOutlineWindow::DrawRectRel( |
| long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd ) |
| { |
| DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) ); |
| } |
| |
| void ScOutlineWindow::DrawImageRel( long nLevelPos, long nEntryPos, sal_uInt16 nId ) |
| { |
| DBG_ASSERT( mpSymbols, "ScOutlineWindow::DrawImageRel - no images" ); |
| const Image& rImage = mpSymbols->GetImage( nId ); |
| SetLineColor(); |
| SetFillColor( GetBackground().GetColor() ); |
| Point aPos( GetPoint( nLevelPos, nEntryPos ) ); |
| DrawRect( Rectangle( aPos, rImage.GetSizePixel() ) ); |
| DrawImage( aPos, rImage ); |
| } |
| |
| void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed ) |
| { |
| Point aPos; |
| if ( GetImagePos( nLevel, nEntry, aPos ) ) |
| { |
| DBG_ASSERT( mpSymbols, "ScOutlineWindow::DrawBorderRel - no images" ); |
| sal_uInt16 nId = bPressed ? SC_OL_IMAGE_PRESSED : SC_OL_IMAGE_NOTPRESSED; |
| bool bClip = (nEntry != SC_OL_HEADERENTRY); |
| if ( bClip ) |
| SetEntryAreaClipRegion(); |
| DrawImage( aPos, mpSymbols->GetImage( nId ) ); |
| if ( bClip ) |
| SetClipRegion(); |
| } |
| mbMTPressed = bPressed; |
| } |
| |
| void ScOutlineWindow::ShowFocus() |
| { |
| if ( HasFocus() ) |
| { |
| // first move to a visible position |
| ImplMoveFocusToVisible( true ); |
| |
| if ( IsFocusButtonVisible() ) |
| { |
| Point aPos; |
| if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) ) |
| { |
| aPos += Point( 1, 1 ); |
| maFocusRect = Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) ); |
| bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY); |
| if ( bClip ) |
| SetEntryAreaClipRegion(); |
| InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW ); |
| if ( bClip ) |
| SetClipRegion(); |
| } |
| } |
| } |
| } |
| |
| void ScOutlineWindow::HideFocus() |
| { |
| if ( !maFocusRect.IsEmpty() ) |
| { |
| bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY); |
| if ( bClip ) |
| SetEntryAreaClipRegion(); |
| InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW ); |
| if ( bClip ) |
| SetClipRegion(); |
| maFocusRect.SetEmpty(); |
| } |
| } |
| |
| void ScOutlineWindow::Paint( const Rectangle& /* rRect */ ) |
| { |
| long nEntriesSign = mbMirrorEntries ? -1 : 1; |
| long nLevelsSign = mbMirrorLevels ? -1 : 1; |
| |
| Size aSize = GetOutputSizePixel(); |
| long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1; |
| long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1; |
| |
| SetLineColor( maLineColor ); |
| long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd; |
| DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd ); |
| |
| const ScOutlineArray* pArray = GetOutlineArray(); |
| if ( !pArray ) return; |
| |
| size_t nLevelCount = GetLevelCount(); |
| |
| // --- draw header images --- |
| |
| if ( mnHeaderSize > 0 ) |
| { |
| long nEntryPos = GetHeaderEntryPos(); |
| for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel ) |
| DrawImageRel( GetLevelPos( nLevel ), nEntryPos, static_cast< sal_uInt16 >( nLevel + 1 ) ); |
| |
| SetLineColor( maLineColor ); |
| long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1)); |
| DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos ); |
| } |
| |
| // --- draw lines & collapse/expand images --- |
| |
| SetEntryAreaClipRegion(); |
| |
| SCCOLROW nStartIndex, nEndIndex; |
| GetVisibleRange( nStartIndex, nEndIndex ); |
| |
| for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel ) |
| { |
| long nLevelPos = GetLevelPos( nLevel ); |
| long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0; |
| |
| size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) ); |
| size_t nEntry; |
| |
| // first draw all lines in the current level |
| SetLineColor(); |
| SetFillColor( maLineColor ); |
| for ( nEntry = 0; nEntry < nEntryCount; ++nEntry ) |
| { |
| const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), |
| sal::static_int_cast<sal_uInt16>(nEntry) ); |
| SCCOLROW nStart = pEntry->GetStart(); |
| SCCOLROW nEnd = pEntry->GetEnd(); |
| |
| // visible range? |
| bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex); |
| // find output coordinates |
| if ( bDraw ) |
| bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos ); |
| // draw, if not collapsed |
| if ( bDraw && !pEntry->IsHidden() ) |
| { |
| if ( nStart >= nStartIndex ) |
| nEntryPos1 += nEntriesSign; |
| nEntryPos2 -= 2 * nEntriesSign; |
| long nLinePos = nLevelPos; |
| if ( mbMirrorLevels ) |
| nLinePos += SC_OL_BITMAPSIZE - 1; // align with right edge of bitmap |
| DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 ); |
| |
| if ( nEnd <= nEndIndex ) |
| DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign, |
| nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 ); |
| } |
| } |
| |
| // draw all images in the level from last to first |
| nEntry = nEntryCount; |
| while ( nEntry ) |
| { |
| --nEntry; |
| |
| const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), |
| sal::static_int_cast<sal_uInt16>(nEntry) ); |
| SCCOLROW nStart = pEntry->GetStart(); |
| // SCCOLROW nEnd = pEntry->GetEnd(); |
| |
| // visible range? |
| bool bDraw = (nStartIndex <= nStart) && (nStart <= nEndIndex + 1); |
| // find output coordinates |
| if ( bDraw ) |
| bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos ); |
| // draw, if not hidden by higher levels |
| if ( bDraw ) |
| { |
| sal_uInt16 nImageId = pEntry->IsHidden() ? SC_OL_IMAGE_PLUS : SC_OL_IMAGE_MINUS; |
| DrawImageRel( nLevelPos, nImagePos, nImageId ); |
| } |
| } |
| } |
| |
| SetClipRegion(); |
| |
| if ( !mbDontDrawFocus ) |
| ShowFocus(); |
| } |
| |
| // focus ---------------------------------------------------------------------- |
| |
| /** Increments or decrements a value and wraps at the specified limits. |
| @return true = value wrapped. */ |
| bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward ) |
| { |
| DBG_ASSERT( nMin <= nMax, "lcl_RotateValue - invalid range" ); |
| DBG_ASSERT( nMax < static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" ); |
| bool bWrap = false; |
| if ( bForward ) |
| { |
| if ( rnValue < nMax ) |
| ++rnValue; |
| else |
| { |
| rnValue = nMin; |
| bWrap = true; |
| } |
| } |
| else |
| { |
| if ( rnValue > nMin ) |
| --rnValue; |
| else |
| { |
| rnValue = nMax; |
| bWrap = true; |
| } |
| } |
| return bWrap; |
| } |
| |
| bool ScOutlineWindow::IsFocusButtonVisible() const |
| { |
| return IsButtonVisible( mnFocusLevel, mnFocusEntry ); |
| } |
| |
| bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward, bool bFindVisible ) |
| { |
| const ScOutlineArray* pArray = GetOutlineArray(); |
| if ( !pArray ) |
| return false; |
| |
| bool bWrapped = false; |
| size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(mnFocusLevel) ); |
| // #i29530# entry count may be decreased after changing active sheet |
| if( mnFocusEntry >= nEntryCount ) |
| mnFocusEntry = SC_OL_HEADERENTRY; |
| size_t nOldEntry = mnFocusEntry; |
| |
| do |
| { |
| if ( mnFocusEntry == SC_OL_HEADERENTRY ) |
| { |
| // move from header to first or last entry |
| if ( nEntryCount > 0 ) |
| mnFocusEntry = bForward ? 0 : (nEntryCount - 1); |
| /* wrapped, if forward from right header to first entry, |
| or if backward from left header to last entry */ |
| // Header and entries are now always in consistent order, |
| // so there's no need to check for mirroring here. |
| if ( !nEntryCount || !bForward ) |
| bWrapped = true; |
| } |
| else if ( lcl_RotateValue( mnFocusEntry, 0, nEntryCount - 1, bForward ) ) |
| { |
| // lcl_RotateValue returns true -> wrapped the entry range -> move to header |
| mnFocusEntry = SC_OL_HEADERENTRY; |
| /* wrapped, if forward from last entry to left header, |
| or if backward from first entry to right header */ |
| if ( bForward ) |
| bWrapped = true; |
| } |
| } |
| while ( bFindVisible && !IsFocusButtonVisible() && (nOldEntry != mnFocusEntry) ); |
| |
| return bWrapped; |
| } |
| |
| bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward ) |
| { |
| const ScOutlineArray* pArray = GetOutlineArray(); |
| if ( !pArray ) |
| return false; |
| |
| bool bWrapped = false; |
| size_t nLevelCount = GetLevelCount(); |
| |
| if ( mnFocusEntry == SC_OL_HEADERENTRY ) |
| { |
| if ( nLevelCount > 0 ) |
| bWrapped = lcl_RotateValue( mnFocusLevel, 0, nLevelCount - 1, bForward ); |
| } |
| else |
| { |
| const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(mnFocusLevel), |
| sal::static_int_cast<sal_uInt16>(mnFocusEntry) ); |
| if ( pEntry ) |
| { |
| SCCOLROW nStart = pEntry->GetStart(); |
| SCCOLROW nEnd = pEntry->GetEnd(); |
| size_t nNewLevel = mnFocusLevel; |
| size_t nNewEntry = 0; |
| |
| bool bFound = false; |
| if ( bForward && (mnFocusLevel + 2 < nLevelCount) ) |
| { |
| // next level -> find first child entry |
| nNewLevel = mnFocusLevel + 1; |
| // TODO - change ScOutlineArray interface to size_t usage |
| sal_uInt16 nTmpEntry = 0; |
| bFound = pArray->GetEntryIndexInRange( sal::static_int_cast<sal_uInt16>(nNewLevel), nStart, nEnd, nTmpEntry ); |
| nNewEntry = nTmpEntry; |
| } |
| else if ( !bForward && (mnFocusLevel > 0) ) |
| { |
| // previous level -> find parent entry |
| nNewLevel = mnFocusLevel - 1; |
| // TODO - change ScOutlineArray interface to size_t usage |
| sal_uInt16 nTmpEntry = 0; |
| bFound = pArray->GetEntryIndex( sal::static_int_cast<sal_uInt16>(nNewLevel), nStart, nTmpEntry ); |
| nNewEntry = nTmpEntry; |
| } |
| |
| if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) ) |
| { |
| mnFocusLevel = nNewLevel; |
| mnFocusEntry = nNewEntry; |
| } |
| } |
| } |
| |
| return bWrapped; |
| } |
| |
| bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward, bool bFindVisible ) |
| { |
| bool bRet = false; |
| size_t nOldLevel = mnFocusLevel; |
| size_t nOldEntry = mnFocusEntry; |
| |
| do |
| { |
| /* one level up, if backward from left header, |
| or one level down, if forward from right header */ |
| if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) ) |
| bRet |= ImplMoveFocusByLevel( bForward ); |
| // move to next/previous entry |
| bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false ); |
| bRet |= bWrapInLevel; |
| /* one level up, if wrapped backward to right header, |
| or one level down, if wrapped forward to right header */ |
| if ( bForward && bWrapInLevel ) |
| bRet |= ImplMoveFocusByLevel( bForward ); |
| } |
| while ( bFindVisible && !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) ); |
| |
| return bRet; |
| } |
| |
| void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward ) |
| { |
| // first try to find an entry in the same level |
| if ( !IsFocusButtonVisible() ) |
| ImplMoveFocusByEntry( bForward, true ); |
| // then try to find any other entry |
| if ( !IsFocusButtonVisible() ) |
| ImplMoveFocusByTabOrder( bForward, true ); |
| } |
| |
| void ScOutlineWindow::MoveFocusByEntry( bool bForward ) |
| { |
| HideFocus(); |
| ImplMoveFocusByEntry( bForward, true ); |
| ShowFocus(); |
| } |
| |
| void ScOutlineWindow::MoveFocusByLevel( bool bForward ) |
| { |
| HideFocus(); |
| ImplMoveFocusByLevel( bForward ); |
| ShowFocus(); |
| } |
| |
| void ScOutlineWindow::MoveFocusByTabOrder( bool bForward ) |
| { |
| HideFocus(); |
| ImplMoveFocusByTabOrder( bForward, true ); |
| ShowFocus(); |
| } |
| |
| void ScOutlineWindow::GetFocus() |
| { |
| Window::GetFocus(); |
| ShowFocus(); |
| } |
| |
| void ScOutlineWindow::LoseFocus() |
| { |
| HideFocus(); |
| Window::LoseFocus(); |
| } |
| |
| |
| // mouse ---------------------------------------------------------------------- |
| |
| void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry ) |
| { |
| mbMTActive = true; |
| mnMTLevel = nLevel; |
| mnMTEntry = nEntry; |
| DrawBorderRel( nLevel, nEntry, true ); |
| } |
| |
| void ScOutlineWindow::EndMouseTracking() |
| { |
| if ( mbMTPressed ) |
| DrawBorderRel( mnMTLevel, mnMTEntry, false ); |
| mbMTActive = false; |
| } |
| |
| void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt ) |
| { |
| if ( IsMouseTracking() ) |
| { |
| size_t nLevel, nEntry; |
| bool bHit = false; |
| |
| if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) ) |
| bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry); |
| |
| if ( bHit != mbMTPressed ) |
| DrawBorderRel( mnMTLevel, mnMTEntry, bHit ); |
| } |
| } |
| |
| void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt ) |
| { |
| if ( IsMouseTracking() ) |
| { |
| EndMouseTracking(); |
| |
| size_t nLevel, nEntry; |
| if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) ) |
| if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) ) |
| DoFunction( nLevel, nEntry ); |
| } |
| } |
| |
| void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt ) |
| { |
| size_t nLevel, nEntry; |
| bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ); |
| if ( bHit ) |
| StartMouseTracking( nLevel, nEntry ); |
| else if ( rMEvt.GetClicks() == 2 ) |
| { |
| bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry ); |
| if ( bHit ) |
| DoFunction( nLevel, nEntry ); |
| } |
| |
| // if an item has been hit and window is focused, move focus to this item |
| if ( bHit && HasFocus() ) |
| { |
| HideFocus(); |
| mnFocusLevel = nLevel; |
| mnFocusEntry = nEntry; |
| ShowFocus(); |
| } |
| } |
| |
| |
| // keyboard ------------------------------------------------------------------- |
| |
| void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt ) |
| { |
| const KeyCode& rKCode = rKEvt.GetKeyCode(); |
| bool bNoMod = !rKCode.GetModifier(); |
| bool bShift = (rKCode.GetModifier() == KEY_SHIFT); |
| bool bCtrl = (rKCode.GetModifier() == KEY_MOD1); |
| |
| sal_uInt16 nCode = rKCode.GetCode(); |
| bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN); |
| bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT); |
| |
| // TAB key |
| if ( (nCode == KEY_TAB) && (bNoMod || bShift) ) |
| // move forward without SHIFT key |
| MoveFocusByTabOrder( bNoMod ); // TAB uses logical order, regardless of mirroring |
| |
| // LEFT/RIGHT/UP/DOWN keys |
| else if ( bNoMod && (bUpDownKey || bLeftRightKey) ) |
| { |
| bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT); |
| if ( mbHoriz == bLeftRightKey ) |
| // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical |
| MoveFocusByEntry( bForward != mbMirrorEntries ); |
| else |
| // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal |
| MoveFocusByLevel( bForward != mbMirrorLevels ); |
| } |
| |
| // CTRL + number |
| else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) ) |
| { |
| size_t nLevel = static_cast< size_t >( nCode - KEY_1 ); |
| if ( nLevel < GetLevelCount() ) |
| DoFunction( nLevel, SC_OL_HEADERENTRY ); |
| } |
| |
| // other key codes |
| else switch ( rKCode.GetFullCode() ) |
| { |
| case KEY_ADD: DoExpand( mnFocusLevel, mnFocusEntry ); break; |
| case KEY_SUBTRACT: DoCollapse( mnFocusLevel, mnFocusEntry ); break; |
| case KEY_SPACE: |
| case KEY_RETURN: DoFunction( mnFocusLevel, mnFocusEntry ); break; |
| default: Window::KeyInput( rKEvt ); |
| } |
| } |
| |
| |
| // ============================================================================ |
| |