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