1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <vcl/taskpanelist.hxx>
21 #include <vcl/settings.hxx>
22 
23 #include <olinewin.hxx>
24 #include <olinetab.hxx>
25 #include <document.hxx>
26 #include <dbfunc.hxx>
27 #include <bitmaps.hlst>
28 
29 const long SC_OL_BITMAPSIZE                 = 12;
30 const long SC_OL_POSOFFSET                  = 2;
31 
32 const size_t SC_OL_NOLEVEL                  = static_cast< size_t >( -1 );
33 const size_t SC_OL_HEADERENTRY              = static_cast< size_t >( -1 );
34 
ScOutlineWindow(vcl::Window * pParent,ScOutlineMode eMode,ScViewData * pViewData,ScSplitPos eWhich)35 ScOutlineWindow::ScOutlineWindow( vcl::Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) :
36     Window( pParent ),
37     mrViewData( *pViewData ),
38     meWhich( eWhich ),
39     mbHoriz( eMode == SC_OUTLINE_HOR ),
40     mbMirrorEntries( false ),           // updated in SetHeaderSize
41     mbMirrorLevels( false ),            // updated in SetHeaderSize
42     maLineColor( COL_BLACK ),
43     mnHeaderSize( 0 ),
44     mnHeaderPos( 0 ),
45     mnMainFirstPos( 0 ),
46     mnMainLastPos( 0 ),
47     mbMTActive( false ),
48     mbMTPressed( false ),
49     mnFocusLevel( 0 ),
50     mnFocusEntry( SC_OL_HEADERENTRY ),
51     mbDontDrawFocus( false )
52 {
53     EnableRTL( false );                 // mirroring is done manually
54 
55     InitSettings();
56     maFocusRect.SetEmpty();
57     SetHeaderSize( 0 );
58 
59     // insert the window into task pane list for "F6 cycling"
60     if( SystemWindow* pSysWin = GetSystemWindow() )
61         if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
62             pTaskPaneList->AddWindow( this );
63 }
64 
~ScOutlineWindow()65 ScOutlineWindow::~ScOutlineWindow()
66 {
67     disposeOnce();
68 }
69 
dispose()70 void ScOutlineWindow::dispose()
71 {
72     // remove the window from task pane list
73     if( SystemWindow* pSysWin = GetSystemWindow() )
74         if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
75             pTaskPaneList->RemoveWindow( this );
76     vcl::Window::dispose();
77 }
78 
SetHeaderSize(long nNewSize)79 void ScOutlineWindow::SetHeaderSize( long nNewSize )
80 {
81     bool bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() );
82     mbMirrorEntries = bLayoutRTL && mbHoriz;
83     mbMirrorLevels = bLayoutRTL && !mbHoriz;
84 
85     bool bNew = (nNewSize != mnHeaderSize);
86     mnHeaderSize = nNewSize;
87     mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0;
88     mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize;
89     mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1;
90     if ( bNew )
91         Invalidate();
92 }
93 
GetDepthSize() const94 long ScOutlineWindow::GetDepthSize() const
95 {
96     long nSize = GetLevelCount() * SC_OL_BITMAPSIZE;
97     if ( nSize > 0 )
98         nSize += 2 * SC_OL_POSOFFSET + 1;
99     return nSize;
100 }
101 
ScrollPixel(long nDiff)102 void ScOutlineWindow::ScrollPixel( long nDiff )
103 {
104     HideFocus();
105     mbDontDrawFocus = true;
106 
107     long nStart = mnMainFirstPos;
108     long nEnd = mnMainLastPos;
109 
110     long nInvStart, nInvEnd;
111     if (nDiff < 0)
112     {
113         nStart -= nDiff;
114         nInvStart = nEnd + nDiff;
115         nInvEnd = nEnd;
116     }
117     else
118     {
119         nEnd -= nDiff;
120         nInvStart = nStart;
121         nInvEnd = nStart + nDiff;
122     }
123 
124     ScrollRel( nDiff, nStart, nEnd );
125     Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) );
126     Update();
127 
128     // if focus becomes invisible, move it to next visible button
129     ImplMoveFocusToVisible( nDiff < 0 );
130 
131     mbDontDrawFocus = false;
132     ShowFocus();
133 }
134 
ScrollRel(long nEntryDiff,long nEntryStart,long nEntryEnd)135 void ScOutlineWindow::ScrollRel( long nEntryDiff, long nEntryStart, long nEntryEnd )
136 {
137     tools::Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) );
138     if ( mbHoriz )
139         Scroll( nEntryDiff, 0, aRect );
140     else
141         Scroll( 0, nEntryDiff, aRect );
142 }
143 
144 // internal -------------------------------------------------------------------
145 
InitSettings()146 void ScOutlineWindow::InitSettings()
147 {
148     const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
149     SetBackground( rStyleSettings.GetFaceColor() );
150     maLineColor = rStyleSettings.GetButtonTextColor();
151     Invalidate();
152 }
153 
GetOutlineArray() const154 const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const
155 {
156     const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() );
157     if ( !pTable ) return nullptr;
158     return mbHoriz ? &pTable->GetColArray() : &pTable->GetRowArray();
159 }
160 
GetOutlineEntry(size_t nLevel,size_t nEntry) const161 const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const
162 {
163     const ScOutlineArray* pArray = GetOutlineArray();
164     return pArray ? pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ) : nullptr;
165 }
166 
IsHidden(SCCOLROW nColRowIndex) const167 bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const
168 {
169     return mbHoriz ?
170         GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) :
171         GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab());
172 }
173 
IsFiltered(SCCOLROW nColRowIndex) const174 bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const
175 {
176     // columns cannot be filtered
177     return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() );
178 }
179 
IsFirstVisible(SCCOLROW nColRowIndex) const180 bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const
181 {
182     bool bAllHidden = true;
183     for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos )
184         bAllHidden = IsHidden( nPos );
185     return bAllHidden;
186 }
187 
GetVisibleRange(SCCOLROW & rnColRowStart,SCCOLROW & rnColRowEnd) const188 void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const
189 {
190     if ( mbHoriz )
191     {
192         rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) );
193         rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) );
194     }
195     else
196     {
197         rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) );
198         rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) );
199     }
200 
201     // include collapsed columns/rows in front of visible range
202     while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) )
203         --rnColRowStart;
204 }
205 
GetPoint(long nLevelPos,long nEntryPos) const206 Point ScOutlineWindow::GetPoint( long nLevelPos, long nEntryPos ) const
207 {
208     return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos );
209 }
210 
GetRectangle(long nLevelStart,long nEntryStart,long nLevelEnd,long nEntryEnd) const211 tools::Rectangle ScOutlineWindow::GetRectangle(
212         long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd ) const
213 {
214     return tools::Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
215 }
216 
GetOutputSizeLevel() const217 long ScOutlineWindow::GetOutputSizeLevel() const
218 {
219     Size aSize( GetOutputSizePixel() );
220     return mbHoriz ? aSize.Height() : aSize.Width();
221 }
222 
GetOutputSizeEntry() const223 long ScOutlineWindow::GetOutputSizeEntry() const
224 {
225     Size aSize( GetOutputSizePixel() );
226     return mbHoriz ? aSize.Width() : aSize.Height();
227 }
228 
GetLevelCount() const229 size_t ScOutlineWindow::GetLevelCount() const
230 {
231     const ScOutlineArray* pArray = GetOutlineArray();
232     size_t nLevelCount = pArray ? pArray->GetDepth() : 0;
233     return nLevelCount ? (nLevelCount + 1) : 0;
234 }
235 
GetLevelPos(size_t nLevel) const236 long ScOutlineWindow::GetLevelPos( size_t nLevel ) const
237 {
238     // #i51970# must always return the *left* edge of the area used by a level
239     long nPos = static_cast< long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE );
240     return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos;
241 }
242 
GetLevelFromPos(long nLevelPos) const243 size_t ScOutlineWindow::GetLevelFromPos( long nLevelPos ) const
244 {
245     if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1;
246     long nStart = SC_OL_POSOFFSET;
247     if ( nLevelPos < nStart ) return SC_OL_NOLEVEL;
248     size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE );
249     return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL;
250 }
251 
GetColRowPos(SCCOLROW nColRowIndex) const252 long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const
253 {
254     long nDocPos = mbHoriz ?
255         mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, true ).X() :
256         mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, true ).Y();
257     return mnMainFirstPos + nDocPos;
258 }
259 
GetHeaderEntryPos() const260 long ScOutlineWindow::GetHeaderEntryPos() const
261 {
262     return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2;
263 }
264 
GetEntryPos(size_t nLevel,size_t nEntry,long & rnStartPos,long & rnEndPos,long & rnImagePos) const265 bool ScOutlineWindow::GetEntryPos(
266         size_t nLevel, size_t nEntry,
267         long& rnStartPos, long& rnEndPos, long& rnImagePos ) const
268 {
269     const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
270     if ( !pEntry || !pEntry->IsVisible() )
271         return false;
272 
273     SCCOLROW nStart = pEntry->GetStart();
274     SCCOLROW nEnd = pEntry->GetEnd();
275 
276     long nEntriesSign = mbMirrorEntries ? -1 : 1;
277 
278     // --- common calculation ---
279 
280     rnStartPos = GetColRowPos( nStart );
281     rnEndPos = GetColRowPos( nEnd + 1 );
282 
283     bool bHidden = IsHidden( nStart );
284     rnImagePos = bHidden ?
285                 (rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) :
286                 rnStartPos + nEntriesSign;
287     long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign +
288                         ( mbMirrorEntries ? 1 : 0 )) / 2;
289     rnImagePos = mbMirrorEntries ? std::max( rnImagePos, nCenter ) : std::min( rnImagePos, nCenter );
290 
291     // --- refinements ---
292 
293     // do not cut leftmost/topmost image
294     if ( bHidden && IsFirstVisible( nStart ) )
295         rnImagePos = rnStartPos;
296 
297     // do not cover previous collapsed image
298     bool bDoNoCover = !bHidden && nEntry;
299     const ScOutlineEntry* pPrevEntry = bDoNoCover ? GetOutlineEntry(nLevel, nEntry - 1) : nullptr;
300     if (pPrevEntry)
301     {
302         SCCOLROW nPrevEnd = pPrevEntry->GetEnd();
303         if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) )
304         {
305             if ( IsFirstVisible( pPrevEntry->GetStart() ) )
306                 rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign;
307             else
308                 rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign;
309             rnImagePos = rnStartPos;
310         }
311     }
312 
313     // restrict rnStartPos...rnEndPos to valid area
314     rnStartPos = std::max( rnStartPos, mnMainFirstPos );
315     rnEndPos = std::max( rnEndPos, mnMainFirstPos );
316 
317     if ( mbMirrorEntries )
318         rnImagePos -= SC_OL_BITMAPSIZE - 1;     // start pos aligns with right edge of bitmap
319 
320     // --- all rows filtered? ---
321 
322     bool bVisible = true;
323     if ( !mbHoriz )
324     {
325         bVisible = false;
326         for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow )
327             bVisible = !IsFiltered( nRow );
328     }
329     return bVisible;
330 }
331 
GetImagePos(size_t nLevel,size_t nEntry,Point & rPos) const332 bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const
333 {
334     bool bRet = nLevel < GetLevelCount();
335     if ( bRet )
336     {
337         long nLevelPos = GetLevelPos( nLevel );
338         if ( nEntry == SC_OL_HEADERENTRY )
339             rPos = GetPoint( nLevelPos, GetHeaderEntryPos() );
340         else
341         {
342             long nStartPos, nEndPos, nImagePos;
343             bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos );
344             rPos = GetPoint( nLevelPos, nImagePos );
345         }
346     }
347     return bRet;
348 }
349 
IsButtonVisible(size_t nLevel,size_t nEntry) const350 bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const
351 {
352     bool bRet = false;
353     if ( nEntry == SC_OL_HEADERENTRY )
354         bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount());
355     else
356     {
357         const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
358         if ( pEntry && pEntry->IsVisible() )
359         {
360             SCCOLROW nStart, nEnd;
361             GetVisibleRange( nStart, nEnd );
362             bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd);
363         }
364     }
365     return bRet;
366 }
367 
ItemHit(const Point & rPos,size_t & rnLevel,size_t & rnEntry,bool & rbButton) const368 bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const
369 {
370     const ScOutlineArray* pArray = GetOutlineArray();
371     if ( !pArray ) return false;
372 
373     SCCOLROW nStartIndex, nEndIndex;
374     GetVisibleRange( nStartIndex, nEndIndex );
375 
376     size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() );
377     if ( nLevel == SC_OL_NOLEVEL )
378         return false;
379 
380     long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y();
381 
382     // --- level buttons ---
383 
384     if ( mnHeaderSize > 0 )
385     {
386         long nImagePos = GetHeaderEntryPos();
387         if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
388         {
389             rnLevel = nLevel;
390             rnEntry = SC_OL_HEADERENTRY;
391             rbButton = true;
392             return true;
393         }
394     }
395 
396     // --- expand/collapse buttons and expanded lines ---
397 
398     // search outline entries backwards
399     size_t nEntry = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
400     while ( nEntry )
401     {
402         --nEntry;
403 
404         const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
405                                                          sal::static_int_cast<sal_uInt16>(nEntry) );
406         SCCOLROW nStart = pEntry->GetStart();
407         SCCOLROW nEnd = pEntry->GetEnd();
408 
409         if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) )
410         {
411             long nStartPos, nEndPos, nImagePos;
412             if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) )
413             {
414                 rnLevel = nLevel;
415                 rnEntry = nEntry;
416 
417                 // button?
418                 if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
419                 {
420                     rbButton = true;
421                     return true;
422                 }
423 
424                 // line?
425                 if ( mbMirrorEntries )
426                     ::std::swap( nStartPos, nEndPos );      // in RTL mode, nStartPos is the larger value
427                 if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) )
428                 {
429                     rbButton = false;
430                     return true;
431                 }
432             }
433         }
434     }
435 
436     return false;
437 }
438 
ButtonHit(const Point & rPos,size_t & rnLevel,size_t & rnEntry) const439 bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
440 {
441     bool bButton;
442     bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
443     return bRet && bButton;
444 }
445 
LineHit(const Point & rPos,size_t & rnLevel,size_t & rnEntry) const446 bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
447 {
448     bool bButton;
449     bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
450     return bRet && !bButton;
451 }
452 
DoFunction(size_t nLevel,size_t nEntry) const453 void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const
454 {
455     ScDBFunc& rFunc = *mrViewData.GetView();
456     if ( nEntry == SC_OL_HEADERENTRY )
457         rFunc.SelectLevel( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel) );
458     else
459     {
460         const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
461         if ( pEntry )
462         {
463             if ( pEntry->IsHidden() )
464                 rFunc.ShowOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
465             else
466                 rFunc.HideOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
467         }
468     }
469 }
470 
DoExpand(size_t nLevel,size_t nEntry) const471 void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const
472 {
473     const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
474     if ( pEntry && pEntry->IsHidden() )
475         DoFunction( nLevel, nEntry );
476 }
477 
DoCollapse(size_t nLevel,size_t nEntry) const478 void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const
479 {
480     const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
481     if ( pEntry && !pEntry->IsHidden() )
482         DoFunction( nLevel, nEntry );
483 }
484 
Resize()485 void ScOutlineWindow::Resize()
486 {
487     Window::Resize();
488     SetHeaderSize( mnHeaderSize );  // recalculates header/group positions
489     if ( !IsFocusButtonVisible() )
490     {
491         HideFocus();
492         ShowFocus();    // calculates valid position
493     }
494 }
495 
DataChanged(const DataChangedEvent & rDCEvt)496 void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt )
497 {
498     if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
499          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
500     {
501         InitSettings();
502         Invalidate();
503     }
504     Window::DataChanged( rDCEvt );
505 }
506 
507 // drawing --------------------------------------------------------------------
508 
SetEntryAreaClipRegion()509 void ScOutlineWindow::SetEntryAreaClipRegion()
510 {
511     SetClipRegion( vcl::Region(tools::Rectangle(
512         GetPoint( 0, mnMainFirstPos ),
513         GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ))));
514 }
515 
DrawLineRel(long nLevelStart,long nEntryStart,long nLevelEnd,long nEntryEnd)516 void ScOutlineWindow::DrawLineRel(
517         long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
518 {
519     DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
520 }
521 
DrawRectRel(long nLevelStart,long nEntryStart,long nLevelEnd,long nEntryEnd)522 void ScOutlineWindow::DrawRectRel(
523         long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
524 {
525     DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) );
526 }
527 
528 namespace
529 {
GetImage(const OUString & rId)530     Image GetImage(const OUString& rId)
531     {
532         return Image(StockImage::Yes, rId);
533     }
534 }
535 
DrawImageRel(long nLevelPos,long nEntryPos,const OUString & rId)536 void ScOutlineWindow::DrawImageRel(long nLevelPos, long nEntryPos, const OUString& rId)
537 {
538     const Image& rImage = GetImage(rId);
539     SetLineColor();
540     SetFillColor( GetBackground().GetColor() );
541     Point aPos( GetPoint( nLevelPos, nEntryPos ) );
542     DrawRect( tools::Rectangle( aPos, rImage.GetSizePixel() ) );
543     DrawImage( aPos, rImage );
544 }
545 
DrawBorderRel(size_t nLevel,size_t nEntry,bool bPressed)546 void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed )
547 {
548     Point aPos;
549     if ( GetImagePos( nLevel, nEntry, aPos ) )
550     {
551         OUString sId = bPressed ? OUString(RID_BMP_PRESSED) : OUString(RID_BMP_NOTPRESSED);
552         bool bClip = (nEntry != SC_OL_HEADERENTRY);
553         if ( bClip )
554             SetEntryAreaClipRegion();
555         DrawImage(aPos, GetImage(sId));
556         if ( bClip )
557             SetClipRegion();
558     }
559     mbMTPressed = bPressed;
560 }
561 
ShowFocus()562 void ScOutlineWindow::ShowFocus()
563 {
564     if ( HasFocus() )
565     {
566         // first move to a visible position
567         ImplMoveFocusToVisible( true );
568 
569         if ( IsFocusButtonVisible() )
570         {
571             Point aPos;
572             if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) )
573             {
574                 aPos += Point( 1, 1 );
575                 maFocusRect = tools::Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) );
576                 bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
577                 if ( bClip )
578                     SetEntryAreaClipRegion();
579                 InvertTracking( maFocusRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
580                 if ( bClip )
581                     SetClipRegion();
582             }
583         }
584     }
585 }
586 
HideFocus()587 void ScOutlineWindow::HideFocus()
588 {
589     if ( !maFocusRect.IsEmpty() )
590     {
591         bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
592         if ( bClip )
593             SetEntryAreaClipRegion();
594         InvertTracking( maFocusRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
595         if ( bClip )
596             SetClipRegion();
597         maFocusRect.SetEmpty();
598     }
599 }
600 
601 static const OUStringLiteral aLevelBmps[]=
602 {
603     RID_BMP_LEVEL1,
604     RID_BMP_LEVEL2,
605     RID_BMP_LEVEL3,
606     RID_BMP_LEVEL4,
607     RID_BMP_LEVEL5,
608     RID_BMP_LEVEL6,
609     RID_BMP_LEVEL7,
610     RID_BMP_LEVEL8
611 };
612 
Paint(vcl::RenderContext &,const tools::Rectangle &)613 void ScOutlineWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& /* rRect */ )
614 {
615     long nEntriesSign = mbMirrorEntries ? -1 : 1;
616     long nLevelsSign  = mbMirrorLevels  ? -1 : 1;
617 
618     Size aSize = GetOutputSizePixel();
619     long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1;
620     long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1;
621 
622     SetLineColor( maLineColor );
623     long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd;
624     DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd );
625 
626     const ScOutlineArray* pArray = GetOutlineArray();
627     if ( !pArray ) return;
628 
629     size_t nLevelCount = GetLevelCount();
630 
631     // --- draw header images ---
632 
633     if ( mnHeaderSize > 0 )
634     {
635         long nEntryPos = GetHeaderEntryPos();
636         for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel )
637             DrawImageRel(GetLevelPos(nLevel), nEntryPos, aLevelBmps[nLevel]);
638 
639         SetLineColor( maLineColor );
640         long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1));
641         DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos );
642     }
643 
644     // --- draw lines & collapse/expand images ---
645 
646     SetEntryAreaClipRegion();
647 
648     SCCOLROW nStartIndex, nEndIndex;
649     GetVisibleRange( nStartIndex, nEndIndex );
650 
651     for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel )
652     {
653         long nLevelPos = GetLevelPos( nLevel );
654         long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0;
655 
656         size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
657         size_t nEntry;
658 
659         // first draw all lines in the current level
660         SetLineColor();
661         SetFillColor( maLineColor );
662         for ( nEntry = 0; nEntry < nEntryCount; ++nEntry )
663         {
664             const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
665                                                              sal::static_int_cast<sal_uInt16>(nEntry) );
666             SCCOLROW nStart = pEntry->GetStart();
667             SCCOLROW nEnd = pEntry->GetEnd();
668 
669             // visible range?
670             bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex);
671             // find output coordinates
672             if ( bDraw )
673                 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
674             // draw, if not collapsed
675             if ( bDraw && !pEntry->IsHidden() )
676             {
677                 if ( nStart >= nStartIndex )
678                     nEntryPos1 += nEntriesSign;
679                 nEntryPos2 -= 2 * nEntriesSign;
680                 long nLinePos = nLevelPos;
681                 if ( mbMirrorLevels )
682                     nLinePos += SC_OL_BITMAPSIZE - 1;   // align with right edge of bitmap
683                 DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 );
684 
685                 if ( nEnd <= nEndIndex )
686                     DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign,
687                                  nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 );
688             }
689         }
690 
691         // draw all images in the level from last to first
692         nEntry = nEntryCount;
693         while ( nEntry )
694         {
695             --nEntry;
696 
697             const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
698                                                              sal::static_int_cast<sal_uInt16>(nEntry) );
699             SCCOLROW nStart = pEntry->GetStart();
700 
701             // visible range?
702             bool bDraw = (nStartIndex <= nStart) && (nStart <= nEndIndex + 1);
703             // find output coordinates
704             if ( bDraw )
705                 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
706             // draw, if not hidden by higher levels
707             if ( bDraw )
708             {
709                 OUString sImageId = pEntry->IsHidden() ? OUString(RID_BMP_PLUS) : OUString(RID_BMP_MINUS);
710                 DrawImageRel(nLevelPos, nImagePos, sImageId);
711             }
712         }
713     }
714 
715     SetClipRegion();
716 
717     if ( !mbDontDrawFocus )
718         ShowFocus();
719 }
720 
721 // focus ----------------------------------------------------------------------
722 
723 /** Increments or decrements a value and wraps at the specified limits.
724     @return  true = value wrapped. */
lcl_RotateValue(size_t & rnValue,size_t nMin,size_t nMax,bool bForward)725 static bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward )
726 {
727     OSL_ENSURE( nMin <= nMax, "lcl_RotateValue - invalid range" );
728     OSL_ENSURE( nMax < static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" );
729     bool bWrap = false;
730     if ( bForward )
731     {
732         if ( rnValue < nMax )
733             ++rnValue;
734         else
735         {
736             rnValue = nMin;
737             bWrap = true;
738         }
739     }
740     else
741     {
742         if ( rnValue > nMin )
743             --rnValue;
744         else
745         {
746             rnValue = nMax;
747             bWrap = true;
748         }
749     }
750     return bWrap;
751 }
752 
IsFocusButtonVisible() const753 bool ScOutlineWindow::IsFocusButtonVisible() const
754 {
755     return IsButtonVisible( mnFocusLevel, mnFocusEntry );
756 }
757 
ImplMoveFocusByEntry(bool bForward,bool bFindVisible)758 bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward, bool bFindVisible )
759 {
760     const ScOutlineArray* pArray = GetOutlineArray();
761     if ( !pArray )
762         return false;
763 
764     bool bWrapped = false;
765     size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(mnFocusLevel) );
766     // #i29530# entry count may be decreased after changing active sheet
767     if( mnFocusEntry >= nEntryCount )
768         mnFocusEntry = SC_OL_HEADERENTRY;
769     size_t nOldEntry = mnFocusEntry;
770 
771     do
772     {
773         if ( mnFocusEntry == SC_OL_HEADERENTRY )
774         {
775             // move from header to first or last entry
776             if ( nEntryCount > 0 )
777                 mnFocusEntry = bForward ? 0 : (nEntryCount - 1);
778             /*  wrapped, if forward from right header to first entry,
779                 or if backward from left header to last entry */
780             // Header and entries are now always in consistent order,
781             // so there's no need to check for mirroring here.
782             if ( !nEntryCount || !bForward )
783                 bWrapped = true;
784         }
785         else if ( lcl_RotateValue( mnFocusEntry, 0, nEntryCount - 1, bForward ) )
786         {
787             // lcl_RotateValue returns true -> wrapped the entry range -> move to header
788             mnFocusEntry = SC_OL_HEADERENTRY;
789             /*  wrapped, if forward from last entry to left header,
790                 or if backward from first entry to right header */
791             if ( bForward )
792                 bWrapped = true;
793         }
794     }
795     while ( bFindVisible && !IsFocusButtonVisible() && (nOldEntry != mnFocusEntry) );
796 
797     return bWrapped;
798 }
799 
ImplMoveFocusByLevel(bool bForward)800 bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward )
801 {
802     const ScOutlineArray* pArray = GetOutlineArray();
803     if ( !pArray )
804         return false;
805 
806     bool bWrapped = false;
807     size_t nLevelCount = GetLevelCount();
808 
809     if ( mnFocusEntry == SC_OL_HEADERENTRY )
810     {
811         if ( nLevelCount > 0 )
812             bWrapped = lcl_RotateValue( mnFocusLevel, 0, nLevelCount - 1, bForward );
813     }
814     else
815     {
816         const ScOutlineEntry* pEntry = pArray->GetEntry(
817             mnFocusLevel, mnFocusEntry);
818 
819         if ( pEntry )
820         {
821             SCCOLROW nStart = pEntry->GetStart();
822             SCCOLROW nEnd = pEntry->GetEnd();
823             size_t nNewLevel = mnFocusLevel;
824             size_t nNewEntry = 0;
825 
826             bool bFound = false;
827             if ( bForward && (mnFocusLevel + 2 < nLevelCount) )
828             {
829                 // next level -> find first child entry
830                 nNewLevel = mnFocusLevel + 1;
831                 bFound = pArray->GetEntryIndexInRange(nNewLevel, nStart, nEnd, nNewEntry);
832             }
833             else if ( !bForward && (mnFocusLevel > 0) )
834             {
835                 // previous level -> find parent entry
836                 nNewLevel = mnFocusLevel - 1;
837                 bFound = pArray->GetEntryIndex(nNewLevel, nStart, nNewEntry);
838             }
839 
840             if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) )
841             {
842                 mnFocusLevel = nNewLevel;
843                 mnFocusEntry = nNewEntry;
844             }
845         }
846     }
847 
848     return bWrapped;
849 }
850 
ImplMoveFocusByTabOrder(bool bForward)851 bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward )
852 {
853     bool bRet = false;
854     size_t nOldLevel = mnFocusLevel;
855     size_t nOldEntry = mnFocusEntry;
856 
857     do
858     {
859         /*  one level up, if backward from left header,
860             or one level down, if forward from right header */
861         if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) )
862             bRet |= ImplMoveFocusByLevel( bForward );
863         // move to next/previous entry
864         bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false );
865         bRet |= bWrapInLevel;
866         /*  one level up, if wrapped backward to right header,
867             or one level down, if wrapped forward to right header */
868         if ( bForward && bWrapInLevel )
869             bRet |= ImplMoveFocusByLevel( bForward );
870     }
871     while ( !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) );
872 
873     return bRet;
874 }
875 
ImplMoveFocusToVisible(bool bForward)876 void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward )
877 {
878     // first try to find an entry in the same level
879     if ( !IsFocusButtonVisible() )
880         ImplMoveFocusByEntry( bForward, true );
881     // then try to find any other entry
882     if ( !IsFocusButtonVisible() )
883         ImplMoveFocusByTabOrder( bForward );
884 }
885 
MoveFocusByEntry(bool bForward)886 void ScOutlineWindow::MoveFocusByEntry( bool bForward )
887 {
888     HideFocus();
889     ImplMoveFocusByEntry( bForward, true );
890     ShowFocus();
891 }
892 
MoveFocusByLevel(bool bForward)893 void ScOutlineWindow::MoveFocusByLevel( bool bForward )
894 {
895     HideFocus();
896     ImplMoveFocusByLevel( bForward );
897     ShowFocus();
898 }
899 
MoveFocusByTabOrder(bool bForward)900 void ScOutlineWindow::MoveFocusByTabOrder( bool bForward )
901 {
902     HideFocus();
903     ImplMoveFocusByTabOrder( bForward );
904     ShowFocus();
905 }
906 
GetFocus()907 void ScOutlineWindow::GetFocus()
908 {
909     Window::GetFocus();
910     ShowFocus();
911 }
912 
LoseFocus()913 void ScOutlineWindow::LoseFocus()
914 {
915     HideFocus();
916     Window::LoseFocus();
917 }
918 
919 // mouse ----------------------------------------------------------------------
920 
StartMouseTracking(size_t nLevel,size_t nEntry)921 void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry )
922 {
923     mbMTActive = true;
924     mnMTLevel = nLevel;
925     mnMTEntry = nEntry;
926     DrawBorderRel( nLevel, nEntry, true );
927 }
928 
EndMouseTracking()929 void ScOutlineWindow::EndMouseTracking()
930 {
931     if ( mbMTPressed )
932         DrawBorderRel( mnMTLevel, mnMTEntry, false );
933     mbMTActive = false;
934 }
935 
MouseMove(const MouseEvent & rMEvt)936 void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt )
937 {
938     if ( IsMouseTracking() )
939     {
940         size_t nLevel, nEntry;
941         bool bHit = false;
942 
943         if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
944             bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry);
945 
946         if ( bHit != mbMTPressed )
947             DrawBorderRel( mnMTLevel, mnMTEntry, bHit );
948     }
949 }
950 
MouseButtonUp(const MouseEvent & rMEvt)951 void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt )
952 {
953     if ( IsMouseTracking() )
954     {
955         EndMouseTracking();
956 
957         size_t nLevel, nEntry;
958         if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
959             if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) )
960                 DoFunction( nLevel, nEntry );
961     }
962 }
963 
MouseButtonDown(const MouseEvent & rMEvt)964 void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt )
965 {
966     size_t nLevel, nEntry;
967     bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry );
968     if ( bHit )
969         StartMouseTracking( nLevel, nEntry );
970     else if ( rMEvt.GetClicks() == 2 )
971     {
972         bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry );
973         if ( bHit )
974             DoFunction( nLevel, nEntry );
975     }
976 
977     // if an item has been hit and window is focused, move focus to this item
978     if ( bHit && HasFocus() )
979     {
980         HideFocus();
981         mnFocusLevel = nLevel;
982         mnFocusEntry = nEntry;
983         ShowFocus();
984     }
985 }
986 
987 // keyboard -------------------------------------------------------------------
988 
KeyInput(const KeyEvent & rKEvt)989 void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt )
990 {
991     const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
992     bool bNoMod = !rKCode.GetModifier();
993     bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
994     bool bCtrl = (rKCode.GetModifier() == KEY_MOD1);
995 
996     sal_uInt16 nCode = rKCode.GetCode();
997     bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN);
998     bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT);
999 
1000     // TAB key
1001     if ( (nCode == KEY_TAB) && (bNoMod || bShift) )
1002         // move forward without SHIFT key
1003         MoveFocusByTabOrder( bNoMod );      // TAB uses logical order, regardless of mirroring
1004 
1005     // LEFT/RIGHT/UP/DOWN keys
1006     else if ( bNoMod && (bUpDownKey || bLeftRightKey) )
1007     {
1008         bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT);
1009         if ( mbHoriz == bLeftRightKey )
1010             // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
1011             MoveFocusByEntry( bForward != mbMirrorEntries );
1012         else
1013             // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
1014             MoveFocusByLevel( bForward != mbMirrorLevels );
1015     }
1016 
1017     // CTRL + number
1018     else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) )
1019     {
1020         size_t nLevel = static_cast< size_t >( nCode - KEY_1 );
1021         if ( nLevel < GetLevelCount() )
1022             DoFunction( nLevel, SC_OL_HEADERENTRY );
1023     }
1024 
1025     // other key codes
1026     else switch ( rKCode.GetFullCode() )
1027     {
1028         case KEY_ADD:       DoExpand( mnFocusLevel, mnFocusEntry );     break;
1029         case KEY_SUBTRACT:  DoCollapse( mnFocusLevel, mnFocusEntry );   break;
1030         case KEY_SPACE:
1031         case KEY_RETURN:    DoFunction( mnFocusLevel, mnFocusEntry );   break;
1032         default:            Window::KeyInput( rKEvt );
1033     }
1034 }
1035 
1036 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1037