xref: /aoo41x/main/sc/source/ui/view/olinewin.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 
32 #include <vcl/svapp.hxx>
33 #include <vcl/taskpanelist.hxx>
34 
35 #include "olinewin.hxx"
36 #include "olinetab.hxx"
37 #include "document.hxx"
38 #include "dbfunc.hxx"
39 #include "sc.hrc"
40 
41 // ============================================================================
42 
43 const long SC_OL_BITMAPSIZE                 = 12;
44 const long SC_OL_POSOFFSET                  = 2;
45 
46 const size_t SC_OL_NOLEVEL                  = static_cast< size_t >( -1 );
47 const size_t SC_OL_HEADERENTRY              = static_cast< size_t >( -1 );
48 
49 const sal_uInt16 SC_OL_IMAGE_PLUS               = 9;
50 const sal_uInt16 SC_OL_IMAGE_MINUS              = SC_OL_IMAGE_PLUS + 1;
51 const sal_uInt16 SC_OL_IMAGE_NOTPRESSED         = SC_OL_IMAGE_MINUS + 1;
52 const sal_uInt16 SC_OL_IMAGE_PRESSED            = SC_OL_IMAGE_NOTPRESSED + 1;
53 
54 // ============================================================================
55 
56 ScOutlineWindow::ScOutlineWindow( Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) :
57 	Window( pParent ),
58     mrViewData( *pViewData ),
59     meWhich( eWhich ),
60     mbHoriz( eMode == SC_OUTLINE_HOR ),
61     mbMirrorEntries( false ),           // updated in SetHeaderSize
62     mbMirrorLevels( false ),            // updated in SetHeaderSize
63     mpSymbols( NULL ),
64     maLineColor( COL_BLACK ),
65     mnHeaderSize( 0 ),
66     mnHeaderPos( 0 ),
67     mnMainFirstPos( 0 ),
68     mnMainLastPos( 0 ),
69     mbMTActive( false ),
70     mbMTPressed( false ),
71     mnFocusLevel( 0 ),
72     mnFocusEntry( SC_OL_HEADERENTRY ),
73     mbDontDrawFocus( false )
74 {
75     EnableRTL( sal_False );                 // mirroring is done manually
76 
77     InitSettings();
78     maFocusRect.SetEmpty();
79     SetHeaderSize( 0 );
80 
81     // insert the window into task pane list for "F6 cycling"
82     if( SystemWindow* pSysWin = GetSystemWindow() )
83         if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
84             pTaskPaneList->AddWindow( this );
85 }
86 
87 ScOutlineWindow::~ScOutlineWindow()
88 {
89     // remove the window from task pane list
90     if( SystemWindow* pSysWin = GetSystemWindow() )
91         if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
92             pTaskPaneList->RemoveWindow( this );
93 }
94 
95 void ScOutlineWindow::SetHeaderSize( long nNewSize )
96 {
97     sal_Bool bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() );
98     mbMirrorEntries = bLayoutRTL && mbHoriz;
99     mbMirrorLevels = bLayoutRTL && !mbHoriz;
100 
101     bool bNew = (nNewSize != mnHeaderSize);
102     mnHeaderSize = nNewSize;
103     mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0;
104     mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize;
105     mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1;
106     if ( bNew )
107         Invalidate();
108 }
109 
110 long ScOutlineWindow::GetDepthSize() const
111 {
112     long nSize = GetLevelCount() * SC_OL_BITMAPSIZE;
113     if ( nSize > 0 )
114         nSize += 2 * SC_OL_POSOFFSET + 1;
115     return nSize;
116 }
117 
118 void ScOutlineWindow::ScrollPixel( long nDiff )
119 {
120     HideFocus();
121     mbDontDrawFocus = true;
122 
123     long nStart = mnMainFirstPos;
124     long nEnd = mnMainLastPos;
125 
126     long nInvStart, nInvEnd;
127     if (nDiff < 0)
128     {
129         nStart -= nDiff;
130         nInvStart = nEnd + nDiff;
131         nInvEnd = nEnd;
132     }
133     else
134     {
135         nEnd -= nDiff;
136         nInvStart = nStart;
137         nInvEnd = nStart + nDiff;
138     }
139 
140     ScrollRel( nDiff, nStart, nEnd );
141     Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) );
142     Update();
143 
144     // if focus becomes invisible, move it to next visible button
145     ImplMoveFocusToVisible( nDiff < 0 );
146 
147     mbDontDrawFocus = false;
148     ShowFocus();
149 }
150 
151 void ScOutlineWindow::ScrollRel( long nEntryDiff, long nEntryStart, long nEntryEnd )
152 {
153     Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) );
154     if ( mbHoriz )
155         Scroll( nEntryDiff, 0, aRect );
156     else
157         Scroll( 0, nEntryDiff, aRect );
158 }
159 
160 // internal -------------------------------------------------------------------
161 
162 void ScOutlineWindow::InitSettings()
163 {
164     const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
165     SetBackground( rStyleSettings.GetFaceColor() );
166     maLineColor = rStyleSettings.GetButtonTextColor();
167     mpSymbols = ScGlobal::GetOutlineSymbols( rStyleSettings.GetHighContrastMode() );
168     Invalidate();
169 }
170 
171 const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const
172 {
173     const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() );
174     if ( !pTable ) return NULL;
175     return mbHoriz ? pTable->GetColArray() : pTable->GetRowArray();
176 }
177 
178 const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const
179 {
180     const ScOutlineArray* pArray = GetOutlineArray();
181     return pArray ? pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ) : NULL;
182 }
183 
184 bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const
185 {
186     return mbHoriz ?
187         GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) :
188         GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab());
189 }
190 
191 bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const
192 {
193     // columns cannot be filtered
194     return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() );
195 }
196 
197 bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const
198 {
199     bool bAllHidden = true;
200     for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos )
201         bAllHidden = IsHidden( nPos );
202     return bAllHidden;
203 }
204 
205 void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const
206 {
207     if ( mbHoriz )
208     {
209         rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) );
210         rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) );
211     }
212     else
213     {
214         rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) );
215         rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) );
216     }
217 
218     // include collapsed columns/rows in front of visible range
219     while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) )
220         --rnColRowStart;
221 }
222 
223 Point ScOutlineWindow::GetPoint( long nLevelPos, long nEntryPos ) const
224 {
225     return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos );
226 }
227 
228 Rectangle ScOutlineWindow::GetRectangle(
229         long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd ) const
230 {
231     return Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
232 }
233 
234 long ScOutlineWindow::GetOutputSizeLevel() const
235 {
236     Size aSize( GetOutputSizePixel() );
237     return mbHoriz ? aSize.Height() : aSize.Width();
238 }
239 
240 long ScOutlineWindow::GetOutputSizeEntry() const
241 {
242     Size aSize( GetOutputSizePixel() );
243     return mbHoriz ? aSize.Width() : aSize.Height();
244 }
245 
246 size_t ScOutlineWindow::GetLevelCount() const
247 {
248     const ScOutlineArray* pArray = GetOutlineArray();
249     size_t nLevelCount = pArray ? pArray->GetDepth() : 0;
250     return nLevelCount ? (nLevelCount + 1) : 0;
251 }
252 
253 long ScOutlineWindow::GetLevelPos( size_t nLevel ) const
254 {
255     // #i51970# must always return the *left* edge of the area used by a level
256     long nPos = static_cast< long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE );
257     return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos;
258 }
259 
260 size_t ScOutlineWindow::GetLevelFromPos( long nLevelPos ) const
261 {
262     if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1;
263     long nStart = SC_OL_POSOFFSET;
264     if ( nLevelPos < nStart ) return SC_OL_NOLEVEL;
265     size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE );
266     return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL;
267 }
268 
269 long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const
270 {
271     long nDocPos = mbHoriz ?
272         mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, sal_True ).X() :
273         mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, sal_True ).Y();
274     return mnMainFirstPos + nDocPos;
275 }
276 
277 long ScOutlineWindow::GetHeaderEntryPos() const
278 {
279     return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2;
280 }
281 
282 bool ScOutlineWindow::GetEntryPos(
283         size_t nLevel, size_t nEntry,
284         long& rnStartPos, long& rnEndPos, long& rnImagePos ) const
285 {
286     const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
287     if ( !pEntry || !pEntry->IsVisible() )
288         return false;
289 
290     SCCOLROW nStart = pEntry->GetStart();
291     SCCOLROW nEnd = pEntry->GetEnd();
292 
293     long nEntriesSign = mbMirrorEntries ? -1 : 1;
294 
295     // --- common calculation ---
296 
297     rnStartPos = GetColRowPos( nStart );
298     rnEndPos = GetColRowPos( nEnd + 1 );
299 
300     bool bHidden = IsHidden( nStart );
301     rnImagePos = bHidden ?
302                 (rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) :
303                 rnStartPos + nEntriesSign;
304     long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign +
305                         ( mbMirrorEntries ? 1 : 0 )) / 2L;
306     rnImagePos = mbMirrorEntries ? Max( rnImagePos, nCenter ) : Min( rnImagePos, nCenter );
307 
308     // --- refinements ---
309 
310     // do not cut leftmost/topmost image
311     if ( bHidden && IsFirstVisible( nStart ) )
312         rnImagePos = rnStartPos;
313 
314     // do not cover previous collapsed image
315     if ( !bHidden && nEntry )
316     {
317         const ScOutlineEntry* pPrevEntry = GetOutlineEntry( nLevel, nEntry - 1 );
318         SCCOLROW nPrevEnd = pPrevEntry->GetEnd();
319         if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) )
320         {
321             if ( IsFirstVisible( pPrevEntry->GetStart() ) )
322                 rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign;
323             else
324                 rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign;
325             rnImagePos = rnStartPos;
326         }
327     }
328 
329     // restrict rnStartPos...rnEndPos to valid area
330     rnStartPos = std::max( rnStartPos, mnMainFirstPos );
331     rnEndPos = std::max( rnEndPos, mnMainFirstPos );
332 
333     if ( mbMirrorEntries )
334         rnImagePos -= SC_OL_BITMAPSIZE - 1;     // start pos aligns with right edge of bitmap
335 
336     // --- all rows filtered? ---
337 
338     bool bVisible = true;
339     if ( !mbHoriz )
340     {
341         bVisible = false;
342         for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow )
343             bVisible = !IsFiltered( nRow );
344     }
345     return bVisible;
346 }
347 
348 bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const
349 {
350     bool bRet = nLevel < GetLevelCount();
351     if ( bRet )
352     {
353         long nLevelPos = GetLevelPos( nLevel );
354         if ( nEntry == SC_OL_HEADERENTRY )
355             rPos = GetPoint( nLevelPos, GetHeaderEntryPos() );
356         else
357         {
358             long nStartPos, nEndPos, nImagePos;
359             bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos );
360             rPos = GetPoint( nLevelPos, nImagePos );
361         }
362     }
363     return bRet;
364 }
365 
366 bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const
367 {
368     bool bRet = false;
369     if ( nEntry == SC_OL_HEADERENTRY )
370         bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount());
371     else
372     {
373         const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
374         if ( pEntry && pEntry->IsVisible() )
375         {
376             SCCOLROW nStart, nEnd;
377             GetVisibleRange( nStart, nEnd );
378             bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd);
379         }
380     }
381     return bRet;
382 }
383 
384 bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const
385 {
386     const ScOutlineArray* pArray = GetOutlineArray();
387     if ( !pArray ) return false;
388 
389     SCCOLROW nStartIndex, nEndIndex;
390     GetVisibleRange( nStartIndex, nEndIndex );
391 
392     size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() );
393     if ( nLevel == SC_OL_NOLEVEL )
394         return false;
395 
396 //    long nLevelPos = GetLevelPos( nLevel );
397     long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y();
398 
399     // --- level buttons ---
400 
401     if ( mnHeaderSize > 0 )
402     {
403         long nImagePos = GetHeaderEntryPos();
404         if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
405         {
406             rnLevel = nLevel;
407             rnEntry = SC_OL_HEADERENTRY;
408             rbButton = true;
409             return true;
410         }
411     }
412 
413     // --- expand/collapse buttons and expanded lines ---
414 
415     // search outline entries backwards
416     size_t nEntry = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
417     while ( nEntry )
418     {
419         --nEntry;
420 
421         const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
422                                                          sal::static_int_cast<sal_uInt16>(nEntry) );
423         SCCOLROW nStart = pEntry->GetStart();
424         SCCOLROW nEnd = pEntry->GetEnd();
425 
426         if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) )
427         {
428             long nStartPos, nEndPos, nImagePos;
429             if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) )
430             {
431                 rnLevel = nLevel;
432                 rnEntry = nEntry;
433 
434                 // button?
435                 if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
436                 {
437                     rbButton = true;
438                     return true;
439                 }
440 
441                 // line?
442                 if ( mbMirrorEntries )
443                     ::std::swap( nStartPos, nEndPos );      // in RTL mode, nStartPos is the larger value
444                 if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) )
445                 {
446                     rbButton = false;
447                     return true;
448                 }
449             }
450         }
451     }
452 
453     return false;
454 }
455 
456 bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
457 {
458     bool bButton;
459     bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
460     return bRet && bButton;
461 }
462 
463 bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
464 {
465     bool bButton;
466     bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
467     return bRet && !bButton;
468 }
469 
470 void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const
471 {
472     ScDBFunc& rFunc = *mrViewData.GetView();
473     if ( nEntry == SC_OL_HEADERENTRY )
474         rFunc.SelectLevel( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel) );
475     else
476     {
477         const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
478         if ( pEntry )
479         {
480             if ( pEntry->IsHidden() )
481                 rFunc.ShowOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
482             else
483                 rFunc.HideOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
484         }
485     }
486 }
487 
488 void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const
489 {
490     const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
491     if ( pEntry && pEntry->IsHidden() )
492         DoFunction( nLevel, nEntry );
493 }
494 
495 void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const
496 {
497     const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
498     if ( pEntry && !pEntry->IsHidden() )
499         DoFunction( nLevel, nEntry );
500 }
501 
502 void ScOutlineWindow::Resize()
503 {
504     Window::Resize();
505     SetHeaderSize( mnHeaderSize );  // recalculates header/group positions
506     if ( !IsFocusButtonVisible() )
507     {
508         HideFocus();
509         ShowFocus();    // calculates valid position
510     }
511 }
512 
513 void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt )
514 {
515     if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
516          (rDCEvt.GetFlags() & SETTINGS_STYLE) )
517     {
518         InitSettings();
519         Invalidate();
520     }
521     Window::DataChanged( rDCEvt );
522 }
523 
524 // drawing --------------------------------------------------------------------
525 
526 void ScOutlineWindow::SetEntryAreaClipRegion()
527 {
528     SetClipRegion( Rectangle(
529         GetPoint( 0, mnMainFirstPos ),
530         GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ) ) );
531 }
532 
533 void ScOutlineWindow::DrawLineRel(
534         long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
535 {
536     DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
537 }
538 
539 void ScOutlineWindow::DrawRectRel(
540         long nLevelStart, long nEntryStart, long nLevelEnd, long nEntryEnd )
541 {
542     DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) );
543 }
544 
545 void ScOutlineWindow::DrawImageRel( long nLevelPos, long nEntryPos, sal_uInt16 nId )
546 {
547     DBG_ASSERT( mpSymbols, "ScOutlineWindow::DrawImageRel - no images" );
548     const Image& rImage = mpSymbols->GetImage( nId );
549     SetLineColor();
550     SetFillColor( GetBackground().GetColor() );
551     Point aPos( GetPoint( nLevelPos, nEntryPos ) );
552     DrawRect( Rectangle( aPos, rImage.GetSizePixel() ) );
553     DrawImage( aPos, rImage );
554 }
555 
556 void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed )
557 {
558     Point aPos;
559     if ( GetImagePos( nLevel, nEntry, aPos ) )
560     {
561         DBG_ASSERT( mpSymbols, "ScOutlineWindow::DrawBorderRel - no images" );
562         sal_uInt16 nId = bPressed ? SC_OL_IMAGE_PRESSED : SC_OL_IMAGE_NOTPRESSED;
563         bool bClip = (nEntry != SC_OL_HEADERENTRY);
564         if ( bClip )
565             SetEntryAreaClipRegion();
566         DrawImage( aPos, mpSymbols->GetImage( nId ) );
567         if ( bClip )
568             SetClipRegion();
569     }
570     mbMTPressed = bPressed;
571 }
572 
573 void ScOutlineWindow::ShowFocus()
574 {
575     if ( HasFocus() )
576     {
577         // first move to a visible position
578         ImplMoveFocusToVisible( true );
579 
580         if ( IsFocusButtonVisible() )
581         {
582             Point aPos;
583             if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) )
584             {
585                 aPos += Point( 1, 1 );
586                 maFocusRect = Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) );
587                 bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
588                 if ( bClip )
589                     SetEntryAreaClipRegion();
590                 InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
591                 if ( bClip )
592                     SetClipRegion();
593             }
594         }
595     }
596 }
597 
598 void ScOutlineWindow::HideFocus()
599 {
600     if ( !maFocusRect.IsEmpty() )
601     {
602         bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
603         if ( bClip )
604             SetEntryAreaClipRegion();
605         InvertTracking( maFocusRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
606         if ( bClip )
607             SetClipRegion();
608         maFocusRect.SetEmpty();
609     }
610 }
611 
612 void ScOutlineWindow::Paint( const Rectangle& /* rRect */ )
613 {
614     long nEntriesSign = mbMirrorEntries ? -1 : 1;
615     long nLevelsSign  = mbMirrorLevels  ? -1 : 1;
616 
617     Size aSize = GetOutputSizePixel();
618     long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1;
619     long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1;
620 
621     SetLineColor( maLineColor );
622     long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd;
623     DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd );
624 
625     const ScOutlineArray* pArray = GetOutlineArray();
626     if ( !pArray ) return;
627 
628     size_t nLevelCount = GetLevelCount();
629 
630     // --- draw header images ---
631 
632     if ( mnHeaderSize > 0 )
633     {
634         long nEntryPos = GetHeaderEntryPos();
635         for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel )
636             DrawImageRel( GetLevelPos( nLevel ), nEntryPos, static_cast< sal_uInt16 >( nLevel + 1 ) );
637 
638         SetLineColor( maLineColor );
639         long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1));
640         DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos );
641     }
642 
643     // --- draw lines & collapse/expand images ---
644 
645     SetEntryAreaClipRegion();
646 
647     SCCOLROW nStartIndex, nEndIndex;
648     GetVisibleRange( nStartIndex, nEndIndex );
649 
650     for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel )
651     {
652         long nLevelPos = GetLevelPos( nLevel );
653         long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0;
654 
655         size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
656         size_t nEntry;
657 
658         // first draw all lines in the current level
659         SetLineColor();
660         SetFillColor( maLineColor );
661         for ( nEntry = 0; nEntry < nEntryCount; ++nEntry )
662         {
663             const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
664                                                              sal::static_int_cast<sal_uInt16>(nEntry) );
665             SCCOLROW nStart = pEntry->GetStart();
666             SCCOLROW nEnd = pEntry->GetEnd();
667 
668             // visible range?
669             bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex);
670             // find output coordinates
671             if ( bDraw )
672                 bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
673             // draw, if not collapsed
674             if ( bDraw && !pEntry->IsHidden() )
675             {
676                 if ( nStart >= nStartIndex )
677                     nEntryPos1 += nEntriesSign;
678                 nEntryPos2 -= 2 * nEntriesSign;
679                 long nLinePos = nLevelPos;
680                 if ( mbMirrorLevels )
681                     nLinePos += SC_OL_BITMAPSIZE - 1;   // align with right edge of bitmap
682                 DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 );
683 
684                 if ( nEnd <= nEndIndex )
685                     DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign,
686                                  nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 );
687             }
688         }
689 
690         // draw all images in the level from last to first
691         nEntry = nEntryCount;
692         while ( nEntry )
693         {
694             --nEntry;
695 
696             const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
697                                                              sal::static_int_cast<sal_uInt16>(nEntry) );
698             SCCOLROW nStart = pEntry->GetStart();
699 //            SCCOLROW nEnd = pEntry->GetEnd();
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                 sal_uInt16 nImageId = pEntry->IsHidden() ? SC_OL_IMAGE_PLUS : SC_OL_IMAGE_MINUS;
710                 DrawImageRel( nLevelPos, nImagePos, nImageId );
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. */
725 bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward )
726 {
727     DBG_ASSERT( nMin <= nMax, "lcl_RotateValue - invalid range" );
728     DBG_ASSERT( 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 
753 bool ScOutlineWindow::IsFocusButtonVisible() const
754 {
755     return IsButtonVisible( mnFocusLevel, mnFocusEntry );
756 }
757 
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 
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( sal::static_int_cast<sal_uInt16>(mnFocusLevel),
817                                                          sal::static_int_cast<sal_uInt16>(mnFocusEntry) );
818         if ( pEntry )
819         {
820             SCCOLROW nStart = pEntry->GetStart();
821             SCCOLROW nEnd = pEntry->GetEnd();
822             size_t nNewLevel = mnFocusLevel;
823             size_t nNewEntry = 0;
824 
825             bool bFound = false;
826             if ( bForward && (mnFocusLevel + 2 < nLevelCount) )
827             {
828                 // next level -> find first child entry
829                 nNewLevel = mnFocusLevel + 1;
830                 // TODO - change ScOutlineArray interface to size_t usage
831                 sal_uInt16 nTmpEntry = 0;
832                 bFound = pArray->GetEntryIndexInRange( sal::static_int_cast<sal_uInt16>(nNewLevel), nStart, nEnd, nTmpEntry );
833                 nNewEntry = nTmpEntry;
834             }
835             else if ( !bForward && (mnFocusLevel > 0) )
836             {
837                 // previous level -> find parent entry
838                 nNewLevel = mnFocusLevel - 1;
839                 // TODO - change ScOutlineArray interface to size_t usage
840                 sal_uInt16 nTmpEntry = 0;
841                 bFound = pArray->GetEntryIndex( sal::static_int_cast<sal_uInt16>(nNewLevel), nStart, nTmpEntry );
842                 nNewEntry = nTmpEntry;
843             }
844 
845             if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) )
846             {
847                 mnFocusLevel = nNewLevel;
848                 mnFocusEntry = nNewEntry;
849             }
850         }
851     }
852 
853     return bWrapped;
854 }
855 
856 bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward, bool bFindVisible )
857 {
858     bool bRet = false;
859     size_t nOldLevel = mnFocusLevel;
860     size_t nOldEntry = mnFocusEntry;
861 
862     do
863     {
864         /*  one level up, if backward from left header,
865             or one level down, if forward from right header */
866         if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) )
867             bRet |= ImplMoveFocusByLevel( bForward );
868         // move to next/previous entry
869         bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false );
870         bRet |= bWrapInLevel;
871         /*  one level up, if wrapped backward to right header,
872             or one level down, if wrapped forward to right header */
873         if ( bForward && bWrapInLevel )
874             bRet |= ImplMoveFocusByLevel( bForward );
875     }
876     while ( bFindVisible && !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) );
877 
878     return bRet;
879 }
880 
881 void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward )
882 {
883     // first try to find an entry in the same level
884     if ( !IsFocusButtonVisible() )
885         ImplMoveFocusByEntry( bForward, true );
886     // then try to find any other entry
887     if ( !IsFocusButtonVisible() )
888         ImplMoveFocusByTabOrder( bForward, true );
889 }
890 
891 void ScOutlineWindow::MoveFocusByEntry( bool bForward )
892 {
893     HideFocus();
894     ImplMoveFocusByEntry( bForward, true );
895     ShowFocus();
896 }
897 
898 void ScOutlineWindow::MoveFocusByLevel( bool bForward )
899 {
900     HideFocus();
901     ImplMoveFocusByLevel( bForward );
902     ShowFocus();
903 }
904 
905 void ScOutlineWindow::MoveFocusByTabOrder( bool bForward )
906 {
907     HideFocus();
908     ImplMoveFocusByTabOrder( bForward, true );
909     ShowFocus();
910 }
911 
912 void ScOutlineWindow::GetFocus()
913 {
914     Window::GetFocus();
915     ShowFocus();
916 }
917 
918 void ScOutlineWindow::LoseFocus()
919 {
920     HideFocus();
921     Window::LoseFocus();
922 }
923 
924 
925 // mouse ----------------------------------------------------------------------
926 
927 void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry )
928 {
929     mbMTActive = true;
930     mnMTLevel = nLevel;
931     mnMTEntry = nEntry;
932     DrawBorderRel( nLevel, nEntry, true );
933 }
934 
935 void ScOutlineWindow::EndMouseTracking()
936 {
937     if ( mbMTPressed )
938         DrawBorderRel( mnMTLevel, mnMTEntry, false );
939     mbMTActive = false;
940 }
941 
942 void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt )
943 {
944     if ( IsMouseTracking() )
945 	{
946         size_t nLevel, nEntry;
947         bool bHit = false;
948 
949         if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
950             bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry);
951 
952         if ( bHit != mbMTPressed )
953             DrawBorderRel( mnMTLevel, mnMTEntry, bHit );
954 	}
955 }
956 
957 void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt )
958 {
959     if ( IsMouseTracking() )
960 	{
961         EndMouseTracking();
962 
963         size_t nLevel, nEntry;
964         if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
965             if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) )
966                 DoFunction( nLevel, nEntry );
967 	}
968 }
969 
970 void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt )
971 {
972     size_t nLevel, nEntry;
973     bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry );
974     if ( bHit )
975         StartMouseTracking( nLevel, nEntry );
976     else if ( rMEvt.GetClicks() == 2 )
977 	{
978         bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry );
979         if ( bHit )
980             DoFunction( nLevel, nEntry );
981 	}
982 
983     // if an item has been hit and window is focused, move focus to this item
984     if ( bHit && HasFocus() )
985     {
986         HideFocus();
987         mnFocusLevel = nLevel;
988         mnFocusEntry = nEntry;
989         ShowFocus();
990     }
991 }
992 
993 
994 // keyboard -------------------------------------------------------------------
995 
996 void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt )
997 {
998     const KeyCode& rKCode = rKEvt.GetKeyCode();
999     bool bNoMod = !rKCode.GetModifier();
1000     bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
1001     bool bCtrl = (rKCode.GetModifier() == KEY_MOD1);
1002 
1003     sal_uInt16 nCode = rKCode.GetCode();
1004     bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN);
1005     bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT);
1006 
1007     // TAB key
1008     if ( (nCode == KEY_TAB) && (bNoMod || bShift) )
1009         // move forward without SHIFT key
1010         MoveFocusByTabOrder( bNoMod );      // TAB uses logical order, regardless of mirroring
1011 
1012     // LEFT/RIGHT/UP/DOWN keys
1013     else if ( bNoMod && (bUpDownKey || bLeftRightKey) )
1014     {
1015         bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT);
1016         if ( mbHoriz == bLeftRightKey )
1017             // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
1018             MoveFocusByEntry( bForward != mbMirrorEntries );
1019         else
1020             // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
1021             MoveFocusByLevel( bForward != mbMirrorLevels );
1022     }
1023 
1024     // CTRL + number
1025     else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) )
1026     {
1027         size_t nLevel = static_cast< size_t >( nCode - KEY_1 );
1028         if ( nLevel < GetLevelCount() )
1029             DoFunction( nLevel, SC_OL_HEADERENTRY );
1030     }
1031 
1032     // other key codes
1033     else switch ( rKCode.GetFullCode() )
1034     {
1035         case KEY_ADD:       DoExpand( mnFocusLevel, mnFocusEntry );     break;
1036         case KEY_SUBTRACT:  DoCollapse( mnFocusLevel, mnFocusEntry );   break;
1037         case KEY_SPACE:
1038         case KEY_RETURN:    DoFunction( mnFocusLevel, mnFocusEntry );   break;
1039         default:            Window::KeyInput( rKEvt );
1040     }
1041 }
1042 
1043 
1044 // ============================================================================
1045 
1046