fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / ui / view / olinewin.cxx
blob90cc0a40a75d969123ce1815232331770ae45347
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>
22 #include <vcl/settings.hxx>
24 #include "olinewin.hxx"
25 #include "olinetab.hxx"
26 #include "document.hxx"
27 #include "dbfunc.hxx"
28 #include "sc.hrc"
30 const long SC_OL_BITMAPSIZE = 12;
31 const long SC_OL_POSOFFSET = 2;
33 const size_t SC_OL_NOLEVEL = static_cast< size_t >( -1 );
34 const size_t SC_OL_HEADERENTRY = static_cast< size_t >( -1 );
36 const sal_uInt16 SC_OL_IMAGE_PLUS = 9;
37 const sal_uInt16 SC_OL_IMAGE_MINUS = SC_OL_IMAGE_PLUS + 1;
38 const sal_uInt16 SC_OL_IMAGE_NOTPRESSED = SC_OL_IMAGE_MINUS + 1;
39 const sal_uInt16 SC_OL_IMAGE_PRESSED = SC_OL_IMAGE_NOTPRESSED + 1;
41 ScOutlineWindow::ScOutlineWindow( vcl::Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) :
42 Window( pParent ),
43 mrViewData( *pViewData ),
44 meWhich( eWhich ),
45 mbHoriz( eMode == SC_OUTLINE_HOR ),
46 mbMirrorEntries( false ), // updated in SetHeaderSize
47 mbMirrorLevels( false ), // updated in SetHeaderSize
48 mpSymbols( NULL ),
49 maLineColor( COL_BLACK ),
50 mnHeaderSize( 0 ),
51 mnHeaderPos( 0 ),
52 mnMainFirstPos( 0 ),
53 mnMainLastPos( 0 ),
54 mbMTActive( false ),
55 mbMTPressed( false ),
56 mnFocusLevel( 0 ),
57 mnFocusEntry( SC_OL_HEADERENTRY ),
58 mbDontDrawFocus( false )
60 EnableRTL( false ); // mirroring is done manually
62 InitSettings();
63 maFocusRect.SetEmpty();
64 SetHeaderSize( 0 );
66 // insert the window into task pane list for "F6 cycling"
67 if( SystemWindow* pSysWin = GetSystemWindow() )
68 if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
69 pTaskPaneList->AddWindow( this );
72 ScOutlineWindow::~ScOutlineWindow()
74 disposeOnce();
77 void ScOutlineWindow::dispose()
79 // remove the window from task pane list
80 if( SystemWindow* pSysWin = GetSystemWindow() )
81 if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
82 pTaskPaneList->RemoveWindow( this );
83 vcl::Window::dispose();
86 void ScOutlineWindow::SetHeaderSize( long nNewSize )
88 bool bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() );
89 mbMirrorEntries = bLayoutRTL && mbHoriz;
90 mbMirrorLevels = bLayoutRTL && !mbHoriz;
92 bool bNew = (nNewSize != mnHeaderSize);
93 mnHeaderSize = nNewSize;
94 mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0;
95 mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize;
96 mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1;
97 if ( bNew )
98 Invalidate();
101 long ScOutlineWindow::GetDepthSize() const
103 long nSize = GetLevelCount() * SC_OL_BITMAPSIZE;
104 if ( nSize > 0 )
105 nSize += 2 * SC_OL_POSOFFSET + 1;
106 return nSize;
109 void ScOutlineWindow::ScrollPixel( long nDiff )
111 HideFocus();
112 mbDontDrawFocus = true;
114 long nStart = mnMainFirstPos;
115 long nEnd = mnMainLastPos;
117 long nInvStart, nInvEnd;
118 if (nDiff < 0)
120 nStart -= nDiff;
121 nInvStart = nEnd + nDiff;
122 nInvEnd = nEnd;
124 else
126 nEnd -= nDiff;
127 nInvStart = nStart;
128 nInvEnd = nStart + nDiff;
131 ScrollRel( nDiff, nStart, nEnd );
132 Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) );
133 Update();
135 // if focus becomes invisible, move it to next visible button
136 ImplMoveFocusToVisible( nDiff < 0 );
138 mbDontDrawFocus = false;
139 ShowFocus();
142 void ScOutlineWindow::ScrollRel( long nEntryDiff, long nEntryStart, long nEntryEnd )
144 Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) );
145 if ( mbHoriz )
146 Scroll( nEntryDiff, 0, aRect );
147 else
148 Scroll( 0, nEntryDiff, aRect );
151 // internal -------------------------------------------------------------------
153 void ScOutlineWindow::InitSettings()
155 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
156 SetBackground( rStyleSettings.GetFaceColor() );
157 maLineColor = rStyleSettings.GetButtonTextColor();
158 mpSymbols = ScGlobal::GetOutlineSymbols();
159 Invalidate();
162 const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const
164 const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() );
165 if ( !pTable ) return NULL;
166 return mbHoriz ? &pTable->GetColArray() : &pTable->GetRowArray();
169 const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const
171 const ScOutlineArray* pArray = GetOutlineArray();
172 return pArray ? pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ) : NULL;
175 bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const
177 return mbHoriz ?
178 GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) :
179 GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab());
182 bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const
184 // columns cannot be filtered
185 return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() );
188 bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const
190 bool bAllHidden = true;
191 for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos )
192 bAllHidden = IsHidden( nPos );
193 return bAllHidden;
196 void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const
198 if ( mbHoriz )
200 rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) );
201 rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) );
203 else
205 rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) );
206 rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) );
209 // include collapsed columns/rows in front of visible range
210 while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) )
211 --rnColRowStart;
214 Point ScOutlineWindow::GetPoint( long nLevelPos, long nEntryPos ) const
216 return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos );
219 Rectangle ScOutlineWindow::GetRectangle(
220 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd ) const
222 return Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
225 long ScOutlineWindow::GetOutputSizeLevel() const
227 Size aSize( GetOutputSizePixel() );
228 return mbHoriz ? aSize.Height() : aSize.Width();
231 long ScOutlineWindow::GetOutputSizeEntry() const
233 Size aSize( GetOutputSizePixel() );
234 return mbHoriz ? aSize.Width() : aSize.Height();
237 size_t ScOutlineWindow::GetLevelCount() const
239 const ScOutlineArray* pArray = GetOutlineArray();
240 size_t nLevelCount = pArray ? pArray->GetDepth() : 0;
241 return nLevelCount ? (nLevelCount + 1) : 0;
244 long ScOutlineWindow::GetLevelPos( size_t nLevel ) const
246 // #i51970# must always return the *left* edge of the area used by a level
247 long nPos = static_cast< long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE );
248 return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos;
251 size_t ScOutlineWindow::GetLevelFromPos( long nLevelPos ) const
253 if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1;
254 long nStart = SC_OL_POSOFFSET;
255 if ( nLevelPos < nStart ) return SC_OL_NOLEVEL;
256 size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE );
257 return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL;
260 long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const
262 long nDocPos = mbHoriz ?
263 mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, true ).X() :
264 mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, true ).Y();
265 return mnMainFirstPos + nDocPos;
268 long ScOutlineWindow::GetHeaderEntryPos() const
270 return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2;
273 bool ScOutlineWindow::GetEntryPos(
274 size_t nLevel, size_t nEntry,
275 long& rnStartPos, long& rnEndPos, long& rnImagePos ) const
277 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
278 if ( !pEntry || !pEntry->IsVisible() )
279 return false;
281 SCCOLROW nStart = pEntry->GetStart();
282 SCCOLROW nEnd = pEntry->GetEnd();
284 long nEntriesSign = mbMirrorEntries ? -1 : 1;
286 // --- common calculation ---
288 rnStartPos = GetColRowPos( nStart );
289 rnEndPos = GetColRowPos( nEnd + 1 );
291 bool bHidden = IsHidden( nStart );
292 rnImagePos = bHidden ?
293 (rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) :
294 rnStartPos + nEntriesSign;
295 long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign +
296 ( mbMirrorEntries ? 1 : 0 )) / 2L;
297 rnImagePos = mbMirrorEntries ? std::max( rnImagePos, nCenter ) : std::min( rnImagePos, nCenter );
299 // --- refinements ---
301 // do not cut leftmost/topmost image
302 if ( bHidden && IsFirstVisible( nStart ) )
303 rnImagePos = rnStartPos;
305 // do not cover previous collapsed image
306 bool bDoNoCover = !bHidden && nEntry;
307 const ScOutlineEntry* pPrevEntry = bDoNoCover ? GetOutlineEntry(nLevel, nEntry - 1) : NULL;
308 if (pPrevEntry)
310 SCCOLROW nPrevEnd = pPrevEntry->GetEnd();
311 if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) )
313 if ( IsFirstVisible( pPrevEntry->GetStart() ) )
314 rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign;
315 else
316 rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign;
317 rnImagePos = rnStartPos;
321 // restrict rnStartPos...rnEndPos to valid area
322 rnStartPos = std::max( rnStartPos, mnMainFirstPos );
323 rnEndPos = std::max( rnEndPos, mnMainFirstPos );
325 if ( mbMirrorEntries )
326 rnImagePos -= SC_OL_BITMAPSIZE - 1; // start pos aligns with right edge of bitmap
328 // --- all rows filtered? ---
330 bool bVisible = true;
331 if ( !mbHoriz )
333 bVisible = false;
334 for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow )
335 bVisible = !IsFiltered( nRow );
337 return bVisible;
340 bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const
342 bool bRet = nLevel < GetLevelCount();
343 if ( bRet )
345 long nLevelPos = GetLevelPos( nLevel );
346 if ( nEntry == SC_OL_HEADERENTRY )
347 rPos = GetPoint( nLevelPos, GetHeaderEntryPos() );
348 else
350 long nStartPos, nEndPos, nImagePos;
351 bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos );
352 rPos = GetPoint( nLevelPos, nImagePos );
355 return bRet;
358 bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const
360 bool bRet = false;
361 if ( nEntry == SC_OL_HEADERENTRY )
362 bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount());
363 else
365 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
366 if ( pEntry && pEntry->IsVisible() )
368 SCCOLROW nStart, nEnd;
369 GetVisibleRange( nStart, nEnd );
370 bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd);
373 return bRet;
376 bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const
378 const ScOutlineArray* pArray = GetOutlineArray();
379 if ( !pArray ) return false;
381 SCCOLROW nStartIndex, nEndIndex;
382 GetVisibleRange( nStartIndex, nEndIndex );
384 size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() );
385 if ( nLevel == SC_OL_NOLEVEL )
386 return false;
388 long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y();
390 // --- level buttons ---
392 if ( mnHeaderSize > 0 )
394 long nImagePos = GetHeaderEntryPos();
395 if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
397 rnLevel = nLevel;
398 rnEntry = SC_OL_HEADERENTRY;
399 rbButton = true;
400 return true;
404 // --- expand/collapse buttons and expanded lines ---
406 // search outline entries backwards
407 size_t nEntry = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
408 while ( nEntry )
410 --nEntry;
412 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
413 sal::static_int_cast<sal_uInt16>(nEntry) );
414 SCCOLROW nStart = pEntry->GetStart();
415 SCCOLROW nEnd = pEntry->GetEnd();
417 if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) )
419 long nStartPos, nEndPos, nImagePos;
420 if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) )
422 rnLevel = nLevel;
423 rnEntry = nEntry;
425 // button?
426 if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
428 rbButton = true;
429 return true;
432 // line?
433 if ( mbMirrorEntries )
434 ::std::swap( nStartPos, nEndPos ); // in RTL mode, nStartPos is the larger value
435 if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) )
437 rbButton = false;
438 return true;
444 return false;
447 bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
449 bool bButton;
450 bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
451 return bRet && bButton;
454 bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
456 bool bButton;
457 bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
458 return bRet && !bButton;
461 void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const
463 ScDBFunc& rFunc = *mrViewData.GetView();
464 if ( nEntry == SC_OL_HEADERENTRY )
465 rFunc.SelectLevel( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel) );
466 else
468 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
469 if ( pEntry )
471 if ( pEntry->IsHidden() )
472 rFunc.ShowOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
473 else
474 rFunc.HideOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
479 void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const
481 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
482 if ( pEntry && pEntry->IsHidden() )
483 DoFunction( nLevel, nEntry );
486 void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const
488 const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
489 if ( pEntry && !pEntry->IsHidden() )
490 DoFunction( nLevel, nEntry );
493 void ScOutlineWindow::Resize()
495 Window::Resize();
496 SetHeaderSize( mnHeaderSize ); // recalculates header/group positions
497 if ( !IsFocusButtonVisible() )
499 HideFocus();
500 ShowFocus(); // calculates valid position
504 void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt )
506 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
507 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
509 InitSettings();
510 Invalidate();
512 Window::DataChanged( rDCEvt );
515 // drawing --------------------------------------------------------------------
517 void ScOutlineWindow::SetEntryAreaClipRegion()
519 SetClipRegion( vcl::Region(Rectangle(
520 GetPoint( 0, mnMainFirstPos ),
521 GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ))));
524 void ScOutlineWindow::DrawLineRel(
525 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
527 DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
530 void ScOutlineWindow::DrawRectRel(
531 long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
533 DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) );
536 void ScOutlineWindow::DrawImageRel( long nLevelPos, long nEntryPos, sal_uInt16 nId )
538 OSL_ENSURE( mpSymbols, "ScOutlineWindow::DrawImageRel - no images" );
539 const Image& rImage = mpSymbols->GetImage( nId );
540 SetLineColor();
541 SetFillColor( GetBackground().GetColor() );
542 Point aPos( GetPoint( nLevelPos, nEntryPos ) );
543 DrawRect( Rectangle( aPos, rImage.GetSizePixel() ) );
544 DrawImage( aPos, rImage );
547 void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed )
549 Point aPos;
550 if ( GetImagePos( nLevel, nEntry, aPos ) )
552 OSL_ENSURE( mpSymbols, "ScOutlineWindow::DrawBorderRel - no images" );
553 sal_uInt16 nId = bPressed ? SC_OL_IMAGE_PRESSED : SC_OL_IMAGE_NOTPRESSED;
554 bool bClip = (nEntry != SC_OL_HEADERENTRY);
555 if ( bClip )
556 SetEntryAreaClipRegion();
557 DrawImage( aPos, mpSymbols->GetImage( nId ) );
558 if ( bClip )
559 SetClipRegion();
561 mbMTPressed = bPressed;
564 void ScOutlineWindow::ShowFocus()
566 if ( HasFocus() )
568 // first move to a visible position
569 ImplMoveFocusToVisible( true );
571 if ( IsFocusButtonVisible() )
573 Point aPos;
574 if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) )
576 aPos += Point( 1, 1 );
577 maFocusRect = Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) );
578 bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
579 if ( bClip )
580 SetEntryAreaClipRegion();
581 InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
582 if ( bClip )
583 SetClipRegion();
589 void ScOutlineWindow::HideFocus()
591 if ( !maFocusRect.IsEmpty() )
593 bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
594 if ( bClip )
595 SetEntryAreaClipRegion();
596 InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
597 if ( bClip )
598 SetClipRegion();
599 maFocusRect.SetEmpty();
603 void ScOutlineWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const Rectangle& /* rRect */ )
605 long nEntriesSign = mbMirrorEntries ? -1 : 1;
606 long nLevelsSign = mbMirrorLevels ? -1 : 1;
608 Size aSize = GetOutputSizePixel();
609 long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1;
610 long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1;
612 SetLineColor( maLineColor );
613 long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd;
614 DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd );
616 const ScOutlineArray* pArray = GetOutlineArray();
617 if ( !pArray ) return;
619 size_t nLevelCount = GetLevelCount();
621 // --- draw header images ---
623 if ( mnHeaderSize > 0 )
625 long nEntryPos = GetHeaderEntryPos();
626 for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel )
627 DrawImageRel( GetLevelPos( nLevel ), nEntryPos, static_cast< sal_uInt16 >( nLevel + 1 ) );
629 SetLineColor( maLineColor );
630 long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1));
631 DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos );
634 // --- draw lines & collapse/expand images ---
636 SetEntryAreaClipRegion();
638 SCCOLROW nStartIndex, nEndIndex;
639 GetVisibleRange( nStartIndex, nEndIndex );
641 for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel )
643 long nLevelPos = GetLevelPos( nLevel );
644 long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0;
646 size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
647 size_t nEntry;
649 // first draw all lines in the current level
650 SetLineColor();
651 SetFillColor( maLineColor );
652 for ( nEntry = 0; nEntry < nEntryCount; ++nEntry )
654 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
655 sal::static_int_cast<sal_uInt16>(nEntry) );
656 SCCOLROW nStart = pEntry->GetStart();
657 SCCOLROW nEnd = pEntry->GetEnd();
659 // visible range?
660 bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex);
661 // find output coordinates
662 if ( bDraw )
663 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
664 // draw, if not collapsed
665 if ( bDraw && !pEntry->IsHidden() )
667 if ( nStart >= nStartIndex )
668 nEntryPos1 += nEntriesSign;
669 nEntryPos2 -= 2 * nEntriesSign;
670 long nLinePos = nLevelPos;
671 if ( mbMirrorLevels )
672 nLinePos += SC_OL_BITMAPSIZE - 1; // align with right edge of bitmap
673 DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 );
675 if ( nEnd <= nEndIndex )
676 DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign,
677 nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 );
681 // draw all images in the level from last to first
682 nEntry = nEntryCount;
683 while ( nEntry )
685 --nEntry;
687 const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
688 sal::static_int_cast<sal_uInt16>(nEntry) );
689 SCCOLROW nStart = pEntry->GetStart();
691 // visible range?
692 bool bDraw = (nStartIndex <= nStart) && (nStart <= nEndIndex + 1);
693 // find output coordinates
694 if ( bDraw )
695 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
696 // draw, if not hidden by higher levels
697 if ( bDraw )
699 sal_uInt16 nImageId = pEntry->IsHidden() ? SC_OL_IMAGE_PLUS : SC_OL_IMAGE_MINUS;
700 DrawImageRel( nLevelPos, nImagePos, nImageId );
705 SetClipRegion();
707 if ( !mbDontDrawFocus )
708 ShowFocus();
711 // focus ----------------------------------------------------------------------
713 /** Increments or decrements a value and wraps at the specified limits.
714 @return true = value wrapped. */
715 static bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward )
717 OSL_ENSURE( nMin <= nMax, "lcl_RotateValue - invalid range" );
718 OSL_ENSURE( nMax < static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" );
719 bool bWrap = false;
720 if ( bForward )
722 if ( rnValue < nMax )
723 ++rnValue;
724 else
726 rnValue = nMin;
727 bWrap = true;
730 else
732 if ( rnValue > nMin )
733 --rnValue;
734 else
736 rnValue = nMax;
737 bWrap = true;
740 return bWrap;
743 bool ScOutlineWindow::IsFocusButtonVisible() const
745 return IsButtonVisible( mnFocusLevel, mnFocusEntry );
748 bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward, bool bFindVisible )
750 const ScOutlineArray* pArray = GetOutlineArray();
751 if ( !pArray )
752 return false;
754 bool bWrapped = false;
755 size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(mnFocusLevel) );
756 // #i29530# entry count may be decreased after changing active sheet
757 if( mnFocusEntry >= nEntryCount )
758 mnFocusEntry = SC_OL_HEADERENTRY;
759 size_t nOldEntry = mnFocusEntry;
763 if ( mnFocusEntry == SC_OL_HEADERENTRY )
765 // move from header to first or last entry
766 if ( nEntryCount > 0 )
767 mnFocusEntry = bForward ? 0 : (nEntryCount - 1);
768 /* wrapped, if forward from right header to first entry,
769 or if backward from left header to last entry */
770 // Header and entries are now always in consistent order,
771 // so there's no need to check for mirroring here.
772 if ( !nEntryCount || !bForward )
773 bWrapped = true;
775 else if ( lcl_RotateValue( mnFocusEntry, 0, nEntryCount - 1, bForward ) )
777 // lcl_RotateValue returns true -> wrapped the entry range -> move to header
778 mnFocusEntry = SC_OL_HEADERENTRY;
779 /* wrapped, if forward from last entry to left header,
780 or if backward from first entry to right header */
781 if ( bForward )
782 bWrapped = true;
785 while ( bFindVisible && !IsFocusButtonVisible() && (nOldEntry != mnFocusEntry) );
787 return bWrapped;
790 bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward )
792 const ScOutlineArray* pArray = GetOutlineArray();
793 if ( !pArray )
794 return false;
796 bool bWrapped = false;
797 size_t nLevelCount = GetLevelCount();
799 if ( mnFocusEntry == SC_OL_HEADERENTRY )
801 if ( nLevelCount > 0 )
802 bWrapped = lcl_RotateValue( mnFocusLevel, 0, nLevelCount - 1, bForward );
804 else
806 const ScOutlineEntry* pEntry = pArray->GetEntry(
807 mnFocusLevel, mnFocusEntry);
809 if ( pEntry )
811 SCCOLROW nStart = pEntry->GetStart();
812 SCCOLROW nEnd = pEntry->GetEnd();
813 size_t nNewLevel = mnFocusLevel;
814 size_t nNewEntry = 0;
816 bool bFound = false;
817 if ( bForward && (mnFocusLevel + 2 < nLevelCount) )
819 // next level -> find first child entry
820 nNewLevel = mnFocusLevel + 1;
821 bFound = pArray->GetEntryIndexInRange(nNewLevel, nStart, nEnd, nNewEntry);
823 else if ( !bForward && (mnFocusLevel > 0) )
825 // previous level -> find parent entry
826 nNewLevel = mnFocusLevel - 1;
827 bFound = pArray->GetEntryIndex(nNewLevel, nStart, nNewEntry);
830 if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) )
832 mnFocusLevel = nNewLevel;
833 mnFocusEntry = nNewEntry;
838 return bWrapped;
841 bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward, bool bFindVisible )
843 bool bRet = false;
844 size_t nOldLevel = mnFocusLevel;
845 size_t nOldEntry = mnFocusEntry;
849 /* one level up, if backward from left header,
850 or one level down, if forward from right header */
851 if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) )
852 bRet |= ImplMoveFocusByLevel( bForward );
853 // move to next/previous entry
854 bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false );
855 bRet |= bWrapInLevel;
856 /* one level up, if wrapped backward to right header,
857 or one level down, if wrapped forward to right header */
858 if ( bForward && bWrapInLevel )
859 bRet |= ImplMoveFocusByLevel( bForward );
861 while ( bFindVisible && !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) );
863 return bRet;
866 void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward )
868 // first try to find an entry in the same level
869 if ( !IsFocusButtonVisible() )
870 ImplMoveFocusByEntry( bForward, true );
871 // then try to find any other entry
872 if ( !IsFocusButtonVisible() )
873 ImplMoveFocusByTabOrder( bForward, true );
876 void ScOutlineWindow::MoveFocusByEntry( bool bForward )
878 HideFocus();
879 ImplMoveFocusByEntry( bForward, true );
880 ShowFocus();
883 void ScOutlineWindow::MoveFocusByLevel( bool bForward )
885 HideFocus();
886 ImplMoveFocusByLevel( bForward );
887 ShowFocus();
890 void ScOutlineWindow::MoveFocusByTabOrder( bool bForward )
892 HideFocus();
893 ImplMoveFocusByTabOrder( bForward, true );
894 ShowFocus();
897 void ScOutlineWindow::GetFocus()
899 Window::GetFocus();
900 ShowFocus();
903 void ScOutlineWindow::LoseFocus()
905 HideFocus();
906 Window::LoseFocus();
909 // mouse ----------------------------------------------------------------------
911 void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry )
913 mbMTActive = true;
914 mnMTLevel = nLevel;
915 mnMTEntry = nEntry;
916 DrawBorderRel( nLevel, nEntry, true );
919 void ScOutlineWindow::EndMouseTracking()
921 if ( mbMTPressed )
922 DrawBorderRel( mnMTLevel, mnMTEntry, false );
923 mbMTActive = false;
926 void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt )
928 if ( IsMouseTracking() )
930 size_t nLevel, nEntry;
931 bool bHit = false;
933 if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
934 bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry);
936 if ( bHit != mbMTPressed )
937 DrawBorderRel( mnMTLevel, mnMTEntry, bHit );
941 void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt )
943 if ( IsMouseTracking() )
945 EndMouseTracking();
947 size_t nLevel, nEntry;
948 if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
949 if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) )
950 DoFunction( nLevel, nEntry );
954 void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt )
956 size_t nLevel, nEntry;
957 bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry );
958 if ( bHit )
959 StartMouseTracking( nLevel, nEntry );
960 else if ( rMEvt.GetClicks() == 2 )
962 bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry );
963 if ( bHit )
964 DoFunction( nLevel, nEntry );
967 // if an item has been hit and window is focused, move focus to this item
968 if ( bHit && HasFocus() )
970 HideFocus();
971 mnFocusLevel = nLevel;
972 mnFocusEntry = nEntry;
973 ShowFocus();
977 // keyboard -------------------------------------------------------------------
979 void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt )
981 const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
982 bool bNoMod = !rKCode.GetModifier();
983 bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
984 bool bCtrl = (rKCode.GetModifier() == KEY_MOD1);
986 sal_uInt16 nCode = rKCode.GetCode();
987 bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN);
988 bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT);
990 // TAB key
991 if ( (nCode == KEY_TAB) && (bNoMod || bShift) )
992 // move forward without SHIFT key
993 MoveFocusByTabOrder( bNoMod ); // TAB uses logical order, regardless of mirroring
995 // LEFT/RIGHT/UP/DOWN keys
996 else if ( bNoMod && (bUpDownKey || bLeftRightKey) )
998 bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT);
999 if ( mbHoriz == bLeftRightKey )
1000 // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
1001 MoveFocusByEntry( bForward != mbMirrorEntries );
1002 else
1003 // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
1004 MoveFocusByLevel( bForward != mbMirrorLevels );
1007 // CTRL + number
1008 else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) )
1010 size_t nLevel = static_cast< size_t >( nCode - KEY_1 );
1011 if ( nLevel < GetLevelCount() )
1012 DoFunction( nLevel, SC_OL_HEADERENTRY );
1015 // other key codes
1016 else switch ( rKCode.GetFullCode() )
1018 case KEY_ADD: DoExpand( mnFocusLevel, mnFocusEntry ); break;
1019 case KEY_SUBTRACT: DoCollapse( mnFocusLevel, mnFocusEntry ); break;
1020 case KEY_SPACE:
1021 case KEY_RETURN: DoFunction( mnFocusLevel, mnFocusEntry ); break;
1022 default: Window::KeyInput( rKEvt );
1026 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */