1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <sal/config.h>
22 #include <vcl/image.hxx>
23 #include <vcl/taskpanelist.hxx>
24 #include <vcl/settings.hxx>
25 #include <vcl/syswin.hxx>
26 #include <osl/diagnose.h>
28 #include <olinewin.hxx>
29 #include <olinetab.hxx>
30 #include <document.hxx>
32 #include <bitmaps.hlst>
34 const tools::Long SC_OL_BITMAPSIZE
= 12;
35 const tools::Long SC_OL_POSOFFSET
= 2;
37 const size_t SC_OL_NOLEVEL
= static_cast< size_t >( -1 );
38 const size_t SC_OL_HEADERENTRY
= static_cast< size_t >( -1 );
40 ScOutlineWindow::ScOutlineWindow( vcl::Window
* pParent
, ScOutlineMode eMode
, ScViewData
* pViewData
, ScSplitPos eWhich
) :
42 mrViewData( *pViewData
),
44 mbHoriz( eMode
== SC_OUTLINE_HOR
),
45 mbMirrorEntries( false ), // updated in SetHeaderSize
46 mbMirrorLevels( false ), // updated in SetHeaderSize
47 maLineColor( COL_BLACK
),
55 mnFocusEntry( SC_OL_HEADERENTRY
),
56 mbDontDrawFocus( false )
58 EnableRTL( false ); // mirroring is done manually
61 maFocusRect
.SetEmpty();
64 // insert the window into task pane list for "F6 cycling"
65 if( SystemWindow
* pSysWin
= GetSystemWindow() )
66 if( TaskPaneList
* pTaskPaneList
= pSysWin
->GetTaskPaneList() )
67 pTaskPaneList
->AddWindow( this );
70 ScOutlineWindow::~ScOutlineWindow()
75 void ScOutlineWindow::dispose()
77 // remove the window from task pane list
78 if( SystemWindow
* pSysWin
= GetSystemWindow() )
79 if( TaskPaneList
* pTaskPaneList
= pSysWin
->GetTaskPaneList() )
80 pTaskPaneList
->RemoveWindow( this );
81 vcl::Window::dispose();
84 void ScOutlineWindow::SetHeaderSize( tools::Long nNewSize
)
86 bool bLayoutRTL
= GetDoc().IsLayoutRTL( GetTab() );
87 mbMirrorEntries
= bLayoutRTL
&& mbHoriz
;
88 mbMirrorLevels
= bLayoutRTL
&& !mbHoriz
;
90 bool bNew
= (nNewSize
!= mnHeaderSize
);
91 mnHeaderSize
= nNewSize
;
92 mnHeaderPos
= mbMirrorEntries
? (GetOutputSizeEntry() - mnHeaderSize
) : 0;
93 mnMainFirstPos
= mbMirrorEntries
? 0 : mnHeaderSize
;
94 mnMainLastPos
= GetOutputSizeEntry() - (mbMirrorEntries
? mnHeaderSize
: 0) - 1;
99 tools::Long
ScOutlineWindow::GetDepthSize() const
101 tools::Long nSize
= GetLevelCount() * SC_OL_BITMAPSIZE
;
103 nSize
+= 2 * SC_OL_POSOFFSET
+ 1;
107 void ScOutlineWindow::ScrollPixel( tools::Long nDiff
)
110 mbDontDrawFocus
= true;
112 tools::Long nStart
= mnMainFirstPos
;
113 tools::Long nEnd
= mnMainLastPos
;
115 tools::Long nInvStart
, nInvEnd
;
119 nInvStart
= nEnd
+ nDiff
;
126 nInvEnd
= nStart
+ nDiff
;
129 ScrollRel( nDiff
, nStart
, nEnd
);
130 Invalidate( GetRectangle( 0, nInvStart
, GetOutputSizeLevel() - 1, nInvEnd
) );
132 // if focus becomes invisible, move it to next visible button
133 ImplMoveFocusToVisible( nDiff
< 0 );
135 mbDontDrawFocus
= false;
139 void ScOutlineWindow::ScrollRel( tools::Long nEntryDiff
, tools::Long nEntryStart
, tools::Long nEntryEnd
)
141 tools::Rectangle
aRect( GetRectangle( 0, nEntryStart
, GetOutputSizeLevel() - 1, nEntryEnd
) );
143 Scroll( nEntryDiff
, 0, aRect
);
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();
158 const ScOutlineArray
* ScOutlineWindow::GetOutlineArray() const
160 const ScOutlineTable
* pTable
= GetDoc().GetOutlineTable( GetTab() );
161 if ( !pTable
) return nullptr;
162 return mbHoriz
? &pTable
->GetColArray() : &pTable
->GetRowArray();
165 const ScOutlineEntry
* ScOutlineWindow::GetOutlineEntry( size_t nLevel
, size_t nEntry
) const
167 const ScOutlineArray
* pArray
= GetOutlineArray();
168 return pArray
? pArray
->GetEntry( sal::static_int_cast
<sal_uInt16
>(nLevel
), sal::static_int_cast
<sal_uInt16
>(nEntry
) ) : nullptr;
171 bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex
) const
174 GetDoc().ColHidden(static_cast<SCCOL
>(nColRowIndex
), GetTab()) :
175 GetDoc().RowHidden(static_cast<SCROW
>(nColRowIndex
), GetTab());
178 bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex
) const
180 // columns cannot be filtered
181 return !mbHoriz
&& GetDoc().RowFiltered( static_cast<SCROW
>(nColRowIndex
), GetTab() );
184 bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex
) const
186 bool bAllHidden
= true;
187 for ( SCCOLROW nPos
= 0; (nPos
< nColRowIndex
) && bAllHidden
; ++nPos
)
188 bAllHidden
= IsHidden( nPos
);
192 void ScOutlineWindow::GetVisibleRange( SCCOLROW
& rnColRowStart
, SCCOLROW
& rnColRowEnd
) const
196 rnColRowStart
= mrViewData
.GetPosX( WhichH( meWhich
) );
197 rnColRowEnd
= rnColRowStart
+ mrViewData
.VisibleCellsX( WhichH( meWhich
) );
201 rnColRowStart
= mrViewData
.GetPosY( WhichV( meWhich
) );
202 rnColRowEnd
= rnColRowStart
+ mrViewData
.VisibleCellsY( WhichV( meWhich
) );
205 // include collapsed columns/rows in front of visible range
206 while ( (rnColRowStart
> 0) && IsHidden( rnColRowStart
- 1 ) )
210 Point
ScOutlineWindow::GetPoint( tools::Long nLevelPos
, tools::Long nEntryPos
) const
212 return mbHoriz
? Point( nEntryPos
, nLevelPos
) : Point( nLevelPos
, nEntryPos
);
215 tools::Rectangle
ScOutlineWindow::GetRectangle(
216 tools::Long nLevelStart
, tools::Long nEntryStart
, tools::Long nLevelEnd
, tools::Long nEntryEnd
) const
218 return tools::Rectangle( GetPoint( nLevelStart
, nEntryStart
), GetPoint( nLevelEnd
, nEntryEnd
) );
221 tools::Long
ScOutlineWindow::GetOutputSizeLevel() const
223 Size
aSize( GetOutputSizePixel() );
224 return mbHoriz
? aSize
.Height() : aSize
.Width();
227 tools::Long
ScOutlineWindow::GetOutputSizeEntry() const
229 Size
aSize( GetOutputSizePixel() );
230 return mbHoriz
? aSize
.Width() : aSize
.Height();
233 size_t ScOutlineWindow::GetLevelCount() const
235 const ScOutlineArray
* pArray
= GetOutlineArray();
236 size_t nLevelCount
= pArray
? pArray
->GetDepth() : 0;
237 return nLevelCount
? (nLevelCount
+ 1) : 0;
240 tools::Long
ScOutlineWindow::GetLevelPos( size_t nLevel
) const
242 // #i51970# must always return the *left* edge of the area used by a level
243 tools::Long nPos
= static_cast< tools::Long
>( SC_OL_POSOFFSET
+ nLevel
* SC_OL_BITMAPSIZE
);
244 return mbMirrorLevels
? (GetOutputSizeLevel() - nPos
- SC_OL_BITMAPSIZE
) : nPos
;
247 size_t ScOutlineWindow::GetLevelFromPos( tools::Long nLevelPos
) const
249 if( mbMirrorLevels
) nLevelPos
= GetOutputSizeLevel() - nLevelPos
- 1;
250 tools::Long nStart
= SC_OL_POSOFFSET
;
251 if ( nLevelPos
< nStart
) return SC_OL_NOLEVEL
;
252 size_t nLevel
= static_cast< size_t >( (nLevelPos
- nStart
) / SC_OL_BITMAPSIZE
);
253 return (nLevel
< GetLevelCount()) ? nLevel
: SC_OL_NOLEVEL
;
256 tools::Long
ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex
) const
258 tools::Long nDocPos
= mbHoriz
?
259 mrViewData
.GetScrPos( static_cast<SCCOL
>(nColRowIndex
), 0, meWhich
, true ).X() :
260 mrViewData
.GetScrPos( 0, static_cast<SCROW
>(nColRowIndex
), meWhich
, true ).Y();
261 return mnMainFirstPos
+ nDocPos
;
264 tools::Long
ScOutlineWindow::GetHeaderEntryPos() const
266 return mnHeaderPos
+ (mnHeaderSize
- SC_OL_BITMAPSIZE
) / 2;
269 bool ScOutlineWindow::GetEntryPos(
270 size_t nLevel
, size_t nEntry
,
271 tools::Long
& rnStartPos
, tools::Long
& rnEndPos
, tools::Long
& rnImagePos
) const
273 const ScOutlineEntry
* pEntry
= GetOutlineEntry( nLevel
, nEntry
);
274 if ( !pEntry
|| !pEntry
->IsVisible() )
277 SCCOLROW nStart
= pEntry
->GetStart();
278 SCCOLROW nEnd
= pEntry
->GetEnd();
280 tools::Long nEntriesSign
= mbMirrorEntries
? -1 : 1;
282 // --- common calculation ---
284 rnStartPos
= GetColRowPos( nStart
);
285 rnEndPos
= GetColRowPos( nEnd
+ 1 );
287 bool bHidden
= IsHidden( nStart
);
288 rnImagePos
= bHidden
?
289 (rnStartPos
- ( SC_OL_BITMAPSIZE
/ 2 ) * nEntriesSign
) :
290 rnStartPos
+ nEntriesSign
;
291 tools::Long nCenter
= (rnStartPos
+ rnEndPos
- SC_OL_BITMAPSIZE
* nEntriesSign
+
292 ( mbMirrorEntries
? 1 : 0 )) / 2;
293 rnImagePos
= mbMirrorEntries
? std::max( rnImagePos
, nCenter
) : std::min( rnImagePos
, nCenter
);
295 // --- refinements ---
297 // do not cut leftmost/topmost image
298 if ( bHidden
&& IsFirstVisible( nStart
) )
299 rnImagePos
= rnStartPos
;
301 // do not cover previous collapsed image
302 bool bDoNoCover
= !bHidden
&& nEntry
;
303 const ScOutlineEntry
* pPrevEntry
= bDoNoCover
? GetOutlineEntry(nLevel
, nEntry
- 1) : nullptr;
306 SCCOLROW nPrevEnd
= pPrevEntry
->GetEnd();
307 if ( (nPrevEnd
+ 1 == nStart
) && IsHidden( nPrevEnd
) )
309 if ( IsFirstVisible( pPrevEntry
->GetStart() ) )
310 rnStartPos
+= SC_OL_BITMAPSIZE
* nEntriesSign
;
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;
330 for ( SCCOLROW nRow
= nStart
; (nRow
<= nEnd
) && !bVisible
; ++nRow
)
331 bVisible
= !IsFiltered( nRow
);
336 bool ScOutlineWindow::GetImagePos( size_t nLevel
, size_t nEntry
, Point
& rPos
) const
338 bool bRet
= nLevel
< GetLevelCount();
341 tools::Long nLevelPos
= GetLevelPos( nLevel
);
342 if ( nEntry
== SC_OL_HEADERENTRY
)
343 rPos
= GetPoint( nLevelPos
, GetHeaderEntryPos() );
346 tools::Long nStartPos
, nEndPos
, nImagePos
;
347 bRet
= GetEntryPos( nLevel
, nEntry
, nStartPos
, nEndPos
, nImagePos
);
348 rPos
= GetPoint( nLevelPos
, nImagePos
);
354 bool ScOutlineWindow::IsButtonVisible( size_t nLevel
, size_t nEntry
) const
357 if ( nEntry
== SC_OL_HEADERENTRY
)
358 bRet
= (mnHeaderSize
> 0) && (nLevel
< GetLevelCount());
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
);
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
)
384 tools::Long nEntryMousePos
= mbHoriz
? rPos
.X() : rPos
.Y();
386 // --- level buttons ---
388 if ( mnHeaderSize
> 0 )
390 tools::Long nImagePos
= GetHeaderEntryPos();
391 if ( (nImagePos
<= nEntryMousePos
) && (nEntryMousePos
< nImagePos
+ SC_OL_BITMAPSIZE
) )
394 rnEntry
= SC_OL_HEADERENTRY
;
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
) );
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 tools::Long nStartPos
, nEndPos
, nImagePos
;
416 if ( GetEntryPos( nLevel
, nEntry
, nStartPos
, nEndPos
, nImagePos
) )
422 if ( (nStart
>= nStartIndex
) && (nImagePos
<= nEntryMousePos
) && (nEntryMousePos
< nImagePos
+ SC_OL_BITMAPSIZE
) )
429 if ( mbMirrorEntries
)
430 ::std::swap( nStartPos
, nEndPos
); // in RTL mode, nStartPos is the larger value
431 if ( (nStartPos
<= nEntryMousePos
) && (nEntryMousePos
<= nEndPos
) )
443 bool ScOutlineWindow::ButtonHit( const Point
& rPos
, size_t& rnLevel
, size_t& rnEntry
) const
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
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
) );
464 const ScOutlineEntry
* pEntry
= GetOutlineEntry( nLevel
, nEntry
);
467 if ( pEntry
->IsHidden() )
468 rFunc
.ShowOutline( mbHoriz
, sal::static_int_cast
<sal_uInt16
>(nLevel
), sal::static_int_cast
<sal_uInt16
>(nEntry
) );
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()
492 SetHeaderSize( mnHeaderSize
); // recalculates header/group positions
493 if ( !IsFocusButtonVisible() )
496 ShowFocus(); // calculates valid position
500 void ScOutlineWindow::DataChanged( const DataChangedEvent
& rDCEvt
)
502 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
503 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
) )
508 Window::DataChanged( rDCEvt
);
511 // drawing --------------------------------------------------------------------
513 void ScOutlineWindow::SetEntryAreaClipRegion()
515 GetOutDev()->SetClipRegion( vcl::Region(tools::Rectangle(
516 GetPoint( 0, mnMainFirstPos
),
517 GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos
))));
520 void ScOutlineWindow::DrawLineRel(
521 tools::Long nLevelStart
, tools::Long nEntryStart
, tools::Long nLevelEnd
, tools::Long nEntryEnd
)
523 GetOutDev()->DrawLine( GetPoint( nLevelStart
, nEntryStart
), GetPoint( nLevelEnd
, nEntryEnd
) );
526 void ScOutlineWindow::DrawRectRel(
527 tools::Long nLevelStart
, tools::Long nEntryStart
, tools::Long nLevelEnd
, tools::Long nEntryEnd
)
529 GetOutDev()->DrawRect( GetRectangle( nLevelStart
, nEntryStart
, nLevelEnd
, nEntryEnd
) );
534 Image
GetImage(const OUString
& rId
)
536 return Image(StockImage::Yes
, rId
);
540 void ScOutlineWindow::DrawImageRel(tools::Long nLevelPos
, tools::Long nEntryPos
, const OUString
& rId
)
542 const Image aImage
= GetImage(rId
);
543 GetOutDev()->SetLineColor();
544 GetOutDev()->SetFillColor( GetBackground().GetColor() );
545 Point
aPos( GetPoint( nLevelPos
, nEntryPos
) );
546 GetOutDev()->DrawRect( tools::Rectangle( aPos
, aImage
.GetSizePixel() ) );
547 GetOutDev()->DrawImage( aPos
, aImage
);
550 void ScOutlineWindow::DrawBorderRel( size_t nLevel
, size_t nEntry
, bool bPressed
)
553 if ( GetImagePos( nLevel
, nEntry
, aPos
) )
555 OUString sId
= bPressed
? RID_BMP_PRESSED
: RID_BMP_NOTPRESSED
;
556 bool bClip
= (nEntry
!= SC_OL_HEADERENTRY
);
558 SetEntryAreaClipRegion();
559 GetOutDev()->DrawImage(aPos
, GetImage(sId
));
561 GetOutDev()->SetClipRegion();
563 mbMTPressed
= bPressed
;
566 void ScOutlineWindow::ShowFocus()
571 // first move to a visible position
572 ImplMoveFocusToVisible( true );
574 if ( !IsFocusButtonVisible() )
578 if ( GetImagePos( mnFocusLevel
, mnFocusEntry
, aPos
) )
580 aPos
+= Point( 1, 1 );
581 maFocusRect
= tools::Rectangle( aPos
, Size( SC_OL_BITMAPSIZE
- 2, SC_OL_BITMAPSIZE
- 2 ) );
582 bool bClip
= (mnFocusEntry
!= SC_OL_HEADERENTRY
);
584 SetEntryAreaClipRegion();
585 InvertTracking( maFocusRect
, ShowTrackFlags::Small
| ShowTrackFlags::TrackWindow
);
587 GetOutDev()->SetClipRegion();
591 void ScOutlineWindow::HideFocus()
593 if ( !maFocusRect
.IsEmpty() )
595 bool bClip
= (mnFocusEntry
!= SC_OL_HEADERENTRY
);
597 SetEntryAreaClipRegion();
598 InvertTracking( maFocusRect
, ShowTrackFlags::Small
| ShowTrackFlags::TrackWindow
);
600 GetOutDev()->SetClipRegion();
601 maFocusRect
.SetEmpty();
605 constexpr OUString aLevelBmps
[]=
617 void ScOutlineWindow::Paint( vcl::RenderContext
& /*rRenderContext*/, const tools::Rectangle
& /* rRect */ )
619 tools::Long nEntriesSign
= mbMirrorEntries
? -1 : 1;
620 tools::Long nLevelsSign
= mbMirrorLevels
? -1 : 1;
622 Size aSize
= GetOutputSizePixel();
623 tools::Long nLevelEnd
= (mbHoriz
? aSize
.Height() : aSize
.Width()) - 1;
624 tools::Long nEntryEnd
= (mbHoriz
? aSize
.Width() : aSize
.Height()) - 1;
626 GetOutDev()->SetLineColor( maLineColor
);
627 tools::Long nBorderPos
= mbMirrorLevels
? 0 : nLevelEnd
;
628 DrawLineRel( nBorderPos
, 0, nBorderPos
, nEntryEnd
);
630 const ScOutlineArray
* pArray
= GetOutlineArray();
631 if ( !pArray
) return;
633 size_t nLevelCount
= GetLevelCount();
635 // --- draw header images ---
637 if ( mnHeaderSize
> 0 )
639 tools::Long nEntryPos
= GetHeaderEntryPos();
640 for ( size_t nLevel
= 0; nLevel
< nLevelCount
; ++nLevel
)
641 DrawImageRel(GetLevelPos(nLevel
), nEntryPos
, aLevelBmps
[nLevel
]);
643 GetOutDev()->SetLineColor( maLineColor
);
644 tools::Long nLinePos
= mnHeaderPos
+ (mbMirrorEntries
? 0 : (mnHeaderSize
- 1));
645 DrawLineRel( 0, nLinePos
, nLevelEnd
, nLinePos
);
648 // --- draw lines & collapse/expand images ---
650 SetEntryAreaClipRegion();
652 SCCOLROW nStartIndex
, nEndIndex
;
653 GetVisibleRange( nStartIndex
, nEndIndex
);
655 for ( size_t nLevel
= 0; nLevel
+ 1 < nLevelCount
; ++nLevel
)
657 tools::Long nLevelPos
= GetLevelPos( nLevel
);
658 tools::Long nEntryPos1
= 0, nEntryPos2
= 0, nImagePos
= 0;
660 size_t nEntryCount
= pArray
->GetCount( sal::static_int_cast
<sal_uInt16
>(nLevel
) );
663 // first draw all lines in the current level
664 GetOutDev()->SetLineColor();
665 GetOutDev()->SetFillColor( maLineColor
);
666 for ( nEntry
= 0; nEntry
< nEntryCount
; ++nEntry
)
668 const ScOutlineEntry
* pEntry
= pArray
->GetEntry( sal::static_int_cast
<sal_uInt16
>(nLevel
),
669 sal::static_int_cast
<sal_uInt16
>(nEntry
) );
670 SCCOLROW nStart
= pEntry
->GetStart();
671 SCCOLROW nEnd
= pEntry
->GetEnd();
674 bool bDraw
= (nEnd
>= nStartIndex
) && (nStart
<= nEndIndex
);
675 // find output coordinates
677 bDraw
= GetEntryPos( nLevel
, nEntry
, nEntryPos1
, nEntryPos2
, nImagePos
);
678 // draw, if not collapsed
679 if ( bDraw
&& !pEntry
->IsHidden() )
681 if ( nStart
>= nStartIndex
)
682 nEntryPos1
+= nEntriesSign
;
683 nEntryPos2
-= 2 * nEntriesSign
;
684 tools::Long nLinePos
= nLevelPos
;
685 if ( mbMirrorLevels
)
686 nLinePos
+= SC_OL_BITMAPSIZE
- 1; // align with right edge of bitmap
687 DrawRectRel( nLinePos
, nEntryPos1
, nLinePos
+ nLevelsSign
, nEntryPos2
);
689 if ( nEnd
<= nEndIndex
)
690 DrawRectRel( nLinePos
, nEntryPos2
- nEntriesSign
,
691 nLinePos
+ ( SC_OL_BITMAPSIZE
/ 3 ) * nLevelsSign
, nEntryPos2
);
695 // draw all images in the level from last to first
696 nEntry
= nEntryCount
;
701 const ScOutlineEntry
* pEntry
= pArray
->GetEntry( sal::static_int_cast
<sal_uInt16
>(nLevel
),
702 sal::static_int_cast
<sal_uInt16
>(nEntry
) );
703 SCCOLROW nStart
= pEntry
->GetStart();
706 bool bDraw
= (nStartIndex
<= nStart
) && (nStart
<= nEndIndex
+ 1);
707 // find output coordinates
709 bDraw
= GetEntryPos( nLevel
, nEntry
, nEntryPos1
, nEntryPos2
, nImagePos
);
710 // draw, if not hidden by higher levels
713 OUString sImageId
= pEntry
->IsHidden() ? RID_BMP_PLUS
: RID_BMP_MINUS
;
714 DrawImageRel(nLevelPos
, nImagePos
, sImageId
);
719 GetOutDev()->SetClipRegion();
721 if ( !mbDontDrawFocus
)
725 // focus ----------------------------------------------------------------------
727 /** Increments or decrements a value and wraps at the specified limits.
728 @return true = value wrapped. */
729 static bool lcl_RotateValue( size_t& rnValue
, size_t nMin
, size_t nMax
, bool bForward
)
731 OSL_ENSURE( nMin
<= nMax
, "lcl_RotateValue - invalid range" );
732 OSL_ENSURE( nMax
< static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" );
736 if ( rnValue
< nMax
)
746 if ( rnValue
> nMin
)
757 bool ScOutlineWindow::IsFocusButtonVisible() const
759 return IsButtonVisible( mnFocusLevel
, mnFocusEntry
);
762 bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward
, bool bFindVisible
)
764 const ScOutlineArray
* pArray
= GetOutlineArray();
768 bool bWrapped
= false;
769 size_t nEntryCount
= pArray
->GetCount( sal::static_int_cast
<sal_uInt16
>(mnFocusLevel
) );
770 // #i29530# entry count may be decreased after changing active sheet
771 if( mnFocusEntry
>= nEntryCount
)
772 mnFocusEntry
= SC_OL_HEADERENTRY
;
773 size_t nOldEntry
= mnFocusEntry
;
777 if ( mnFocusEntry
== SC_OL_HEADERENTRY
)
779 // move from header to first or last entry
780 if ( nEntryCount
> 0 )
781 mnFocusEntry
= bForward
? 0 : (nEntryCount
- 1);
782 /* wrapped, if forward from right header to first entry,
783 or if backward from left header to last entry */
784 // Header and entries are now always in consistent order,
785 // so there's no need to check for mirroring here.
786 if ( !nEntryCount
|| !bForward
)
789 else if ( lcl_RotateValue( mnFocusEntry
, 0, nEntryCount
- 1, bForward
) )
791 // lcl_RotateValue returns true -> wrapped the entry range -> move to header
792 mnFocusEntry
= SC_OL_HEADERENTRY
;
793 /* wrapped, if forward from last entry to left header,
794 or if backward from first entry to right header */
799 while ( bFindVisible
&& !IsFocusButtonVisible() && (nOldEntry
!= mnFocusEntry
) );
804 bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward
)
806 const ScOutlineArray
* pArray
= GetOutlineArray();
810 bool bWrapped
= false;
811 size_t nLevelCount
= GetLevelCount();
813 if ( mnFocusEntry
== SC_OL_HEADERENTRY
)
815 if ( nLevelCount
> 0 )
816 bWrapped
= lcl_RotateValue( mnFocusLevel
, 0, nLevelCount
- 1, bForward
);
820 const ScOutlineEntry
* pEntry
= pArray
->GetEntry(
821 mnFocusLevel
, mnFocusEntry
);
825 SCCOLROW nStart
= pEntry
->GetStart();
826 SCCOLROW nEnd
= pEntry
->GetEnd();
827 size_t nNewLevel
= mnFocusLevel
;
828 size_t nNewEntry
= 0;
831 if ( bForward
&& (mnFocusLevel
+ 2 < nLevelCount
) )
833 // next level -> find first child entry
834 nNewLevel
= mnFocusLevel
+ 1;
835 bFound
= pArray
->GetEntryIndexInRange(nNewLevel
, nStart
, nEnd
, nNewEntry
);
837 else if ( !bForward
&& (mnFocusLevel
> 0) )
839 // previous level -> find parent entry
840 nNewLevel
= mnFocusLevel
- 1;
841 bFound
= pArray
->GetEntryIndex(nNewLevel
, nStart
, nNewEntry
);
844 if ( bFound
&& IsButtonVisible( nNewLevel
, nNewEntry
) )
846 mnFocusLevel
= nNewLevel
;
847 mnFocusEntry
= nNewEntry
;
855 bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward
)
858 size_t nOldLevel
= mnFocusLevel
;
859 size_t nOldEntry
= mnFocusEntry
;
863 /* one level up, if backward from left header,
864 or one level down, if forward from right header */
865 if ( (!bForward
) && (mnFocusEntry
== SC_OL_HEADERENTRY
) )
866 bRet
|= ImplMoveFocusByLevel( bForward
);
867 // move to next/previous entry
868 bool bWrapInLevel
= ImplMoveFocusByEntry( bForward
, false );
869 bRet
|= bWrapInLevel
;
870 /* one level up, if wrapped backward to right header,
871 or one level down, if wrapped forward to right header */
872 if ( bForward
&& bWrapInLevel
)
873 bRet
|= ImplMoveFocusByLevel( bForward
);
875 while ( !IsFocusButtonVisible() && ((nOldLevel
!= mnFocusLevel
) || (nOldEntry
!= mnFocusEntry
)) );
880 void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward
)
882 // first try to find an entry in the same level
883 if ( !IsFocusButtonVisible() )
884 ImplMoveFocusByEntry( bForward
, true );
885 // then try to find any other entry
886 if ( !IsFocusButtonVisible() )
887 ImplMoveFocusByTabOrder( bForward
);
890 void ScOutlineWindow::MoveFocusByEntry( bool bForward
)
893 ImplMoveFocusByEntry( bForward
, true );
897 void ScOutlineWindow::MoveFocusByLevel( bool bForward
)
900 ImplMoveFocusByLevel( bForward
);
904 void ScOutlineWindow::MoveFocusByTabOrder( bool bForward
)
907 ImplMoveFocusByTabOrder( bForward
);
911 void ScOutlineWindow::GetFocus()
917 void ScOutlineWindow::LoseFocus()
923 // mouse ----------------------------------------------------------------------
925 void ScOutlineWindow::StartMouseTracking( size_t nLevel
, size_t nEntry
)
930 DrawBorderRel( nLevel
, nEntry
, true );
933 void ScOutlineWindow::EndMouseTracking()
936 DrawBorderRel( mnMTLevel
, mnMTEntry
, false );
940 void ScOutlineWindow::MouseMove( const MouseEvent
& rMEvt
)
942 if ( IsMouseTracking() )
944 size_t nLevel
, nEntry
;
947 if ( ButtonHit( rMEvt
.GetPosPixel(), nLevel
, nEntry
) )
948 bHit
= (nLevel
== mnMTLevel
) && (nEntry
== mnMTEntry
);
950 if ( bHit
!= mbMTPressed
)
951 DrawBorderRel( mnMTLevel
, mnMTEntry
, bHit
);
955 void ScOutlineWindow::MouseButtonUp( const MouseEvent
& rMEvt
)
957 if ( IsMouseTracking() )
961 size_t nLevel
, nEntry
;
962 if ( ButtonHit( rMEvt
.GetPosPixel(), nLevel
, nEntry
) )
963 if ( (nLevel
== mnMTLevel
) && (nEntry
== mnMTEntry
) )
964 DoFunction( nLevel
, nEntry
);
968 void ScOutlineWindow::MouseButtonDown( const MouseEvent
& rMEvt
)
970 size_t nLevel
, nEntry
;
971 bool bHit
= ButtonHit( rMEvt
.GetPosPixel(), nLevel
, nEntry
);
973 StartMouseTracking( nLevel
, nEntry
);
974 else if ( rMEvt
.GetClicks() == 2 )
976 bHit
= LineHit( rMEvt
.GetPosPixel(), nLevel
, nEntry
);
978 DoFunction( nLevel
, nEntry
);
981 // if an item has been hit and window is focused, move focus to this item
982 if ( bHit
&& HasFocus() )
985 mnFocusLevel
= nLevel
;
986 mnFocusEntry
= nEntry
;
991 // keyboard -------------------------------------------------------------------
993 void ScOutlineWindow::KeyInput( const KeyEvent
& rKEvt
)
995 const vcl::KeyCode
& rKCode
= rKEvt
.GetKeyCode();
996 bool bNoMod
= !rKCode
.GetModifier();
997 bool bShift
= (rKCode
.GetModifier() == KEY_SHIFT
);
998 bool bCtrl
= (rKCode
.GetModifier() == KEY_MOD1
);
1000 sal_uInt16 nCode
= rKCode
.GetCode();
1001 bool bUpDownKey
= (nCode
== KEY_UP
) || (nCode
== KEY_DOWN
);
1002 bool bLeftRightKey
= (nCode
== KEY_LEFT
) || (nCode
== KEY_RIGHT
);
1005 if ( (nCode
== KEY_TAB
) && (bNoMod
|| bShift
) )
1006 // move forward without SHIFT key
1007 MoveFocusByTabOrder( bNoMod
); // TAB uses logical order, regardless of mirroring
1009 // LEFT/RIGHT/UP/DOWN keys
1010 else if ( bNoMod
&& (bUpDownKey
|| bLeftRightKey
) )
1012 bool bForward
= (nCode
== KEY_DOWN
) || (nCode
== KEY_RIGHT
);
1013 if ( mbHoriz
== bLeftRightKey
)
1014 // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
1015 MoveFocusByEntry( bForward
!= mbMirrorEntries
);
1017 // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
1018 MoveFocusByLevel( bForward
!= mbMirrorLevels
);
1022 else if ( bCtrl
&& (nCode
>= KEY_1
) && (nCode
<= KEY_9
) )
1024 size_t nLevel
= static_cast< size_t >( nCode
- KEY_1
);
1025 if ( nLevel
< GetLevelCount() )
1026 DoFunction( nLevel
, SC_OL_HEADERENTRY
);
1030 else switch ( rKCode
.GetFullCode() )
1032 case KEY_ADD
: DoExpand( mnFocusLevel
, mnFocusEntry
); break;
1033 case KEY_SUBTRACT
: DoCollapse( mnFocusLevel
, mnFocusEntry
); break;
1035 case KEY_RETURN
: DoFunction( mnFocusLevel
, mnFocusEntry
); break;
1036 default: Window::KeyInput( rKEvt
);
1040 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */