Version 4.0.2.1, tag libreoffice-4.0.2.1
[LibreOffice.git] / sc / source / ui / view / olinewin.cxx
blobef85b7d44bf60b3256057adaf8872ae7fc8e977b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/svapp.hxx>
21 #include <vcl/taskpanelist.hxx>
23 #include "olinewin.hxx"
24 #include "olinetab.hxx"
25 #include "document.hxx"
26 #include "dbfunc.hxx"
27 #include "sc.hrc"
29 // ============================================================================
31 const long SC_OL_BITMAPSIZE = 12;
32 const long SC_OL_POSOFFSET = 2;
34 const size_t SC_OL_NOLEVEL = static_cast< size_t >( -1 );
35 const size_t SC_OL_HEADERENTRY = static_cast< size_t >( -1 );
37 const sal_uInt16 SC_OL_IMAGE_PLUS = 9;
38 const sal_uInt16 SC_OL_IMAGE_MINUS = SC_OL_IMAGE_PLUS + 1;
39 const sal_uInt16 SC_OL_IMAGE_NOTPRESSED = SC_OL_IMAGE_MINUS + 1;
40 const sal_uInt16 SC_OL_IMAGE_PRESSED = SC_OL_IMAGE_NOTPRESSED + 1;
42 // ============================================================================
44 ScOutlineWindow::ScOutlineWindow( Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) :
45 Window( pParent ),
46 mrViewData( *pViewData ),
47 meWhich( eWhich ),
48 mbHoriz( eMode == SC_OUTLINE_HOR ),
49 mbMirrorEntries( false ), // updated in SetHeaderSize
50 mbMirrorLevels( false ), // updated in SetHeaderSize
51 mpSymbols( NULL ),
52 maLineColor( COL_BLACK ),
53 mnHeaderSize( 0 ),
54 mnHeaderPos( 0 ),
55 mnMainFirstPos( 0 ),
56 mnMainLastPos( 0 ),
57 mbMTActive( false ),
58 mbMTPressed( false ),
59 mnFocusLevel( 0 ),
60 mnFocusEntry( SC_OL_HEADERENTRY ),
61 mbDontDrawFocus( false )
63 EnableRTL( false ); // mirroring is done manually
65 InitSettings();
66 maFocusRect.SetEmpty();
67 SetHeaderSize( 0 );
69 // insert the window into task pane list for "F6 cycling"
70 if( SystemWindow* pSysWin = GetSystemWindow() )
71 if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
72 pTaskPaneList->AddWindow( this );
75 ScOutlineWindow::~ScOutlineWindow()
77 // remove the window from task pane list
78 if( SystemWindow* pSysWin = GetSystemWindow() )
79 if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
80 pTaskPaneList->RemoveWindow( this );
83 void ScOutlineWindow::SetHeaderSize( long nNewSize )
85 sal_Bool bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() );
86 mbMirrorEntries = bLayoutRTL && mbHoriz;
87 mbMirrorLevels = bLayoutRTL && !mbHoriz;
89 bool bNew = (nNewSize != mnHeaderSize);
90 mnHeaderSize = nNewSize;
91 mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0;
92 mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize;
93 mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1;
94 if ( bNew )
95 Invalidate();
98 long ScOutlineWindow::GetDepthSize() const
100 long nSize = GetLevelCount() * SC_OL_BITMAPSIZE;
101 if ( nSize > 0 )
102 nSize += 2 * SC_OL_POSOFFSET + 1;
103 return nSize;
106 void ScOutlineWindow::ScrollPixel( long nDiff )
108 HideFocus();
109 mbDontDrawFocus = true;
111 long nStart = mnMainFirstPos;
112 long nEnd = mnMainLastPos;
114 long nInvStart, nInvEnd;
115 if (nDiff < 0)
117 nStart -= nDiff;
118 nInvStart = nEnd + nDiff;
119 nInvEnd = nEnd;
121 else
123 nEnd -= nDiff;
124 nInvStart = nStart;
125 nInvEnd = nStart + nDiff;
128 ScrollRel( nDiff, nStart, nEnd );
129 Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) );
130 Update();
132 // if focus becomes invisible, move it to next visible button
133 ImplMoveFocusToVisible( nDiff < 0 );
135 mbDontDrawFocus = false;
136 ShowFocus();
139 void ScOutlineWindow::ScrollRel( long nEntryDiff, long nEntryStart, long nEntryEnd )
141 Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) );
142 if ( mbHoriz )
143 Scroll( nEntryDiff, 0, aRect );
144 else
145 Scroll( 0, nEntryDiff, aRect );
148 // internal -------------------------------------------------------------------
150 void ScOutlineWindow::InitSettings()
152 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
153 SetBackground( rStyleSettings.GetFaceColor() );
154 maLineColor = rStyleSettings.GetButtonTextColor();
155 mpSymbols = ScGlobal::GetOutlineSymbols();
156 Invalidate();
159 const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const
161 const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() );
162 if ( !pTable ) return NULL;
163 return mbHoriz ? pTable->GetColArray() : pTable->GetRowArray();
166 const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const
168 const ScOutlineArray* pArray = GetOutlineArray();
169 return pArray ? pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ) : NULL;
172 bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const
174 return mbHoriz ?
175 GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) :
176 GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab());
179 bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const
181 // columns cannot be filtered
182 return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() );
185 bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const
187 bool bAllHidden = true;
188 for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos )
189 bAllHidden = IsHidden( nPos );
190 return bAllHidden;
193 void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const
195 if ( mbHoriz )
197 rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) );
198 rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) );
200 else
202 rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) );
203 rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) );
206 // include collapsed columns/rows in front of visible range
207 while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) )
208 --rnColRowStart;
211 Point ScOutlineWindow::GetPoint( long nLevelPos, long nEntryPos ) const
213 return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos );
216 Rectangle ScOutlineWindow::GetRectangle(
217 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd ) const
219 return Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
222 long ScOutlineWindow::GetOutputSizeLevel() const
224 Size aSize( GetOutputSizePixel() );
225 return mbHoriz ? aSize.Height() : aSize.Width();
228 long ScOutlineWindow::GetOutputSizeEntry() const
230 Size aSize( GetOutputSizePixel() );
231 return mbHoriz ? aSize.Width() : aSize.Height();
234 size_t ScOutlineWindow::GetLevelCount() const
236 const ScOutlineArray* pArray = GetOutlineArray();
237 size_t nLevelCount = pArray ? pArray->GetDepth() : 0;
238 return nLevelCount ? (nLevelCount + 1) : 0;
241 long ScOutlineWindow::GetLevelPos( size_t nLevel ) const
243 // #i51970# must always return the *left* edge of the area used by a level
244 long nPos = static_cast< long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE );
245 return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos;
248 size_t ScOutlineWindow::GetLevelFromPos( long nLevelPos ) const
250 if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1;
251 long nStart = SC_OL_POSOFFSET;
252 if ( nLevelPos < nStart ) return SC_OL_NOLEVEL;
253 size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE );
254 return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL;
257 long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const
259 long nDocPos = mbHoriz ?
260 mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, sal_True ).X() :
261 mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, sal_True ).Y();
262 return mnMainFirstPos + nDocPos;
265 long ScOutlineWindow::GetHeaderEntryPos() const
267 return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2;
270 bool ScOutlineWindow::GetEntryPos(
271 size_t nLevel, size_t nEntry,
272 long& rnStartPos, long& rnEndPos, long& rnImagePos ) const
274 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
275 if ( !pEntry || !pEntry->IsVisible() )
276 return false;
278 SCCOLROW nStart = pEntry->GetStart();
279 SCCOLROW nEnd = pEntry->GetEnd();
281 long nEntriesSign = mbMirrorEntries ? -1 : 1;
283 // --- common calculation ---
285 rnStartPos = GetColRowPos( nStart );
286 rnEndPos = GetColRowPos( nEnd + 1 );
288 bool bHidden = IsHidden( nStart );
289 rnImagePos = bHidden ?
290 (rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) :
291 rnStartPos + nEntriesSign;
292 long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign +
293 ( mbMirrorEntries ? 1 : 0 )) / 2L;
294 rnImagePos = mbMirrorEntries ? Max( rnImagePos, nCenter ) : Min( rnImagePos, nCenter );
296 // --- refinements ---
298 // do not cut leftmost/topmost image
299 if ( bHidden && IsFirstVisible( nStart ) )
300 rnImagePos = rnStartPos;
302 // do not cover previous collapsed image
303 if ( !bHidden && nEntry )
305 const ScOutlineEntry* pPrevEntry = GetOutlineEntry( nLevel, nEntry - 1 );
306 SCCOLROW nPrevEnd = pPrevEntry->GetEnd();
307 if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) )
309 if ( IsFirstVisible( pPrevEntry->GetStart() ) )
310 rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign;
311 else
312 rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign;
313 rnImagePos = rnStartPos;
317 // restrict rnStartPos...rnEndPos to valid area
318 rnStartPos = std::max( rnStartPos, mnMainFirstPos );
319 rnEndPos = std::max( rnEndPos, mnMainFirstPos );
321 if ( mbMirrorEntries )
322 rnImagePos -= SC_OL_BITMAPSIZE - 1; // start pos aligns with right edge of bitmap
324 // --- all rows filtered? ---
326 bool bVisible = true;
327 if ( !mbHoriz )
329 bVisible = false;
330 for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow )
331 bVisible = !IsFiltered( nRow );
333 return bVisible;
336 bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const
338 bool bRet = nLevel < GetLevelCount();
339 if ( bRet )
341 long nLevelPos = GetLevelPos( nLevel );
342 if ( nEntry == SC_OL_HEADERENTRY )
343 rPos = GetPoint( nLevelPos, GetHeaderEntryPos() );
344 else
346 long nStartPos, nEndPos, nImagePos;
347 bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos );
348 rPos = GetPoint( nLevelPos, nImagePos );
351 return bRet;
354 bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const
356 bool bRet = false;
357 if ( nEntry == SC_OL_HEADERENTRY )
358 bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount());
359 else
361 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
362 if ( pEntry && pEntry->IsVisible() )
364 SCCOLROW nStart, nEnd;
365 GetVisibleRange( nStart, nEnd );
366 bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd);
369 return bRet;
372 bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const
374 const ScOutlineArray* pArray = GetOutlineArray();
375 if ( !pArray ) return false;
377 SCCOLROW nStartIndex, nEndIndex;
378 GetVisibleRange( nStartIndex, nEndIndex );
380 size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() );
381 if ( nLevel == SC_OL_NOLEVEL )
382 return false;
384 long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y();
386 // --- level buttons ---
388 if ( mnHeaderSize > 0 )
390 long nImagePos = GetHeaderEntryPos();
391 if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
393 rnLevel = nLevel;
394 rnEntry = SC_OL_HEADERENTRY;
395 rbButton = true;
396 return true;
400 // --- expand/collapse buttons and expanded lines ---
402 // search outline entries backwards
403 size_t nEntry = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
404 while ( nEntry )
406 --nEntry;
408 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
409 sal::static_int_cast<sal_uInt16>(nEntry) );
410 SCCOLROW nStart = pEntry->GetStart();
411 SCCOLROW nEnd = pEntry->GetEnd();
413 if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) )
415 long nStartPos, nEndPos, nImagePos;
416 if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) )
418 rnLevel = nLevel;
419 rnEntry = nEntry;
421 // button?
422 if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
424 rbButton = true;
425 return true;
428 // line?
429 if ( mbMirrorEntries )
430 ::std::swap( nStartPos, nEndPos ); // in RTL mode, nStartPos is the larger value
431 if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) )
433 rbButton = false;
434 return true;
440 return false;
443 bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
445 bool bButton;
446 bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
447 return bRet && bButton;
450 bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
452 bool bButton;
453 bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
454 return bRet && !bButton;
457 void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const
459 ScDBFunc& rFunc = *mrViewData.GetView();
460 if ( nEntry == SC_OL_HEADERENTRY )
461 rFunc.SelectLevel( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel) );
462 else
464 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
465 if ( pEntry )
467 if ( pEntry->IsHidden() )
468 rFunc.ShowOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
469 else
470 rFunc.HideOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
475 void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const
477 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
478 if ( pEntry && pEntry->IsHidden() )
479 DoFunction( nLevel, nEntry );
482 void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const
484 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
485 if ( pEntry && !pEntry->IsHidden() )
486 DoFunction( nLevel, nEntry );
489 void ScOutlineWindow::Resize()
491 Window::Resize();
492 SetHeaderSize( mnHeaderSize ); // recalculates header/group positions
493 if ( !IsFocusButtonVisible() )
495 HideFocus();
496 ShowFocus(); // calculates valid position
500 void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt )
502 if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
503 (rDCEvt.GetFlags() & SETTINGS_STYLE) )
505 InitSettings();
506 Invalidate();
508 Window::DataChanged( rDCEvt );
511 // drawing --------------------------------------------------------------------
513 void ScOutlineWindow::SetEntryAreaClipRegion()
515 SetClipRegion( Rectangle(
516 GetPoint( 0, mnMainFirstPos ),
517 GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ) ) );
520 void ScOutlineWindow::DrawLineRel(
521 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
523 DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
526 void ScOutlineWindow::DrawRectRel(
527 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
529 DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) );
532 void ScOutlineWindow::DrawImageRel( long nLevelPos, long nEntryPos, sal_uInt16 nId )
534 OSL_ENSURE( mpSymbols, "ScOutlineWindow::DrawImageRel - no images" );
535 const Image& rImage = mpSymbols->GetImage( nId );
536 SetLineColor();
537 SetFillColor( GetBackground().GetColor() );
538 Point aPos( GetPoint( nLevelPos, nEntryPos ) );
539 DrawRect( Rectangle( aPos, rImage.GetSizePixel() ) );
540 DrawImage( aPos, rImage );
543 void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed )
545 Point aPos;
546 if ( GetImagePos( nLevel, nEntry, aPos ) )
548 OSL_ENSURE( mpSymbols, "ScOutlineWindow::DrawBorderRel - no images" );
549 sal_uInt16 nId = bPressed ? SC_OL_IMAGE_PRESSED : SC_OL_IMAGE_NOTPRESSED;
550 bool bClip = (nEntry != SC_OL_HEADERENTRY);
551 if ( bClip )
552 SetEntryAreaClipRegion();
553 DrawImage( aPos, mpSymbols->GetImage( nId ) );
554 if ( bClip )
555 SetClipRegion();
557 mbMTPressed = bPressed;
560 void ScOutlineWindow::ShowFocus()
562 if ( HasFocus() )
564 // first move to a visible position
565 ImplMoveFocusToVisible( true );
567 if ( IsFocusButtonVisible() )
569 Point aPos;
570 if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) )
572 aPos += Point( 1, 1 );
573 maFocusRect = Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) );
574 bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
575 if ( bClip )
576 SetEntryAreaClipRegion();
577 InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
578 if ( bClip )
579 SetClipRegion();
585 void ScOutlineWindow::HideFocus()
587 if ( !maFocusRect.IsEmpty() )
589 bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
590 if ( bClip )
591 SetEntryAreaClipRegion();
592 InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
593 if ( bClip )
594 SetClipRegion();
595 maFocusRect.SetEmpty();
599 void ScOutlineWindow::Paint( const Rectangle& /* rRect */ )
601 long nEntriesSign = mbMirrorEntries ? -1 : 1;
602 long nLevelsSign = mbMirrorLevels ? -1 : 1;
604 Size aSize = GetOutputSizePixel();
605 long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1;
606 long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1;
608 SetLineColor( maLineColor );
609 long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd;
610 DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd );
612 const ScOutlineArray* pArray = GetOutlineArray();
613 if ( !pArray ) return;
615 size_t nLevelCount = GetLevelCount();
617 // --- draw header images ---
619 if ( mnHeaderSize > 0 )
621 long nEntryPos = GetHeaderEntryPos();
622 for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel )
623 DrawImageRel( GetLevelPos( nLevel ), nEntryPos, static_cast< sal_uInt16 >( nLevel + 1 ) );
625 SetLineColor( maLineColor );
626 long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1));
627 DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos );
630 // --- draw lines & collapse/expand images ---
632 SetEntryAreaClipRegion();
634 SCCOLROW nStartIndex, nEndIndex;
635 GetVisibleRange( nStartIndex, nEndIndex );
637 for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel )
639 long nLevelPos = GetLevelPos( nLevel );
640 long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0;
642 size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
643 size_t nEntry;
645 // first draw all lines in the current level
646 SetLineColor();
647 SetFillColor( maLineColor );
648 for ( nEntry = 0; nEntry < nEntryCount; ++nEntry )
650 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
651 sal::static_int_cast<sal_uInt16>(nEntry) );
652 SCCOLROW nStart = pEntry->GetStart();
653 SCCOLROW nEnd = pEntry->GetEnd();
655 // visible range?
656 bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex);
657 // find output coordinates
658 if ( bDraw )
659 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
660 // draw, if not collapsed
661 if ( bDraw && !pEntry->IsHidden() )
663 if ( nStart >= nStartIndex )
664 nEntryPos1 += nEntriesSign;
665 nEntryPos2 -= 2 * nEntriesSign;
666 long nLinePos = nLevelPos;
667 if ( mbMirrorLevels )
668 nLinePos += SC_OL_BITMAPSIZE - 1; // align with right edge of bitmap
669 DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 );
671 if ( nEnd <= nEndIndex )
672 DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign,
673 nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 );
677 // draw all images in the level from last to first
678 nEntry = nEntryCount;
679 while ( nEntry )
681 --nEntry;
683 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
684 sal::static_int_cast<sal_uInt16>(nEntry) );
685 SCCOLROW nStart = pEntry->GetStart();
687 // visible range?
688 bool bDraw = (nStartIndex <= nStart) && (nStart <= nEndIndex + 1);
689 // find output coordinates
690 if ( bDraw )
691 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
692 // draw, if not hidden by higher levels
693 if ( bDraw )
695 sal_uInt16 nImageId = pEntry->IsHidden() ? SC_OL_IMAGE_PLUS : SC_OL_IMAGE_MINUS;
696 DrawImageRel( nLevelPos, nImagePos, nImageId );
701 SetClipRegion();
703 if ( !mbDontDrawFocus )
704 ShowFocus();
707 // focus ----------------------------------------------------------------------
709 /** Increments or decrements a value and wraps at the specified limits.
710 @return true = value wrapped. */
711 static bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward )
713 OSL_ENSURE( nMin <= nMax, "lcl_RotateValue - invalid range" );
714 OSL_ENSURE( nMax < static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" );
715 bool bWrap = false;
716 if ( bForward )
718 if ( rnValue < nMax )
719 ++rnValue;
720 else
722 rnValue = nMin;
723 bWrap = true;
726 else
728 if ( rnValue > nMin )
729 --rnValue;
730 else
732 rnValue = nMax;
733 bWrap = true;
736 return bWrap;
739 bool ScOutlineWindow::IsFocusButtonVisible() const
741 return IsButtonVisible( mnFocusLevel, mnFocusEntry );
744 bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward, bool bFindVisible )
746 const ScOutlineArray* pArray = GetOutlineArray();
747 if ( !pArray )
748 return false;
750 bool bWrapped = false;
751 size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(mnFocusLevel) );
752 // #i29530# entry count may be decreased after changing active sheet
753 if( mnFocusEntry >= nEntryCount )
754 mnFocusEntry = SC_OL_HEADERENTRY;
755 size_t nOldEntry = mnFocusEntry;
759 if ( mnFocusEntry == SC_OL_HEADERENTRY )
761 // move from header to first or last entry
762 if ( nEntryCount > 0 )
763 mnFocusEntry = bForward ? 0 : (nEntryCount - 1);
764 /* wrapped, if forward from right header to first entry,
765 or if backward from left header to last entry */
766 // Header and entries are now always in consistent order,
767 // so there's no need to check for mirroring here.
768 if ( !nEntryCount || !bForward )
769 bWrapped = true;
771 else if ( lcl_RotateValue( mnFocusEntry, 0, nEntryCount - 1, bForward ) )
773 // lcl_RotateValue returns true -> wrapped the entry range -> move to header
774 mnFocusEntry = SC_OL_HEADERENTRY;
775 /* wrapped, if forward from last entry to left header,
776 or if backward from first entry to right header */
777 if ( bForward )
778 bWrapped = true;
781 while ( bFindVisible && !IsFocusButtonVisible() && (nOldEntry != mnFocusEntry) );
783 return bWrapped;
786 bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward )
788 const ScOutlineArray* pArray = GetOutlineArray();
789 if ( !pArray )
790 return false;
792 bool bWrapped = false;
793 size_t nLevelCount = GetLevelCount();
795 if ( mnFocusEntry == SC_OL_HEADERENTRY )
797 if ( nLevelCount > 0 )
798 bWrapped = lcl_RotateValue( mnFocusLevel, 0, nLevelCount - 1, bForward );
800 else
802 const ScOutlineEntry* pEntry = pArray->GetEntry(
803 mnFocusLevel, mnFocusEntry);
805 if ( pEntry )
807 SCCOLROW nStart = pEntry->GetStart();
808 SCCOLROW nEnd = pEntry->GetEnd();
809 size_t nNewLevel = mnFocusLevel;
810 size_t nNewEntry = 0;
812 bool bFound = false;
813 if ( bForward && (mnFocusLevel + 2 < nLevelCount) )
815 // next level -> find first child entry
816 nNewLevel = mnFocusLevel + 1;
817 bFound = pArray->GetEntryIndexInRange(nNewLevel, nStart, nEnd, nNewEntry);
819 else if ( !bForward && (mnFocusLevel > 0) )
821 // previous level -> find parent entry
822 nNewLevel = mnFocusLevel - 1;
823 bFound = pArray->GetEntryIndex(nNewLevel, nStart, nNewEntry);
826 if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) )
828 mnFocusLevel = nNewLevel;
829 mnFocusEntry = nNewEntry;
834 return bWrapped;
837 bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward, bool bFindVisible )
839 bool bRet = false;
840 size_t nOldLevel = mnFocusLevel;
841 size_t nOldEntry = mnFocusEntry;
845 /* one level up, if backward from left header,
846 or one level down, if forward from right header */
847 if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) )
848 bRet |= ImplMoveFocusByLevel( bForward );
849 // move to next/previous entry
850 bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false );
851 bRet |= bWrapInLevel;
852 /* one level up, if wrapped backward to right header,
853 or one level down, if wrapped forward to right header */
854 if ( bForward && bWrapInLevel )
855 bRet |= ImplMoveFocusByLevel( bForward );
857 while ( bFindVisible && !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) );
859 return bRet;
862 void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward )
864 // first try to find an entry in the same level
865 if ( !IsFocusButtonVisible() )
866 ImplMoveFocusByEntry( bForward, true );
867 // then try to find any other entry
868 if ( !IsFocusButtonVisible() )
869 ImplMoveFocusByTabOrder( bForward, true );
872 void ScOutlineWindow::MoveFocusByEntry( bool bForward )
874 HideFocus();
875 ImplMoveFocusByEntry( bForward, true );
876 ShowFocus();
879 void ScOutlineWindow::MoveFocusByLevel( bool bForward )
881 HideFocus();
882 ImplMoveFocusByLevel( bForward );
883 ShowFocus();
886 void ScOutlineWindow::MoveFocusByTabOrder( bool bForward )
888 HideFocus();
889 ImplMoveFocusByTabOrder( bForward, true );
890 ShowFocus();
893 void ScOutlineWindow::GetFocus()
895 Window::GetFocus();
896 ShowFocus();
899 void ScOutlineWindow::LoseFocus()
901 HideFocus();
902 Window::LoseFocus();
906 // mouse ----------------------------------------------------------------------
908 void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry )
910 mbMTActive = true;
911 mnMTLevel = nLevel;
912 mnMTEntry = nEntry;
913 DrawBorderRel( nLevel, nEntry, true );
916 void ScOutlineWindow::EndMouseTracking()
918 if ( mbMTPressed )
919 DrawBorderRel( mnMTLevel, mnMTEntry, false );
920 mbMTActive = false;
923 void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt )
925 if ( IsMouseTracking() )
927 size_t nLevel, nEntry;
928 bool bHit = false;
930 if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
931 bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry);
933 if ( bHit != mbMTPressed )
934 DrawBorderRel( mnMTLevel, mnMTEntry, bHit );
938 void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt )
940 if ( IsMouseTracking() )
942 EndMouseTracking();
944 size_t nLevel, nEntry;
945 if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
946 if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) )
947 DoFunction( nLevel, nEntry );
951 void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt )
953 size_t nLevel, nEntry;
954 bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry );
955 if ( bHit )
956 StartMouseTracking( nLevel, nEntry );
957 else if ( rMEvt.GetClicks() == 2 )
959 bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry );
960 if ( bHit )
961 DoFunction( nLevel, nEntry );
964 // if an item has been hit and window is focused, move focus to this item
965 if ( bHit && HasFocus() )
967 HideFocus();
968 mnFocusLevel = nLevel;
969 mnFocusEntry = nEntry;
970 ShowFocus();
975 // keyboard -------------------------------------------------------------------
977 void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt )
979 const KeyCode& rKCode = rKEvt.GetKeyCode();
980 bool bNoMod = !rKCode.GetModifier();
981 bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
982 bool bCtrl = (rKCode.GetModifier() == KEY_MOD1);
984 sal_uInt16 nCode = rKCode.GetCode();
985 bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN);
986 bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT);
988 // TAB key
989 if ( (nCode == KEY_TAB) && (bNoMod || bShift) )
990 // move forward without SHIFT key
991 MoveFocusByTabOrder( bNoMod ); // TAB uses logical order, regardless of mirroring
993 // LEFT/RIGHT/UP/DOWN keys
994 else if ( bNoMod && (bUpDownKey || bLeftRightKey) )
996 bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT);
997 if ( mbHoriz == bLeftRightKey )
998 // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
999 MoveFocusByEntry( bForward != mbMirrorEntries );
1000 else
1001 // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
1002 MoveFocusByLevel( bForward != mbMirrorLevels );
1005 // CTRL + number
1006 else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) )
1008 size_t nLevel = static_cast< size_t >( nCode - KEY_1 );
1009 if ( nLevel < GetLevelCount() )
1010 DoFunction( nLevel, SC_OL_HEADERENTRY );
1013 // other key codes
1014 else switch ( rKCode.GetFullCode() )
1016 case KEY_ADD: DoExpand( mnFocusLevel, mnFocusEntry ); break;
1017 case KEY_SUBTRACT: DoCollapse( mnFocusLevel, mnFocusEntry ); break;
1018 case KEY_SPACE:
1019 case KEY_RETURN: DoFunction( mnFocusLevel, mnFocusEntry ); break;
1020 default: Window::KeyInput( rKEvt );
1025 // ============================================================================
1027 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */