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