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