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