update dev300-m58
[ooovba.git] / sc / source / ui / view / olinewin.cxx
blob82d66d154c09d4e04a930d1ae4159277a166c7ce
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: olinewin.cxx,v $
10 * $Revision: 1.15.32.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
35 #include <vcl/svapp.hxx>
36 #include <vcl/taskpanelist.hxx>
38 #include "olinewin.hxx"
39 #include "olinetab.hxx"
40 #include "document.hxx"
41 #include "dbfunc.hxx"
42 #include "sc.hrc"
44 // ============================================================================
46 const long SC_OL_BITMAPSIZE = 12;
47 const long SC_OL_POSOFFSET = 2;
49 const size_t SC_OL_NOLEVEL = static_cast< size_t >( -1 );
50 const size_t SC_OL_HEADERENTRY = static_cast< size_t >( -1 );
52 const USHORT SC_OL_IMAGE_PLUS = 9;
53 const USHORT SC_OL_IMAGE_MINUS = SC_OL_IMAGE_PLUS + 1;
54 const USHORT SC_OL_IMAGE_NOTPRESSED = SC_OL_IMAGE_MINUS + 1;
55 const USHORT SC_OL_IMAGE_PRESSED = SC_OL_IMAGE_NOTPRESSED + 1;
57 // ============================================================================
59 ScOutlineWindow::ScOutlineWindow( Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) :
60 Window( pParent ),
61 mrViewData( *pViewData ),
62 meWhich( eWhich ),
63 mbHoriz( eMode == SC_OUTLINE_HOR ),
64 mbMirrorEntries( false ), // updated in SetHeaderSize
65 mbMirrorLevels( false ), // updated in SetHeaderSize
66 mpSymbols( NULL ),
67 maLineColor( COL_BLACK ),
68 mnHeaderSize( 0 ),
69 mnHeaderPos( 0 ),
70 mnMainFirstPos( 0 ),
71 mnMainLastPos( 0 ),
72 mbMTActive( false ),
73 mbMTPressed( false ),
74 mnFocusLevel( 0 ),
75 mnFocusEntry( SC_OL_HEADERENTRY ),
76 mbDontDrawFocus( false )
78 EnableRTL( FALSE ); // mirroring is done manually
80 InitSettings();
81 maFocusRect.SetEmpty();
82 SetHeaderSize( 0 );
84 // insert the window into task pane list for "F6 cycling"
85 if( SystemWindow* pSysWin = GetSystemWindow() )
86 if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
87 pTaskPaneList->AddWindow( this );
90 ScOutlineWindow::~ScOutlineWindow()
92 // remove the window from task pane list
93 if( SystemWindow* pSysWin = GetSystemWindow() )
94 if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
95 pTaskPaneList->RemoveWindow( this );
98 void ScOutlineWindow::SetHeaderSize( long nNewSize )
100 BOOL bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() );
101 mbMirrorEntries = bLayoutRTL && mbHoriz;
102 mbMirrorLevels = bLayoutRTL && !mbHoriz;
104 bool bNew = (nNewSize != mnHeaderSize);
105 mnHeaderSize = nNewSize;
106 mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0;
107 mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize;
108 mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1;
109 if ( bNew )
110 Invalidate();
113 long ScOutlineWindow::GetDepthSize() const
115 long nSize = GetLevelCount() * SC_OL_BITMAPSIZE;
116 if ( nSize > 0 )
117 nSize += 2 * SC_OL_POSOFFSET + 1;
118 return nSize;
121 void ScOutlineWindow::ScrollPixel( long nDiff )
123 HideFocus();
124 mbDontDrawFocus = true;
126 long nStart = mnMainFirstPos;
127 long nEnd = mnMainLastPos;
129 long nInvStart, nInvEnd;
130 if (nDiff < 0)
132 nStart -= nDiff;
133 nInvStart = nEnd + nDiff;
134 nInvEnd = nEnd;
136 else
138 nEnd -= nDiff;
139 nInvStart = nStart;
140 nInvEnd = nStart + nDiff;
143 ScrollRel( nDiff, nStart, nEnd );
144 Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) );
145 Update();
147 // if focus becomes invisible, move it to next visible button
148 ImplMoveFocusToVisible( nDiff < 0 );
150 mbDontDrawFocus = false;
151 ShowFocus();
154 void ScOutlineWindow::ScrollRel( long nEntryDiff, long nEntryStart, long nEntryEnd )
156 Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) );
157 if ( mbHoriz )
158 Scroll( nEntryDiff, 0, aRect );
159 else
160 Scroll( 0, nEntryDiff, aRect );
163 // internal -------------------------------------------------------------------
165 void ScOutlineWindow::InitSettings()
167 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
168 SetBackground( rStyleSettings.GetFaceColor() );
169 maLineColor = rStyleSettings.GetButtonTextColor();
170 mpSymbols = ScGlobal::GetOutlineSymbols( !!GetBackground().GetColor().IsDark() );
171 Invalidate();
174 const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const
176 const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() );
177 if ( !pTable ) return NULL;
178 return mbHoriz ? pTable->GetColArray() : pTable->GetRowArray();
181 const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const
183 const ScOutlineArray* pArray = GetOutlineArray();
184 return pArray ? pArray->GetEntry( sal::static_int_cast<USHORT>(nLevel), sal::static_int_cast<USHORT>(nEntry) ) : NULL;
187 bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const
189 return mbHoriz ?
190 GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) :
191 GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab());
194 bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const
196 // columns cannot be filtered
197 return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() );
200 bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const
202 bool bAllHidden = true;
203 for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos )
204 bAllHidden = IsHidden( nPos );
205 return bAllHidden;
208 void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const
210 if ( mbHoriz )
212 rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) );
213 rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) );
215 else
217 rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) );
218 rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) );
221 // include collapsed columns/rows in front of visible range
222 while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) )
223 --rnColRowStart;
226 Point ScOutlineWindow::GetPoint( long nLevelPos, long nEntryPos ) const
228 return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos );
231 Rectangle ScOutlineWindow::GetRectangle(
232 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd ) const
234 return Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
237 long ScOutlineWindow::GetOutputSizeLevel() const
239 Size aSize( GetOutputSizePixel() );
240 return mbHoriz ? aSize.Height() : aSize.Width();
243 long ScOutlineWindow::GetOutputSizeEntry() const
245 Size aSize( GetOutputSizePixel() );
246 return mbHoriz ? aSize.Width() : aSize.Height();
249 size_t ScOutlineWindow::GetLevelCount() const
251 const ScOutlineArray* pArray = GetOutlineArray();
252 size_t nLevelCount = pArray ? pArray->GetDepth() : 0;
253 return nLevelCount ? (nLevelCount + 1) : 0;
256 long ScOutlineWindow::GetLevelPos( size_t nLevel ) const
258 // #i51970# must always return the *left* edge of the area used by a level
259 long nPos = static_cast< long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE );
260 return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos;
263 size_t ScOutlineWindow::GetLevelFromPos( long nLevelPos ) const
265 if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1;
266 long nStart = SC_OL_POSOFFSET;
267 if ( nLevelPos < nStart ) return SC_OL_NOLEVEL;
268 size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE );
269 return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL;
272 long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const
274 long nDocPos = mbHoriz ?
275 mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, TRUE ).X() :
276 mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, TRUE ).Y();
277 return mnMainFirstPos + nDocPos;
280 long ScOutlineWindow::GetHeaderEntryPos() const
282 return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2;
285 bool ScOutlineWindow::GetEntryPos(
286 size_t nLevel, size_t nEntry,
287 long& rnStartPos, long& rnEndPos, long& rnImagePos ) const
289 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
290 if ( !pEntry || !pEntry->IsVisible() )
291 return false;
293 SCCOLROW nStart = pEntry->GetStart();
294 SCCOLROW nEnd = pEntry->GetEnd();
296 long nEntriesSign = mbMirrorEntries ? -1 : 1;
298 // --- common calculation ---
300 rnStartPos = GetColRowPos( nStart );
301 rnEndPos = GetColRowPos( nEnd + 1 );
303 bool bHidden = IsHidden( nStart );
304 rnImagePos = bHidden ?
305 (rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) :
306 rnStartPos + nEntriesSign;
307 long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign +
308 ( mbMirrorEntries ? 1 : 0 )) / 2L;
309 rnImagePos = mbMirrorEntries ? Max( rnImagePos, nCenter ) : Min( rnImagePos, nCenter );
311 // --- refinements ---
313 // do not cut leftmost/topmost image
314 if ( bHidden && IsFirstVisible( nStart ) )
315 rnImagePos = rnStartPos;
317 // do not cover previous collapsed image
318 if ( !bHidden && nEntry )
320 const ScOutlineEntry* pPrevEntry = GetOutlineEntry( nLevel, nEntry - 1 );
321 SCCOLROW nPrevEnd = pPrevEntry->GetEnd();
322 if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) )
324 if ( IsFirstVisible( pPrevEntry->GetStart() ) )
325 rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign;
326 else
327 rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign;
328 rnImagePos = rnStartPos;
332 // restrict rnStartPos...rnEndPos to valid area
333 rnStartPos = std::max( rnStartPos, mnMainFirstPos );
334 rnEndPos = std::max( rnEndPos, mnMainFirstPos );
336 if ( mbMirrorEntries )
337 rnImagePos -= SC_OL_BITMAPSIZE - 1; // start pos aligns with right edge of bitmap
339 // --- all rows filtered? ---
341 bool bVisible = true;
342 if ( !mbHoriz )
344 bVisible = false;
345 for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow )
346 bVisible = !IsFiltered( nRow );
348 return bVisible;
351 bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const
353 bool bRet = nLevel < GetLevelCount();
354 if ( bRet )
356 long nLevelPos = GetLevelPos( nLevel );
357 if ( nEntry == SC_OL_HEADERENTRY )
358 rPos = GetPoint( nLevelPos, GetHeaderEntryPos() );
359 else
361 long nStartPos, nEndPos, nImagePos;
362 bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos );
363 rPos = GetPoint( nLevelPos, nImagePos );
366 return bRet;
369 bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const
371 bool bRet = false;
372 if ( nEntry == SC_OL_HEADERENTRY )
373 bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount());
374 else
376 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
377 if ( pEntry && pEntry->IsVisible() )
379 SCCOLROW nStart, nEnd;
380 GetVisibleRange( nStart, nEnd );
381 bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd);
384 return bRet;
387 bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const
389 const ScOutlineArray* pArray = GetOutlineArray();
390 if ( !pArray ) return false;
392 SCCOLROW nStartIndex, nEndIndex;
393 GetVisibleRange( nStartIndex, nEndIndex );
395 size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() );
396 if ( nLevel == SC_OL_NOLEVEL )
397 return false;
399 // long nLevelPos = GetLevelPos( nLevel );
400 long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y();
402 // --- level buttons ---
404 if ( mnHeaderSize > 0 )
406 long nImagePos = GetHeaderEntryPos();
407 if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
409 rnLevel = nLevel;
410 rnEntry = SC_OL_HEADERENTRY;
411 rbButton = true;
412 return true;
416 // --- expand/collapse buttons and expanded lines ---
418 // search outline entries backwards
419 size_t nEntry = pArray->GetCount( sal::static_int_cast<USHORT>(nLevel) );
420 while ( nEntry )
422 --nEntry;
424 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<USHORT>(nLevel),
425 sal::static_int_cast<USHORT>(nEntry) );
426 SCCOLROW nStart = pEntry->GetStart();
427 SCCOLROW nEnd = pEntry->GetEnd();
429 if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) )
431 long nStartPos, nEndPos, nImagePos;
432 if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) )
434 rnLevel = nLevel;
435 rnEntry = nEntry;
437 // button?
438 if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
440 rbButton = true;
441 return true;
444 // line?
445 if ( mbMirrorEntries )
446 ::std::swap( nStartPos, nEndPos ); // in RTL mode, nStartPos is the larger value
447 if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) )
449 rbButton = false;
450 return true;
456 return false;
459 bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
461 bool bButton;
462 bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
463 return bRet && bButton;
466 bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
468 bool bButton;
469 bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
470 return bRet && !bButton;
473 void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const
475 ScDBFunc& rFunc = *mrViewData.GetView();
476 if ( nEntry == SC_OL_HEADERENTRY )
477 rFunc.SelectLevel( mbHoriz, sal::static_int_cast<USHORT>(nLevel) );
478 else
480 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
481 if ( pEntry )
483 if ( pEntry->IsHidden() )
484 rFunc.ShowOutline( mbHoriz, sal::static_int_cast<USHORT>(nLevel), sal::static_int_cast<USHORT>(nEntry) );
485 else
486 rFunc.HideOutline( mbHoriz, sal::static_int_cast<USHORT>(nLevel), sal::static_int_cast<USHORT>(nEntry) );
491 void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const
493 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
494 if ( pEntry && pEntry->IsHidden() )
495 DoFunction( nLevel, nEntry );
498 void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const
500 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
501 if ( pEntry && !pEntry->IsHidden() )
502 DoFunction( nLevel, nEntry );
505 void ScOutlineWindow::Resize()
507 Window::Resize();
508 SetHeaderSize( mnHeaderSize ); // recalculates header/group positions
509 if ( !IsFocusButtonVisible() )
511 HideFocus();
512 ShowFocus(); // calculates valid position
516 void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt )
518 if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
519 (rDCEvt.GetFlags() & SETTINGS_STYLE) )
521 InitSettings();
522 Invalidate();
524 Window::DataChanged( rDCEvt );
527 // drawing --------------------------------------------------------------------
529 void ScOutlineWindow::SetEntryAreaClipRegion()
531 SetClipRegion( Rectangle(
532 GetPoint( 0, mnMainFirstPos ),
533 GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ) ) );
536 void ScOutlineWindow::DrawLineRel(
537 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
539 DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
542 void ScOutlineWindow::DrawRectRel(
543 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
545 DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) );
548 void ScOutlineWindow::DrawImageRel( long nLevelPos, long nEntryPos, USHORT nId )
550 DBG_ASSERT( mpSymbols, "ScOutlineWindow::DrawImageRel - no images" );
551 const Image& rImage = mpSymbols->GetImage( nId );
552 SetLineColor();
553 SetFillColor( GetBackground().GetColor() );
554 Point aPos( GetPoint( nLevelPos, nEntryPos ) );
555 DrawRect( Rectangle( aPos, rImage.GetSizePixel() ) );
556 DrawImage( aPos, rImage );
559 void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed )
561 Point aPos;
562 if ( GetImagePos( nLevel, nEntry, aPos ) )
564 DBG_ASSERT( mpSymbols, "ScOutlineWindow::DrawBorderRel - no images" );
565 USHORT nId = bPressed ? SC_OL_IMAGE_PRESSED : SC_OL_IMAGE_NOTPRESSED;
566 bool bClip = (nEntry != SC_OL_HEADERENTRY);
567 if ( bClip )
568 SetEntryAreaClipRegion();
569 DrawImage( aPos, mpSymbols->GetImage( nId ) );
570 if ( bClip )
571 SetClipRegion();
573 mbMTPressed = bPressed;
576 void ScOutlineWindow::ShowFocus()
578 if ( HasFocus() )
580 // first move to a visible position
581 ImplMoveFocusToVisible( true );
583 if ( IsFocusButtonVisible() )
585 Point aPos;
586 if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) )
588 aPos += Point( 1, 1 );
589 maFocusRect = Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) );
590 bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
591 if ( bClip )
592 SetEntryAreaClipRegion();
593 InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
594 if ( bClip )
595 SetClipRegion();
601 void ScOutlineWindow::HideFocus()
603 if ( !maFocusRect.IsEmpty() )
605 bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
606 if ( bClip )
607 SetEntryAreaClipRegion();
608 InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
609 if ( bClip )
610 SetClipRegion();
611 maFocusRect.SetEmpty();
615 void ScOutlineWindow::Paint( const Rectangle& /* rRect */ )
617 long nEntriesSign = mbMirrorEntries ? -1 : 1;
618 long nLevelsSign = mbMirrorLevels ? -1 : 1;
620 Size aSize = GetOutputSizePixel();
621 long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1;
622 long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1;
624 SetLineColor( maLineColor );
625 long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd;
626 DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd );
628 const ScOutlineArray* pArray = GetOutlineArray();
629 if ( !pArray ) return;
631 size_t nLevelCount = GetLevelCount();
633 // --- draw header images ---
635 if ( mnHeaderSize > 0 )
637 long nEntryPos = GetHeaderEntryPos();
638 for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel )
639 DrawImageRel( GetLevelPos( nLevel ), nEntryPos, static_cast< USHORT >( nLevel + 1 ) );
641 SetLineColor( maLineColor );
642 long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1));
643 DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos );
646 // --- draw lines & collapse/expand images ---
648 SetEntryAreaClipRegion();
650 SCCOLROW nStartIndex, nEndIndex;
651 GetVisibleRange( nStartIndex, nEndIndex );
653 for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel )
655 long nLevelPos = GetLevelPos( nLevel );
656 long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0;
658 size_t nEntryCount = pArray->GetCount( sal::static_int_cast<USHORT>(nLevel) );
659 size_t nEntry;
661 // first draw all lines in the current level
662 SetLineColor();
663 SetFillColor( maLineColor );
664 for ( nEntry = 0; nEntry < nEntryCount; ++nEntry )
666 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<USHORT>(nLevel),
667 sal::static_int_cast<USHORT>(nEntry) );
668 SCCOLROW nStart = pEntry->GetStart();
669 SCCOLROW nEnd = pEntry->GetEnd();
671 // visible range?
672 bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex);
673 // find output coordinates
674 if ( bDraw )
675 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
676 // draw, if not collapsed
677 if ( bDraw && !pEntry->IsHidden() )
679 if ( nStart >= nStartIndex )
680 nEntryPos1 += nEntriesSign;
681 nEntryPos2 -= 2 * nEntriesSign;
682 long nLinePos = nLevelPos;
683 if ( mbMirrorLevels )
684 nLinePos += SC_OL_BITMAPSIZE - 1; // align with right edge of bitmap
685 DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 );
687 if ( nEnd <= nEndIndex )
688 DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign,
689 nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 );
693 // draw all images in the level from last to first
694 nEntry = nEntryCount;
695 while ( nEntry )
697 --nEntry;
699 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<USHORT>(nLevel),
700 sal::static_int_cast<USHORT>(nEntry) );
701 SCCOLROW nStart = pEntry->GetStart();
702 // SCCOLROW nEnd = pEntry->GetEnd();
704 // visible range?
705 bool bDraw = (nStartIndex <= nStart) && (nStart <= nEndIndex + 1);
706 // find output coordinates
707 if ( bDraw )
708 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
709 // draw, if not hidden by higher levels
710 if ( bDraw )
712 USHORT nImageId = pEntry->IsHidden() ? SC_OL_IMAGE_PLUS : SC_OL_IMAGE_MINUS;
713 DrawImageRel( nLevelPos, nImagePos, nImageId );
718 SetClipRegion();
720 if ( !mbDontDrawFocus )
721 ShowFocus();
724 // focus ----------------------------------------------------------------------
726 /** Increments or decrements a value and wraps at the specified limits.
727 @return true = value wrapped. */
728 bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward )
730 DBG_ASSERT( nMin <= nMax, "lcl_RotateValue - invalid range" );
731 DBG_ASSERT( nMax < static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" );
732 bool bWrap = false;
733 if ( bForward )
735 if ( rnValue < nMax )
736 ++rnValue;
737 else
739 rnValue = nMin;
740 bWrap = true;
743 else
745 if ( rnValue > nMin )
746 --rnValue;
747 else
749 rnValue = nMax;
750 bWrap = true;
753 return bWrap;
756 bool ScOutlineWindow::IsFocusButtonVisible() const
758 return IsButtonVisible( mnFocusLevel, mnFocusEntry );
761 bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward, bool bFindVisible )
763 const ScOutlineArray* pArray = GetOutlineArray();
764 if ( !pArray )
765 return false;
767 bool bWrapped = false;
768 size_t nEntryCount = pArray->GetCount( sal::static_int_cast<USHORT>(mnFocusLevel) );
769 // #i29530# entry count may be decreased after changing active sheet
770 if( mnFocusEntry >= nEntryCount )
771 mnFocusEntry = SC_OL_HEADERENTRY;
772 size_t nOldEntry = mnFocusEntry;
776 if ( mnFocusEntry == SC_OL_HEADERENTRY )
778 // move from header to first or last entry
779 if ( nEntryCount > 0 )
780 mnFocusEntry = bForward ? 0 : (nEntryCount - 1);
781 /* wrapped, if forward from right header to first entry,
782 or if backward from left header to last entry */
783 // Header and entries are now always in consistent order,
784 // so there's no need to check for mirroring here.
785 if ( !nEntryCount || !bForward )
786 bWrapped = true;
788 else if ( lcl_RotateValue( mnFocusEntry, 0, nEntryCount - 1, bForward ) )
790 // lcl_RotateValue returns true -> wrapped the entry range -> move to header
791 mnFocusEntry = SC_OL_HEADERENTRY;
792 /* wrapped, if forward from last entry to left header,
793 or if backward from first entry to right header */
794 if ( bForward )
795 bWrapped = true;
798 while ( bFindVisible && !IsFocusButtonVisible() && (nOldEntry != mnFocusEntry) );
800 return bWrapped;
803 bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward )
805 const ScOutlineArray* pArray = GetOutlineArray();
806 if ( !pArray )
807 return false;
809 bool bWrapped = false;
810 size_t nLevelCount = GetLevelCount();
812 if ( mnFocusEntry == SC_OL_HEADERENTRY )
814 if ( nLevelCount > 0 )
815 bWrapped = lcl_RotateValue( mnFocusLevel, 0, nLevelCount - 1, bForward );
817 else
819 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<USHORT>(mnFocusLevel),
820 sal::static_int_cast<USHORT>(mnFocusEntry) );
821 if ( pEntry )
823 SCCOLROW nStart = pEntry->GetStart();
824 SCCOLROW nEnd = pEntry->GetEnd();
825 size_t nNewLevel = mnFocusLevel;
826 size_t nNewEntry = 0;
828 bool bFound = false;
829 if ( bForward && (mnFocusLevel + 2 < nLevelCount) )
831 // next level -> find first child entry
832 nNewLevel = mnFocusLevel + 1;
833 // TODO - change ScOutlineArray interface to size_t usage
834 USHORT nTmpEntry = 0;
835 bFound = pArray->GetEntryIndexInRange( sal::static_int_cast<USHORT>(nNewLevel), nStart, nEnd, nTmpEntry );
836 nNewEntry = nTmpEntry;
838 else if ( !bForward && (mnFocusLevel > 0) )
840 // previous level -> find parent entry
841 nNewLevel = mnFocusLevel - 1;
842 // TODO - change ScOutlineArray interface to size_t usage
843 USHORT nTmpEntry = 0;
844 bFound = pArray->GetEntryIndex( sal::static_int_cast<USHORT>(nNewLevel), nStart, nTmpEntry );
845 nNewEntry = nTmpEntry;
848 if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) )
850 mnFocusLevel = nNewLevel;
851 mnFocusEntry = nNewEntry;
856 return bWrapped;
859 bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward, bool bFindVisible )
861 bool bRet = false;
862 size_t nOldLevel = mnFocusLevel;
863 size_t nOldEntry = mnFocusEntry;
867 /* one level up, if backward from left header,
868 or one level down, if forward from right header */
869 if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) )
870 bRet |= ImplMoveFocusByLevel( bForward );
871 // move to next/previous entry
872 bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false );
873 bRet |= bWrapInLevel;
874 /* one level up, if wrapped backward to right header,
875 or one level down, if wrapped forward to right header */
876 if ( bForward && bWrapInLevel )
877 bRet |= ImplMoveFocusByLevel( bForward );
879 while ( bFindVisible && !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) );
881 return bRet;
884 void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward )
886 // first try to find an entry in the same level
887 if ( !IsFocusButtonVisible() )
888 ImplMoveFocusByEntry( bForward, true );
889 // then try to find any other entry
890 if ( !IsFocusButtonVisible() )
891 ImplMoveFocusByTabOrder( bForward, true );
894 void ScOutlineWindow::MoveFocusByEntry( bool bForward )
896 HideFocus();
897 ImplMoveFocusByEntry( bForward, true );
898 ShowFocus();
901 void ScOutlineWindow::MoveFocusByLevel( bool bForward )
903 HideFocus();
904 ImplMoveFocusByLevel( bForward );
905 ShowFocus();
908 void ScOutlineWindow::MoveFocusByTabOrder( bool bForward )
910 HideFocus();
911 ImplMoveFocusByTabOrder( bForward, true );
912 ShowFocus();
915 void ScOutlineWindow::GetFocus()
917 Window::GetFocus();
918 ShowFocus();
921 void ScOutlineWindow::LoseFocus()
923 HideFocus();
924 Window::LoseFocus();
928 // mouse ----------------------------------------------------------------------
930 void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry )
932 mbMTActive = true;
933 mnMTLevel = nLevel;
934 mnMTEntry = nEntry;
935 DrawBorderRel( nLevel, nEntry, true );
938 void ScOutlineWindow::EndMouseTracking()
940 if ( mbMTPressed )
941 DrawBorderRel( mnMTLevel, mnMTEntry, false );
942 mbMTActive = false;
945 void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt )
947 if ( IsMouseTracking() )
949 size_t nLevel, nEntry;
950 bool bHit = false;
952 if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
953 bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry);
955 if ( bHit != mbMTPressed )
956 DrawBorderRel( mnMTLevel, mnMTEntry, bHit );
960 void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt )
962 if ( IsMouseTracking() )
964 EndMouseTracking();
966 size_t nLevel, nEntry;
967 if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
968 if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) )
969 DoFunction( nLevel, nEntry );
973 void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt )
975 size_t nLevel, nEntry;
976 bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry );
977 if ( bHit )
978 StartMouseTracking( nLevel, nEntry );
979 else if ( rMEvt.GetClicks() == 2 )
981 bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry );
982 if ( bHit )
983 DoFunction( nLevel, nEntry );
986 // if an item has been hit and window is focused, move focus to this item
987 if ( bHit && HasFocus() )
989 HideFocus();
990 mnFocusLevel = nLevel;
991 mnFocusEntry = nEntry;
992 ShowFocus();
997 // keyboard -------------------------------------------------------------------
999 void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt )
1001 const KeyCode& rKCode = rKEvt.GetKeyCode();
1002 bool bNoMod = !rKCode.GetModifier();
1003 bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
1004 bool bCtrl = (rKCode.GetModifier() == KEY_MOD1);
1006 USHORT nCode = rKCode.GetCode();
1007 bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN);
1008 bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT);
1010 // TAB key
1011 if ( (nCode == KEY_TAB) && (bNoMod || bShift) )
1012 // move forward without SHIFT key
1013 MoveFocusByTabOrder( bNoMod ); // TAB uses logical order, regardless of mirroring
1015 // LEFT/RIGHT/UP/DOWN keys
1016 else if ( bNoMod && (bUpDownKey || bLeftRightKey) )
1018 bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT);
1019 if ( mbHoriz == bLeftRightKey )
1020 // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
1021 MoveFocusByEntry( bForward != mbMirrorEntries );
1022 else
1023 // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
1024 MoveFocusByLevel( bForward != mbMirrorLevels );
1027 // CTRL + number
1028 else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) )
1030 size_t nLevel = static_cast< size_t >( nCode - KEY_1 );
1031 if ( nLevel < GetLevelCount() )
1032 DoFunction( nLevel, SC_OL_HEADERENTRY );
1035 // other key codes
1036 else switch ( rKCode.GetFullCode() )
1038 case KEY_ADD: DoExpand( mnFocusLevel, mnFocusEntry ); break;
1039 case KEY_SUBTRACT: DoCollapse( mnFocusLevel, mnFocusEntry ); break;
1040 case KEY_SPACE:
1041 case KEY_RETURN: DoFunction( mnFocusLevel, mnFocusEntry ); break;
1042 default: Window::KeyInput( rKEvt );
1047 // ============================================================================