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 /* 21 TODO: 22 - delete anchor in SelectionEngine when selecting manually 23 - SelectAll( false ) => only repaint the deselected entries 24 */ 25 26 #include <vcl/treelistbox.hxx> 27 #include <vcl/accessiblefactory.hxx> 28 #include <com/sun/star/accessibility/AccessibleStateType.hpp> 29 #include <vcl/svapp.hxx> 30 #include <vcl/accel.hxx> 31 #include <vcl/settings.hxx> 32 #include <vcl/commandevent.hxx> 33 #include <vcl/uitest/uiobject.hxx> 34 #include <sot/formats.hxx> 35 #include <unotools/accessiblestatesethelper.hxx> 36 #include <rtl/instance.hxx> 37 #include <comphelper/string.hxx> 38 #include <sal/log.hxx> 39 #include <tools/debug.hxx> 40 41 #include <vcl/svlbitm.hxx> 42 #include <vcl/treelistentry.hxx> 43 #include <vcl/viewdataentry.hxx> 44 #include <svimpbox.hxx> 45 46 #include <set> 47 #include <string.h> 48 #include <vector> 49 50 using namespace css::accessibility; 51 52 // Drag&Drop 53 static VclPtr<SvTreeListBox> g_pDDSource; 54 static VclPtr<SvTreeListBox> g_pDDTarget; 55 56 #define SVLBOX_ACC_RETURN 1 57 #define SVLBOX_ACC_ESCAPE 2 58 59 // *************************************************************** 60 61 namespace { 62 63 class MyEdit_Impl : public Edit 64 { 65 SvInplaceEdit2* pOwner; 66 public: 67 MyEdit_Impl( vcl::Window* pParent, SvInplaceEdit2* pOwner ); 68 virtual ~MyEdit_Impl() override { disposeOnce(); } 69 virtual void dispose() override { pOwner = nullptr; Edit::dispose(); } 70 virtual void KeyInput( const KeyEvent& rKEvt ) override; 71 virtual void LoseFocus() override; 72 }; 73 74 } 75 76 MyEdit_Impl::MyEdit_Impl( vcl::Window* pParent, SvInplaceEdit2* _pOwner ) : 77 78 Edit( pParent, WB_LEFT ), 79 80 pOwner( _pOwner ) 81 82 { 83 } 84 85 void MyEdit_Impl::KeyInput( const KeyEvent& rKEvt ) 86 { 87 if( !pOwner->KeyInput( rKEvt )) 88 Edit::KeyInput( rKEvt ); 89 } 90 91 void MyEdit_Impl::LoseFocus() 92 { 93 if (pOwner) 94 pOwner->LoseFocus(); 95 } 96 97 SvInplaceEdit2::SvInplaceEdit2 98 ( 99 vcl::Window* pParent, const Point& rPos, 100 const Size& rSize, 101 const OUString& rData, 102 const Link<SvInplaceEdit2&,void>& rNotifyEditEnd, 103 const Selection& rSelection 104 ) : 105 106 aCallBackHdl ( rNotifyEditEnd ), 107 bCanceled ( false ), 108 bAlreadyInCallBack ( false ) 109 110 { 111 112 pEdit = VclPtr<MyEdit_Impl>::Create( pParent, this ); 113 114 vcl::Font aFont( pParent->GetFont() ); 115 aFont.SetTransparent( false ); 116 Color aColor( pParent->GetBackground().GetColor() ); 117 aFont.SetFillColor(aColor ); 118 pEdit->SetFont( aFont ); 119 pEdit->SetBackground( pParent->GetBackground() ); 120 pEdit->SetPosPixel( rPos ); 121 pEdit->SetSizePixel( rSize ); 122 pEdit->SetText( rData ); 123 pEdit->SetSelection( rSelection ); 124 pEdit->SaveValue(); 125 126 aAccReturn.InsertItem( SVLBOX_ACC_RETURN, vcl::KeyCode(KEY_RETURN) ); 127 aAccEscape.InsertItem( SVLBOX_ACC_ESCAPE, vcl::KeyCode(KEY_ESCAPE) ); 128 129 aAccReturn.SetActivateHdl( LINK( this, SvInplaceEdit2, ReturnHdl_Impl) ); 130 aAccEscape.SetActivateHdl( LINK( this, SvInplaceEdit2, EscapeHdl_Impl) ); 131 Application::InsertAccel( &aAccReturn ); 132 Application::InsertAccel( &aAccEscape ); 133 134 pEdit->Show(); 135 pEdit->GrabFocus(); 136 } 137 138 SvInplaceEdit2::~SvInplaceEdit2() 139 { 140 if( !bAlreadyInCallBack ) 141 { 142 Application::RemoveAccel( &aAccReturn ); 143 Application::RemoveAccel( &aAccEscape ); 144 } 145 pEdit.disposeAndClear(); 146 } 147 148 OUString const & SvInplaceEdit2::GetSavedValue() const 149 { 150 return pEdit->GetSavedValue(); 151 } 152 153 void SvInplaceEdit2::Hide() 154 { 155 pEdit->Hide(); 156 } 157 158 159 IMPL_LINK_NOARG(SvInplaceEdit2, ReturnHdl_Impl, Accelerator&, void) 160 { 161 bCanceled = false; 162 CallCallBackHdl_Impl(); 163 } 164 165 IMPL_LINK_NOARG(SvInplaceEdit2, EscapeHdl_Impl, Accelerator&, void) 166 { 167 bCanceled = true; 168 CallCallBackHdl_Impl(); 169 } 170 171 bool SvInplaceEdit2::KeyInput( const KeyEvent& rKEvt ) 172 { 173 vcl::KeyCode aCode = rKEvt.GetKeyCode(); 174 sal_uInt16 nCode = aCode.GetCode(); 175 176 switch ( nCode ) 177 { 178 case KEY_ESCAPE: 179 bCanceled = true; 180 CallCallBackHdl_Impl(); 181 return true; 182 183 case KEY_RETURN: 184 bCanceled = false; 185 CallCallBackHdl_Impl(); 186 return true; 187 } 188 return false; 189 } 190 191 void SvInplaceEdit2::StopEditing( bool bCancel ) 192 { 193 if ( !bAlreadyInCallBack ) 194 { 195 bCanceled = bCancel; 196 CallCallBackHdl_Impl(); 197 } 198 } 199 200 void SvInplaceEdit2::LoseFocus() 201 { 202 if ( !bAlreadyInCallBack 203 && ((!Application::GetFocusWindow()) || !pEdit->IsChild( Application::GetFocusWindow()) ) 204 ) 205 { 206 bCanceled = false; 207 aIdle.SetPriority(TaskPriority::REPAINT); 208 aIdle.SetInvokeHandler(LINK(this,SvInplaceEdit2,Timeout_Impl)); 209 aIdle.SetDebugName( "svtools::SvInplaceEdit2 aIdle" ); 210 aIdle.Start(); 211 } 212 } 213 214 IMPL_LINK_NOARG(SvInplaceEdit2, Timeout_Impl, Timer *, void) 215 { 216 CallCallBackHdl_Impl(); 217 } 218 219 void SvInplaceEdit2::CallCallBackHdl_Impl() 220 { 221 aIdle.Stop(); 222 if ( !bAlreadyInCallBack ) 223 { 224 bAlreadyInCallBack = true; 225 Application::RemoveAccel( &aAccReturn ); 226 Application::RemoveAccel( &aAccEscape ); 227 pEdit->Hide(); 228 aCallBackHdl.Call( *this ); 229 } 230 } 231 232 OUString SvInplaceEdit2::GetText() const 233 { 234 return pEdit->GetText(); 235 } 236 237 // *************************************************************** 238 // class SvLBoxTab 239 // *************************************************************** 240 241 242 SvLBoxTab::SvLBoxTab() 243 { 244 nPos = 0; 245 nFlags = SvLBoxTabFlags::NONE; 246 } 247 248 SvLBoxTab::SvLBoxTab( long nPosition, SvLBoxTabFlags nTabFlags ) 249 { 250 nPos = nPosition; 251 nFlags = nTabFlags; 252 } 253 254 SvLBoxTab::SvLBoxTab( const SvLBoxTab& rTab ) 255 { 256 nPos = rTab.nPos; 257 nFlags = rTab.nFlags; 258 } 259 260 SvLBoxTab::~SvLBoxTab() 261 { 262 } 263 264 265 long SvLBoxTab::CalcOffset( long nItemWidth, long nTabWidth ) 266 { 267 long nOffset = 0; 268 if ( nFlags & SvLBoxTabFlags::ADJUST_RIGHT ) 269 { 270 nOffset = nTabWidth - nItemWidth; 271 if( nOffset < 0 ) 272 nOffset = 0; 273 } 274 else if ( nFlags & SvLBoxTabFlags::ADJUST_CENTER ) 275 { 276 if( nFlags & SvLBoxTabFlags::FORCE ) 277 { 278 // correct implementation of centering 279 nOffset = ( nTabWidth - nItemWidth ) / 2; 280 if( nOffset < 0 ) 281 nOffset = 0; 282 } 283 else 284 { 285 // historically grown, wrong calculation of tabs which is needed by 286 // Abo-Tabbox, Tools/Options/Customize etc. 287 nItemWidth++; 288 nOffset = -( nItemWidth / 2 ); 289 } 290 } 291 return nOffset; 292 } 293 294 // *************************************************************** 295 // class SvLBoxItem 296 // *************************************************************** 297 298 299 SvLBoxItem::SvLBoxItem() 300 : mbDisabled(false) 301 { 302 } 303 304 SvLBoxItem::~SvLBoxItem() 305 { 306 } 307 308 int SvLBoxItem::GetWidth(const SvTreeListBox* pView, const SvTreeListEntry* pEntry) const 309 { 310 const SvViewDataItem* pViewData = pView->GetViewDataItem( pEntry, this ); 311 int nWidth = pViewData->mnWidth; 312 if (nWidth == -1) 313 { 314 nWidth = CalcWidth(pView); 315 const_cast<SvViewDataItem*>(pViewData)->mnWidth = nWidth; 316 } 317 return nWidth; 318 } 319 320 int SvLBoxItem::GetHeight(const SvTreeListBox* pView, const SvTreeListEntry* pEntry) const 321 { 322 const SvViewDataItem* pViewData = pView->GetViewDataItem( pEntry, this ); 323 return pViewData->mnHeight; 324 } 325 326 int SvLBoxItem::GetWidth(const SvTreeListBox* pView, const SvViewDataEntry* pData, sal_uInt16 nItemPos) 327 { 328 const SvViewDataItem& rIData = pData->GetItem(nItemPos); 329 int nWidth = rIData.mnWidth; 330 if (nWidth == -1) 331 { 332 nWidth = CalcWidth(pView); 333 const_cast<SvViewDataItem&>(rIData).mnWidth = nWidth; 334 } 335 return nWidth; 336 } 337 338 int SvLBoxItem::GetHeight(const SvViewDataEntry* pData, sal_uInt16 nItemPos) 339 { 340 const SvViewDataItem& rIData = pData->GetItem(nItemPos); 341 return rIData.mnHeight; 342 } 343 344 int SvLBoxItem::CalcWidth(const SvTreeListBox* /*pView*/) const 345 { 346 return 0; 347 } 348 349 struct SvTreeListBoxImpl 350 { 351 bool m_bEntryMnemonicsEnabled:1; 352 bool m_bDoingQuickSelection:1; 353 354 vcl::MnemonicEngine m_aMnemonicEngine; 355 vcl::QuickSelectionEngine m_aQuickSelectionEngine; 356 357 explicit SvTreeListBoxImpl(SvTreeListBox& _rBox) : 358 m_bEntryMnemonicsEnabled(false), 359 m_bDoingQuickSelection(false), 360 m_aMnemonicEngine(_rBox), 361 m_aQuickSelectionEngine(_rBox) {} 362 }; 363 364 365 SvTreeListBox::SvTreeListBox(vcl::Window* pParent, WinBits nWinStyle) : 366 Control(pParent, nWinStyle | WB_CLIPCHILDREN), 367 DropTargetHelper(this), 368 DragSourceHelper(this), 369 mpImpl(new SvTreeListBoxImpl(*this)), 370 mbContextBmpExpanded(false), 371 mbAlternatingRowColors(false), 372 mbUpdateAlternatingRows(false), 373 mbQuickSearch(false), 374 eSelMode(SelectionMode::NONE), 375 nMinWidthInChars(0), 376 mnDragAction(DND_ACTION_COPYMOVE | DND_ACTION_LINK), 377 mbCenterAndClipText(false) 378 { 379 nImpFlags = SvTreeListBoxFlags::NONE; 380 pTargetEntry = nullptr; 381 nDragDropMode = DragDropMode::NONE; 382 pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl )); 383 pHdlEntry = nullptr; 384 eSelMode = SelectionMode::Single; 385 nDragDropMode = DragDropMode::NONE; 386 SetType(WindowType::TREELISTBOX); 387 388 InitTreeView(); 389 pImpl->SetModel( pModel.get() ); 390 391 SetSublistOpenWithLeftRight(); 392 } 393 394 void SvTreeListBox::Clear() 395 { 396 if (pModel) 397 pModel->Clear(); // Model calls SvTreeListBox::ModelHasCleared() 398 } 399 400 void SvTreeListBox::EnableEntryMnemonics() 401 { 402 if ( IsEntryMnemonicsEnabled() ) 403 return; 404 405 mpImpl->m_bEntryMnemonicsEnabled = true; 406 Invalidate(); 407 } 408 409 bool SvTreeListBox::IsEntryMnemonicsEnabled() const 410 { 411 return mpImpl->m_bEntryMnemonicsEnabled; 412 } 413 414 IMPL_LINK( SvTreeListBox, CloneHdl_Impl, SvTreeListEntry*, pEntry, SvTreeListEntry* ) 415 { 416 return CloneEntry(pEntry); 417 } 418 419 sal_uLong SvTreeListBox::Insert( SvTreeListEntry* pEntry, SvTreeListEntry* pParent, sal_uLong nPos ) 420 { 421 sal_uLong nInsPos = pModel->Insert( pEntry, pParent, nPos ); 422 pEntry->SetBackColor( GetBackground().GetColor() ); 423 SetAlternatingRowColors( mbAlternatingRowColors ); 424 return nInsPos; 425 } 426 427 sal_uLong SvTreeListBox::Insert( SvTreeListEntry* pEntry,sal_uLong nRootPos ) 428 { 429 sal_uLong nInsPos = pModel->Insert( pEntry, nRootPos ); 430 pEntry->SetBackColor( GetBackground().GetColor() ); 431 SetAlternatingRowColors( mbAlternatingRowColors ); 432 return nInsPos; 433 } 434 435 bool SvTreeListBox::ExpandingHdl() 436 { 437 return !aExpandingHdl.IsSet() || aExpandingHdl.Call( this ); 438 } 439 440 void SvTreeListBox::ExpandedHdl() 441 { 442 aExpandedHdl.Call( this ); 443 } 444 445 void SvTreeListBox::SelectHdl() 446 { 447 aSelectHdl.Call( this ); 448 } 449 450 void SvTreeListBox::DeselectHdl() 451 { 452 aDeselectHdl.Call( this ); 453 } 454 455 bool SvTreeListBox::DoubleClickHdl() 456 { 457 return !aDoubleClickHdl.IsSet() || aDoubleClickHdl.Call(this); 458 } 459 460 461 bool SvTreeListBox::CheckDragAndDropMode( SvTreeListBox const * pSource, sal_Int8 nAction ) 462 { 463 if ( pSource == this ) 464 { 465 if ( !(nDragDropMode & (DragDropMode::CTRL_MOVE | DragDropMode::CTRL_COPY) ) ) 466 return false; // D&D locked within list 467 if( DND_ACTION_MOVE == nAction ) 468 { 469 if ( !(nDragDropMode & DragDropMode::CTRL_MOVE) ) 470 return false; // no local move 471 } 472 else 473 { 474 if ( !(nDragDropMode & DragDropMode::CTRL_COPY)) 475 return false; // no local copy 476 } 477 } 478 else 479 { 480 if ( !(nDragDropMode & DragDropMode::APP_DROP ) ) 481 return false; // no drop 482 if ( DND_ACTION_MOVE == nAction ) 483 { 484 if ( !(nDragDropMode & DragDropMode::APP_MOVE) ) 485 return false; // no global move 486 } 487 else 488 { 489 if ( !(nDragDropMode & DragDropMode::APP_COPY)) 490 return false; // no global copy 491 } 492 } 493 return true; 494 } 495 496 497 /* 498 NotifyMoving/Copying 499 ==================== 500 501 default behavior: 502 503 1. target doesn't have children 504 - entry becomes sibling of target. entry comes after target 505 (->Window: below the target) 506 2. target is an expanded parent 507 - entry inserted at the beginning of the target childlist 508 3. target is a collapsed parent 509 - entry is inserted at the end of the target childlist 510 */ 511 TriState SvTreeListBox::NotifyMoving( 512 SvTreeListEntry* pTarget, // D&D dropping position in GetModel() 513 SvTreeListEntry* pEntry, // entry that we want to move, from 514 // GetSourceListBox()->GetModel() 515 SvTreeListEntry*& rpNewParent, // new target parent 516 sal_uLong& rNewChildPos) // position in childlist of target parent 517 { 518 DBG_ASSERT(pEntry,"NotifyMoving:SourceEntry?"); 519 if( !pTarget ) 520 { 521 rpNewParent = nullptr; 522 rNewChildPos = 0; 523 return TRISTATE_TRUE; 524 } 525 if ( !pTarget->HasChildren() && !pTarget->HasChildrenOnDemand() ) 526 { 527 // case 1 528 rpNewParent = GetParent( pTarget ); 529 rNewChildPos = SvTreeList::GetRelPos( pTarget ) + 1; 530 rNewChildPos += nCurEntrySelPos; 531 nCurEntrySelPos++; 532 } 533 else 534 { 535 // cases 2 & 3 536 rpNewParent = pTarget; 537 if( IsExpanded(pTarget)) 538 rNewChildPos = 0; 539 else 540 rNewChildPos = TREELIST_APPEND; 541 } 542 return TRISTATE_TRUE; 543 } 544 545 TriState SvTreeListBox::NotifyCopying( 546 SvTreeListEntry* pTarget, // D&D dropping position in GetModel() 547 SvTreeListEntry* pEntry, // entry that we want to move, from 548 // GetSourceListBox()->GetModel() 549 SvTreeListEntry*& rpNewParent, // new target parent 550 sal_uLong& rNewChildPos) // position in childlist of target parent 551 { 552 return NotifyMoving(pTarget,pEntry,rpNewParent,rNewChildPos); 553 } 554 555 SvTreeListEntry* SvTreeListBox::FirstChild( SvTreeListEntry* pParent ) const 556 { 557 return pModel->FirstChild(pParent); 558 } 559 560 // return: all entries copied 561 bool SvTreeListBox::CopySelection( SvTreeListBox* pSource, SvTreeListEntry* pTarget ) 562 { 563 nCurEntrySelPos = 0; // selection counter for NotifyMoving/Copying 564 bool bSuccess = true; 565 std::vector<SvTreeListEntry*> aList; 566 bool bClone = ( pSource->GetModel() != GetModel() ); 567 Link<SvTreeListEntry*,SvTreeListEntry*> aCloneLink( pModel->GetCloneLink() ); 568 pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl )); 569 570 // cache selection to simplify iterating over the selection when doing a D&D 571 // exchange within the same listbox 572 SvTreeListEntry* pSourceEntry = pSource->FirstSelected(); 573 while ( pSourceEntry ) 574 { 575 // children are copied automatically 576 pSource->SelectChildren( pSourceEntry, false ); 577 aList.push_back( pSourceEntry ); 578 pSourceEntry = pSource->NextSelected( pSourceEntry ); 579 } 580 581 for (auto const& elem : aList) 582 { 583 pSourceEntry = elem; 584 SvTreeListEntry* pNewParent = nullptr; 585 sal_uLong nInsertionPos = TREELIST_APPEND; 586 TriState nOk = NotifyCopying(pTarget,pSourceEntry,pNewParent,nInsertionPos); 587 if ( nOk ) 588 { 589 if ( bClone ) 590 { 591 sal_uLong nCloneCount = 0; 592 pSourceEntry = pModel->Clone(pSourceEntry, nCloneCount); 593 pModel->InsertTree(pSourceEntry, pNewParent, nInsertionPos); 594 } 595 else 596 { 597 sal_uLong nListPos = pModel->Copy(pSourceEntry, pNewParent, nInsertionPos); 598 pSourceEntry = GetEntry( pNewParent, nListPos ); 599 } 600 } 601 else 602 bSuccess = false; 603 604 if (nOk == TRISTATE_INDET) // HACK: make visible moved entry 605 MakeVisible( pSourceEntry ); 606 } 607 pModel->SetCloneLink( aCloneLink ); 608 return bSuccess; 609 } 610 611 // return: all entries were moved 612 bool SvTreeListBox::MoveSelectionCopyFallbackPossible( SvTreeListBox* pSource, SvTreeListEntry* pTarget, bool bAllowCopyFallback ) 613 { 614 nCurEntrySelPos = 0; // selection counter for NotifyMoving/Copying 615 bool bSuccess = true; 616 std::vector<SvTreeListEntry*> aList; 617 bool bClone = ( pSource->GetModel() != GetModel() ); 618 Link<SvTreeListEntry*,SvTreeListEntry*> aCloneLink( pModel->GetCloneLink() ); 619 if ( bClone ) 620 pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl )); 621 622 SvTreeListEntry* pSourceEntry = pSource->FirstSelected(); 623 while ( pSourceEntry ) 624 { 625 // children are automatically moved 626 pSource->SelectChildren( pSourceEntry, false ); 627 aList.push_back( pSourceEntry ); 628 pSourceEntry = pSource->NextSelected( pSourceEntry ); 629 } 630 631 for (auto const& elem : aList) 632 { 633 pSourceEntry = elem; 634 SvTreeListEntry* pNewParent = nullptr; 635 sal_uLong nInsertionPos = TREELIST_APPEND; 636 TriState nOk = NotifyMoving(pTarget,pSourceEntry,pNewParent,nInsertionPos); 637 TriState nCopyOk = nOk; 638 if ( !nOk && bAllowCopyFallback ) 639 { 640 nInsertionPos = TREELIST_APPEND; 641 nCopyOk = NotifyCopying(pTarget,pSourceEntry,pNewParent,nInsertionPos); 642 } 643 644 if ( nOk || nCopyOk ) 645 { 646 if ( bClone ) 647 { 648 sal_uLong nCloneCount = 0; 649 pSourceEntry = pModel->Clone(pSourceEntry, nCloneCount); 650 pModel->InsertTree(pSourceEntry, pNewParent, nInsertionPos); 651 } 652 else 653 { 654 if ( nOk ) 655 pModel->Move(pSourceEntry, pNewParent, nInsertionPos); 656 else 657 pModel->Copy(pSourceEntry, pNewParent, nInsertionPos); 658 } 659 } 660 else 661 bSuccess = false; 662 663 if (nOk == TRISTATE_INDET) // HACK: make moved entry visible 664 MakeVisible( pSourceEntry ); 665 } 666 pModel->SetCloneLink( aCloneLink ); 667 return bSuccess; 668 } 669 670 void SvTreeListBox::RemoveSelection() 671 { 672 std::vector<const SvTreeListEntry*> aList; 673 // cache selection, as the implementation deselects everything on the first 674 // remove 675 SvTreeListEntry* pEntry = FirstSelected(); 676 while ( pEntry ) 677 { 678 aList.push_back( pEntry ); 679 if ( pEntry->HasChildren() ) 680 // remove deletes all children automatically 681 SelectChildren(pEntry, false); 682 pEntry = NextSelected( pEntry ); 683 } 684 685 for (auto const& elem : aList) 686 pModel->Remove(elem); 687 } 688 689 void SvTreeListBox::RemoveEntry(SvTreeListEntry const * pEntry) 690 { 691 pModel->Remove(pEntry); 692 } 693 694 void SvTreeListBox::RecalcViewData() 695 { 696 SvTreeListEntry* pEntry = First(); 697 while( pEntry ) 698 { 699 sal_uInt16 nCount = pEntry->ItemCount(); 700 sal_uInt16 nCurPos = 0; 701 while ( nCurPos < nCount ) 702 { 703 SvLBoxItem& rItem = pEntry->GetItem( nCurPos ); 704 rItem.InitViewData( this, pEntry ); 705 nCurPos++; 706 } 707 pEntry = Next( pEntry ); 708 } 709 } 710 711 void SvTreeListBox::ImplShowTargetEmphasis( SvTreeListEntry* pEntry, bool bShow) 712 { 713 if ( bShow && (nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) ) 714 return; 715 if ( !bShow && !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) ) 716 return; 717 pImpl->PaintDDCursor( pEntry, bShow); 718 if( bShow ) 719 nImpFlags |= SvTreeListBoxFlags::TARGEMPH_VIS; 720 else 721 nImpFlags &= ~SvTreeListBoxFlags::TARGEMPH_VIS; 722 } 723 724 void SvTreeListBox::OnCurrentEntryChanged() 725 { 726 if ( !mpImpl->m_bDoingQuickSelection ) 727 mpImpl->m_aQuickSelectionEngine.Reset(); 728 } 729 730 SvTreeListEntry* SvTreeListBox::GetEntry( SvTreeListEntry* pParent, sal_uLong nPos ) const 731 { 732 return pModel->GetEntry(pParent, nPos); 733 } 734 735 SvTreeListEntry* SvTreeListBox::GetEntry( sal_uLong nRootPos ) const 736 { 737 return pModel->GetEntry(nRootPos); 738 } 739 740 SvTreeListEntry* SvTreeListBox::GetEntryFromPath( const ::std::deque< sal_Int32 >& _rPath ) const 741 { 742 743 SvTreeListEntry* pEntry = nullptr; 744 SvTreeListEntry* pParent = nullptr; 745 for (auto const& elem : _rPath) 746 { 747 pEntry = GetEntry( pParent, elem ); 748 if ( !pEntry ) 749 break; 750 pParent = pEntry; 751 } 752 753 return pEntry; 754 } 755 756 void SvTreeListBox::FillEntryPath( SvTreeListEntry* pEntry, ::std::deque< sal_Int32 >& _rPath ) const 757 { 758 759 if ( !pEntry ) 760 return; 761 762 SvTreeListEntry* pParentEntry = GetParent( pEntry ); 763 while ( true ) 764 { 765 sal_uLong i, nCount = GetLevelChildCount( pParentEntry ); 766 for ( i = 0; i < nCount; ++i ) 767 { 768 SvTreeListEntry* pTemp = GetEntry( pParentEntry, i ); 769 DBG_ASSERT( pEntry, "invalid entry" ); 770 if ( pEntry == pTemp ) 771 { 772 _rPath.push_front( static_cast<sal_Int32>(i) ); 773 break; 774 } 775 } 776 777 if ( pParentEntry ) 778 { 779 pEntry = pParentEntry; 780 pParentEntry = GetParent( pParentEntry ); 781 } 782 else 783 break; 784 } 785 } 786 787 const SvTreeListEntry* SvTreeListBox::GetParent( const SvTreeListEntry* pEntry ) const 788 { 789 return pModel->GetParent(pEntry); 790 } 791 792 SvTreeListEntry* SvTreeListBox::GetParent( SvTreeListEntry* pEntry ) const 793 { 794 return pModel->GetParent(pEntry); 795 } 796 797 SvTreeListEntry* SvTreeListBox::GetRootLevelParent( SvTreeListEntry* pEntry ) const 798 { 799 return pModel->GetRootLevelParent(pEntry); 800 } 801 802 sal_uLong SvTreeListBox::GetChildCount( SvTreeListEntry const * pParent ) const 803 { 804 return pModel->GetChildCount(pParent); 805 } 806 807 sal_uLong SvTreeListBox::GetLevelChildCount( SvTreeListEntry* _pParent ) const 808 { 809 810 //if _pParent is 0, then pEntry is the first child of the root. 811 SvTreeListEntry* pEntry = FirstChild( _pParent ); 812 813 if( !pEntry )//there is only root, root don't have children 814 return 0; 815 816 if( !_pParent )//root and children of root 817 return pEntry->pParent->m_Children.size(); 818 819 return _pParent->m_Children.size(); 820 } 821 822 SvViewDataEntry* SvTreeListBox::GetViewDataEntry( SvTreeListEntry const * pEntry ) const 823 { 824 return const_cast<SvViewDataEntry*>(SvListView::GetViewData(pEntry)); 825 } 826 827 SvViewDataItem* SvTreeListBox::GetViewDataItem(SvTreeListEntry const * pEntry, SvLBoxItem const * pItem) 828 { 829 return const_cast<SvViewDataItem*>(static_cast<const SvTreeListBox*>(this)->GetViewDataItem(pEntry, pItem)); 830 } 831 832 const SvViewDataItem* SvTreeListBox::GetViewDataItem(const SvTreeListEntry* pEntry, const SvLBoxItem* pItem) const 833 { 834 const SvViewDataEntry* pEntryData = SvListView::GetViewData(pEntry); 835 assert(pEntryData && "Entry not in View"); 836 sal_uInt16 nItemPos = pEntry->GetPos(pItem); 837 return &pEntryData->GetItem(nItemPos); 838 } 839 840 void SvTreeListBox::InitViewData( SvViewDataEntry* pData, SvTreeListEntry* pEntry ) 841 { 842 SvTreeListEntry* pInhEntry = pEntry; 843 SvViewDataEntry* pEntryData = pData; 844 845 pEntryData->Init(pInhEntry->ItemCount()); 846 sal_uInt16 nCount = pInhEntry->ItemCount(); 847 sal_uInt16 nCurPos = 0; 848 while( nCurPos < nCount ) 849 { 850 SvLBoxItem& rItem = pInhEntry->GetItem( nCurPos ); 851 SvViewDataItem& rItemData = pEntryData->GetItem(nCurPos); 852 rItem.InitViewData( this, pInhEntry, &rItemData ); 853 nCurPos++; 854 } 855 } 856 857 void SvTreeListBox::EnableSelectionAsDropTarget( bool bEnable ) 858 { 859 sal_uInt16 nRefDepth; 860 SvTreeListEntry* pTemp; 861 862 SvTreeListEntry* pSelEntry = FirstSelected(); 863 while( pSelEntry ) 864 { 865 if ( !bEnable ) 866 { 867 pSelEntry->nEntryFlags |= SvTLEntryFlags::DISABLE_DROP; 868 nRefDepth = pModel->GetDepth( pSelEntry ); 869 pTemp = Next( pSelEntry ); 870 while( pTemp && pModel->GetDepth( pTemp ) > nRefDepth ) 871 { 872 pTemp->nEntryFlags |= SvTLEntryFlags::DISABLE_DROP; 873 pTemp = Next( pTemp ); 874 } 875 } 876 else 877 { 878 pSelEntry->nEntryFlags &= ~SvTLEntryFlags::DISABLE_DROP; 879 nRefDepth = pModel->GetDepth( pSelEntry ); 880 pTemp = Next( pSelEntry ); 881 while( pTemp && pModel->GetDepth( pTemp ) > nRefDepth ) 882 { 883 pTemp->nEntryFlags &= ~SvTLEntryFlags::DISABLE_DROP; 884 pTemp = Next( pTemp ); 885 } 886 } 887 pSelEntry = NextSelected( pSelEntry ); 888 } 889 } 890 891 // ****************************************************************** 892 // InplaceEditing 893 // ****************************************************************** 894 895 void SvTreeListBox::EditText( const OUString& rStr, const tools::Rectangle& rRect, 896 const Selection& rSel ) 897 { 898 pEdCtrl.reset(); 899 nImpFlags |= SvTreeListBoxFlags::IN_EDT; 900 nImpFlags &= ~SvTreeListBoxFlags::EDTEND_CALLED; 901 HideFocus(); 902 pEdCtrl.reset( new SvInplaceEdit2( 903 this, rRect.TopLeft(), rRect.GetSize(), rStr, 904 LINK( this, SvTreeListBox, TextEditEndedHdl_Impl ), 905 rSel ) ); 906 } 907 908 IMPL_LINK_NOARG(SvTreeListBox, TextEditEndedHdl_Impl, SvInplaceEdit2&, void) 909 { 910 if ( nImpFlags & SvTreeListBoxFlags::EDTEND_CALLED ) // avoid nesting 911 return; 912 nImpFlags |= SvTreeListBoxFlags::EDTEND_CALLED; 913 OUString aStr; 914 if ( !pEdCtrl->EditingCanceled() ) 915 aStr = pEdCtrl->GetText(); 916 else 917 aStr = pEdCtrl->GetSavedValue(); 918 EditedText( aStr ); 919 // Hide may only be called after the new text was put into the entry, so 920 // that we don't call the selection handler in the GetFocus of the listbox 921 // with the old entry text. 922 pEdCtrl->Hide(); 923 nImpFlags &= ~SvTreeListBoxFlags::IN_EDT; 924 GrabFocus(); 925 } 926 927 void SvTreeListBox::CancelTextEditing() 928 { 929 if ( pEdCtrl ) 930 pEdCtrl->StopEditing( true ); 931 nImpFlags &= ~SvTreeListBoxFlags::IN_EDT; 932 } 933 934 void SvTreeListBox::EndEditing( bool bCancel ) 935 { 936 if( pEdCtrl ) 937 pEdCtrl->StopEditing( bCancel ); 938 nImpFlags &= ~SvTreeListBoxFlags::IN_EDT; 939 } 940 941 942 const void* SvTreeListBox::FirstSearchEntry( OUString& _rEntryText ) const 943 { 944 SvTreeListEntry* pEntry = GetCurEntry(); 945 if ( pEntry ) 946 pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( NextSearchEntry( pEntry, _rEntryText ) ) ); 947 else 948 { 949 pEntry = FirstSelected(); 950 if ( !pEntry ) 951 pEntry = First(); 952 } 953 954 if ( pEntry ) 955 _rEntryText = GetEntryText( pEntry ); 956 957 return pEntry; 958 } 959 960 const void* SvTreeListBox::NextSearchEntry( const void* _pCurrentSearchEntry, OUString& _rEntryText ) const 961 { 962 SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( _pCurrentSearchEntry ) ); 963 964 if ( ( ( GetChildCount( pEntry ) > 0 ) 965 || ( pEntry->HasChildrenOnDemand() ) 966 ) 967 && !IsExpanded( pEntry ) 968 ) 969 { 970 pEntry = pEntry->NextSibling(); 971 } 972 else 973 { 974 pEntry = Next( pEntry ); 975 } 976 977 if ( !pEntry ) 978 pEntry = First(); 979 980 if ( pEntry ) 981 _rEntryText = GetEntryText( pEntry ); 982 983 return pEntry; 984 } 985 986 void SvTreeListBox::SelectSearchEntry( const void* _pEntry ) 987 { 988 SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( _pEntry ) ); 989 DBG_ASSERT( pEntry, "SvTreeListBox::SelectSearchEntry: invalid entry!" ); 990 if ( !pEntry ) 991 return; 992 993 SelectAll( false ); 994 SetCurEntry( pEntry ); 995 Select( pEntry ); 996 } 997 998 void SvTreeListBox::ExecuteSearchEntry( const void* /*_pEntry*/ ) const 999 { 1000 // nothing to do here, we have no "execution" 1001 } 1002 1003 vcl::StringEntryIdentifier SvTreeListBox::CurrentEntry( OUString& _out_entryText ) const 1004 { 1005 // always accept the current entry if there is one 1006 SvTreeListEntry* pCurrentEntry( GetCurEntry() ); 1007 if ( pCurrentEntry ) 1008 { 1009 _out_entryText = GetEntryText( pCurrentEntry ); 1010 return pCurrentEntry; 1011 } 1012 return FirstSearchEntry( _out_entryText ); 1013 } 1014 1015 vcl::StringEntryIdentifier SvTreeListBox::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const 1016 { 1017 return NextSearchEntry( _currentEntry, _out_entryText ); 1018 } 1019 1020 void SvTreeListBox::SelectEntry( vcl::StringEntryIdentifier _entry ) 1021 { 1022 SelectSearchEntry( _entry ); 1023 } 1024 1025 bool SvTreeListBox::HandleKeyInput( const KeyEvent& _rKEvt ) 1026 { 1027 if ( _rKEvt.GetKeyCode().IsMod1() ) 1028 return false; 1029 1030 if ( IsEntryMnemonicsEnabled() 1031 && mpImpl->m_aMnemonicEngine.HandleKeyEvent( _rKEvt ) 1032 ) 1033 return true; 1034 1035 if (mbQuickSearch) 1036 { 1037 mpImpl->m_bDoingQuickSelection = true; 1038 const bool bHandled = mpImpl->m_aQuickSelectionEngine.HandleKeyEvent( _rKEvt ); 1039 mpImpl->m_bDoingQuickSelection = false; 1040 if ( bHandled ) 1041 return true; 1042 } 1043 1044 return false; 1045 } 1046 1047 bool SvTreeListBox::EditingCanceled() const 1048 { 1049 return pEdCtrl && pEdCtrl->EditingCanceled(); 1050 } 1051 1052 1053 //JP 28.3.2001: new Drag & Drop API 1054 sal_Int8 SvTreeListBox::AcceptDrop( const AcceptDropEvent& rEvt ) 1055 { 1056 sal_Int8 nRet = DND_ACTION_NONE; 1057 1058 if (rEvt.mbLeaving || !CheckDragAndDropMode(g_pDDSource, rEvt.mnAction)) 1059 { 1060 ImplShowTargetEmphasis( pTargetEntry, false ); 1061 } 1062 else if( nDragDropMode == DragDropMode::NONE ) 1063 { 1064 SAL_WARN( "svtools.contnr", "SvTreeListBox::QueryDrop(): no target" ); 1065 } 1066 else 1067 { 1068 SvTreeListEntry* pEntry = GetDropTarget( rEvt.maPosPixel ); 1069 if( !IsDropFormatSupported( SotClipboardFormatId::TREELISTBOX ) ) 1070 { 1071 SAL_WARN( "svtools.contnr", "SvTreeListBox::QueryDrop(): no format" ); 1072 } 1073 else 1074 { 1075 DBG_ASSERT(g_pDDSource, "SvTreeListBox::QueryDrop(): SourceBox == 0"); 1076 if (!( pEntry && g_pDDSource->GetModel() == GetModel() 1077 && DND_ACTION_MOVE == rEvt.mnAction 1078 && (pEntry->nEntryFlags & SvTLEntryFlags::DISABLE_DROP))) 1079 { 1080 if( NotifyAcceptDrop( pEntry )) 1081 nRet = rEvt.mnAction; 1082 } 1083 } 1084 1085 // **** draw emphasis **** 1086 if( DND_ACTION_NONE == nRet ) 1087 ImplShowTargetEmphasis( pTargetEntry, false ); 1088 else if( pEntry != pTargetEntry || !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) ) 1089 { 1090 ImplShowTargetEmphasis( pTargetEntry, false ); 1091 pTargetEntry = pEntry; 1092 ImplShowTargetEmphasis( pTargetEntry, true ); 1093 } 1094 } 1095 return nRet; 1096 } 1097 1098 sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt, SvTreeListBox* pSourceView ) 1099 { 1100 assert(pSourceView); 1101 pSourceView->EnableSelectionAsDropTarget(); 1102 1103 ImplShowTargetEmphasis( pTargetEntry, false ); 1104 g_pDDTarget = this; 1105 1106 TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); 1107 1108 sal_Int8 nRet; 1109 if( aData.HasFormat( SotClipboardFormatId::TREELISTBOX )) 1110 nRet = rEvt.mnAction; 1111 else 1112 nRet = DND_ACTION_NONE; 1113 1114 if( DND_ACTION_NONE != nRet ) 1115 { 1116 nRet = DND_ACTION_NONE; 1117 1118 SvTreeListEntry* pTarget = pTargetEntry; // may be 0! 1119 1120 if( DND_ACTION_COPY == rEvt.mnAction ) 1121 { 1122 if (CopySelection(g_pDDSource, pTarget)) 1123 nRet = rEvt.mnAction; 1124 } 1125 else if( DND_ACTION_MOVE == rEvt.mnAction ) 1126 { 1127 if (MoveSelectionCopyFallbackPossible( g_pDDSource, pTarget, false )) 1128 nRet = rEvt.mnAction; 1129 } 1130 else if( DND_ACTION_COPYMOVE == rEvt.mnAction ) 1131 { 1132 if (MoveSelectionCopyFallbackPossible(g_pDDSource, pTarget, true)) 1133 nRet = rEvt.mnAction; 1134 } 1135 } 1136 return nRet; 1137 } 1138 1139 sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt ) 1140 { 1141 return ExecuteDrop( rEvt, g_pDDSource ); 1142 } 1143 1144 /** 1145 * This sets the global variables used to determine the 1146 * in-process drag source. 1147 */ 1148 void SvTreeListBox::SetupDragOrigin() 1149 { 1150 g_pDDSource = this; 1151 g_pDDTarget = nullptr; 1152 } 1153 1154 void SvTreeListBox::StartDrag( sal_Int8, const Point& rPosPixel ) 1155 { 1156 Point aEventPos( rPosPixel ); 1157 MouseEvent aMouseEvt( aEventPos, 1, MouseEventModifiers::SELECT, MOUSE_LEFT ); 1158 MouseButtonUp( aMouseEvt ); 1159 1160 nOldDragMode = GetDragDropMode(); 1161 if ( nOldDragMode == DragDropMode::NONE ) 1162 return; 1163 1164 ReleaseMouse(); 1165 1166 SvTreeListEntry* pEntry = GetEntry( rPosPixel ); // GetDropTarget( rPos ); 1167 if( !pEntry ) 1168 { 1169 DragFinished( DND_ACTION_NONE ); 1170 return; 1171 } 1172 1173 rtl::Reference<TransferDataContainer> xContainer = m_xTransferHelper; 1174 1175 if (!xContainer) 1176 { 1177 xContainer.set(new TransferDataContainer); 1178 // apparently some (unused) content is needed 1179 xContainer->CopyAnyData( SotClipboardFormatId::TREELISTBOX, 1180 "unused", SAL_N_ELEMENTS("unused") ); 1181 } 1182 1183 nDragDropMode = NotifyStartDrag( *xContainer, pEntry ); 1184 if( nDragDropMode == DragDropMode::NONE || 0 == GetSelectionCount() ) 1185 { 1186 nDragDropMode = nOldDragMode; 1187 DragFinished( DND_ACTION_NONE ); 1188 return; 1189 } 1190 1191 SetupDragOrigin(); 1192 1193 bool bOldUpdateMode = Control::IsUpdateMode(); 1194 Control::SetUpdateMode( true ); 1195 Update(); 1196 Control::SetUpdateMode( bOldUpdateMode ); 1197 1198 // Disallow using the selection and its children as drop targets. 1199 // Important: If the selection of the SourceListBox is changed in the 1200 // DropHandler, the entries have to be allowed as drop targets again: 1201 // (GetSourceListBox()->EnableSelectionAsDropTarget( true, true );) 1202 EnableSelectionAsDropTarget( false ); 1203 1204 xContainer->StartDrag(this, mnDragAction, GetDragFinishedHdl()); 1205 } 1206 1207 void SvTreeListBox::SetDragHelper(rtl::Reference<TransferDataContainer>& rHelper, sal_uInt8 eDNDConstants) 1208 { 1209 m_xTransferHelper = rHelper; 1210 mnDragAction = eDNDConstants; 1211 } 1212 1213 void SvTreeListBox::DragFinished( sal_Int8 1214 #ifndef UNX 1215 nAction 1216 #endif 1217 ) 1218 { 1219 EnableSelectionAsDropTarget(); 1220 1221 #ifndef UNX 1222 if ( (nAction == DND_ACTION_MOVE) 1223 && ( (g_pDDTarget && (g_pDDTarget->GetModel() != GetModel())) 1224 || !g_pDDTarget)) 1225 { 1226 RemoveSelection(); 1227 } 1228 #endif 1229 1230 ImplShowTargetEmphasis( pTargetEntry, false ); 1231 g_pDDSource = nullptr; 1232 g_pDDTarget = nullptr; 1233 pTargetEntry = nullptr; 1234 nDragDropMode = nOldDragMode; 1235 } 1236 1237 DragDropMode SvTreeListBox::NotifyStartDrag( TransferDataContainer&, SvTreeListEntry* ) 1238 { 1239 return DragDropMode(0xffff); 1240 } 1241 1242 bool SvTreeListBox::NotifyAcceptDrop( SvTreeListEntry* ) 1243 { 1244 return true; 1245 } 1246 1247 // Handler and methods for Drag - finished handler. 1248 // The with get GetDragFinishedHdl() get link can set on the 1249 // TransferDataContainer. This link is a callback for the DragFinished 1250 // call. AddBox method is called from the GetDragFinishedHdl() and the 1251 // remove is called in link callback and in the destructor. So it can't 1252 // called to a deleted object. 1253 1254 namespace 1255 { 1256 struct SortLBoxes : public rtl::Static<std::set<sal_uLong>, SortLBoxes> {}; 1257 } 1258 1259 void SvTreeListBox::AddBoxToDDList_Impl( const SvTreeListBox& rB ) 1260 { 1261 sal_uLong nVal = reinterpret_cast<sal_uLong>(&rB); 1262 SortLBoxes::get().insert( nVal ); 1263 } 1264 1265 void SvTreeListBox::RemoveBoxFromDDList_Impl( const SvTreeListBox& rB ) 1266 { 1267 sal_uLong nVal = reinterpret_cast<sal_uLong>(&rB); 1268 SortLBoxes::get().erase( nVal ); 1269 } 1270 1271 IMPL_LINK( SvTreeListBox, DragFinishHdl_Impl, sal_Int8, nAction, void ) 1272 { 1273 sal_uLong nVal = reinterpret_cast<sal_uLong>(this); 1274 std::set<sal_uLong> &rSortLBoxes = SortLBoxes::get(); 1275 std::set<sal_uLong>::const_iterator it = rSortLBoxes.find(nVal); 1276 if( it != rSortLBoxes.end() ) 1277 { 1278 DragFinished( nAction ); 1279 rSortLBoxes.erase( it ); 1280 } 1281 } 1282 1283 Link<sal_Int8,void> SvTreeListBox::GetDragFinishedHdl() const 1284 { 1285 AddBoxToDDList_Impl( *this ); 1286 return LINK( const_cast<SvTreeListBox*>(this), SvTreeListBox, DragFinishHdl_Impl ); 1287 } 1288 1289 /* 1290 Bugs/TODO 1291 1292 - calculate rectangle when editing in-place (bug with some fonts) 1293 - SetSpaceBetweenEntries: offset is not taken into account in SetEntryHeight 1294 */ 1295 1296 #define SV_LBOX_DEFAULT_INDENT_PIXEL 20 1297 1298 void SvTreeListBox::InitTreeView() 1299 { 1300 pCheckButtonData = nullptr; 1301 pEdEntry = nullptr; 1302 pEdItem = nullptr; 1303 nEntryHeight = 0; 1304 pEdCtrl = nullptr; 1305 nFirstSelTab = 0; 1306 nLastSelTab = 0; 1307 nFocusWidth = -1; 1308 mnCheckboxItemWidth = 0; 1309 1310 nTreeFlags = SvTreeFlags::RECALCTABS; 1311 nIndent = SV_LBOX_DEFAULT_INDENT_PIXEL; 1312 nEntryHeightOffs = SV_ENTRYHEIGHTOFFS_PIXEL; 1313 pImpl.reset( new SvImpLBox( this, GetModel(), GetStyle() ) ); 1314 1315 mbContextBmpExpanded = true; 1316 nContextBmpWidthMax = 0; 1317 1318 SetFont( GetFont() ); 1319 AdjustEntryHeightAndRecalc(); 1320 1321 SetSpaceBetweenEntries( 0 ); 1322 SetLineColor(); 1323 InitSettings(); 1324 ImplInitStyle(); 1325 SetTabs(); 1326 } 1327 1328 OUString SvTreeListBox::GetEntryAltText( SvTreeListEntry* ) const 1329 { 1330 return OUString(); 1331 } 1332 1333 OUString SvTreeListBox::GetEntryLongDescription( SvTreeListEntry* ) const 1334 { 1335 return OUString(); 1336 } 1337 1338 OUString SvTreeListBox::SearchEntryTextWithHeadTitle( SvTreeListEntry* pEntry ) 1339 { 1340 assert(pEntry); 1341 OUStringBuffer sRet; 1342 1343 sal_uInt16 nCount = pEntry->ItemCount(); 1344 sal_uInt16 nCur = 0; 1345 while( nCur < nCount ) 1346 { 1347 SvLBoxItem& rItem = pEntry->GetItem( nCur ); 1348 if ( (rItem.GetType() == SvLBoxItemType::String) && 1349 !static_cast<SvLBoxString&>( rItem ).GetText().isEmpty() ) 1350 { 1351 sRet.append(static_cast<SvLBoxString&>( rItem ).GetText()).append(","); 1352 } 1353 nCur++; 1354 } 1355 1356 if (!sRet.isEmpty()) 1357 sRet = sRet.copy(0, sRet.getLength() - 1); 1358 return sRet.makeStringAndClear(); 1359 } 1360 1361 SvTreeListBox::~SvTreeListBox() 1362 { 1363 disposeOnce(); 1364 } 1365 1366 void SvTreeListBox::dispose() 1367 { 1368 if( pImpl ) 1369 { 1370 pImpl->CallEventListeners( VclEventId::ObjectDying ); 1371 pImpl.reset(); 1372 } 1373 if( mpImpl ) 1374 { 1375 ClearTabList(); 1376 1377 pEdCtrl.reset(); 1378 1379 SvListView::dispose(); 1380 1381 SvTreeListBox::RemoveBoxFromDDList_Impl( *this ); 1382 1383 if (this == g_pDDSource) 1384 g_pDDSource = nullptr; 1385 if (this == g_pDDTarget) 1386 g_pDDTarget = nullptr; 1387 mpImpl.reset(); 1388 } 1389 1390 DropTargetHelper::dispose(); 1391 DragSourceHelper::dispose(); 1392 Control::dispose(); 1393 } 1394 1395 void SvTreeListBox::SetNoAutoCurEntry( bool b ) 1396 { 1397 pImpl->SetNoAutoCurEntry( b ); 1398 } 1399 1400 void SvTreeListBox::SetSublistOpenWithReturn() 1401 { 1402 pImpl->m_bSubLstOpRet = true; 1403 } 1404 1405 void SvTreeListBox::SetSublistOpenWithLeftRight() 1406 { 1407 pImpl->m_bSubLstOpLR = true; 1408 } 1409 1410 void SvTreeListBox::SetSublistDontOpenWithDoubleClick(bool bDontOpen) 1411 { 1412 pImpl->m_bSubLstOpDblClick = !bDontOpen; 1413 } 1414 1415 void SvTreeListBox::Resize() 1416 { 1417 if( IsEditingActive() ) 1418 EndEditing( true ); 1419 1420 Control::Resize(); 1421 1422 pImpl->Resize(); 1423 nFocusWidth = -1; 1424 pImpl->ShowCursor( false ); 1425 pImpl->ShowCursor( true ); 1426 } 1427 1428 /* Cases: 1429 1430 A) entries have bitmaps 1431 0. no buttons 1432 1. node buttons (can optionally also be on root items) 1433 2. node buttons (can optionally also be on root items) + CheckButton 1434 3. CheckButton 1435 B) entries don't have bitmaps (=>via WindowBits because of D&D!) 1436 0. no buttons 1437 1. node buttons (can optionally also be on root items) 1438 2. node buttons (can optionally also be on root items) + CheckButton 1439 3. CheckButton 1440 */ 1441 1442 #define NO_BUTTONS 0 1443 #define NODE_BUTTONS 1 1444 #define NODE_AND_CHECK_BUTTONS 2 1445 #define CHECK_BUTTONS 3 1446 1447 #define TABFLAGS_TEXT (SvLBoxTabFlags::DYNAMIC | \ 1448 SvLBoxTabFlags::ADJUST_LEFT | \ 1449 SvLBoxTabFlags::EDITABLE | \ 1450 SvLBoxTabFlags::SHOW_SELECTION) 1451 1452 #define TABFLAGS_CONTEXTBMP (SvLBoxTabFlags::DYNAMIC | SvLBoxTabFlags::ADJUST_CENTER) 1453 1454 #define TABFLAGS_CHECKBTN (SvLBoxTabFlags::DYNAMIC | \ 1455 SvLBoxTabFlags::ADJUST_CENTER) 1456 1457 #define TAB_STARTPOS 2 1458 1459 // take care of GetTextOffset when doing changes 1460 void SvTreeListBox::SetTabs() 1461 { 1462 if( IsEditingActive() ) 1463 EndEditing( true ); 1464 nTreeFlags &= ~SvTreeFlags::RECALCTABS; 1465 nFocusWidth = -1; 1466 const WinBits nStyle( GetStyle() ); 1467 bool bHasButtons = (nStyle & WB_HASBUTTONS)!=0; 1468 bool bHasButtonsAtRoot = (nStyle & (WB_HASLINESATROOT | 1469 WB_HASBUTTONSATROOT))!=0; 1470 long nStartPos = TAB_STARTPOS; 1471 long nNodeWidthPixel = GetExpandedNodeBmp().GetSizePixel().Width(); 1472 1473 // pCheckButtonData->Width() knows nothing about the native checkbox width, 1474 // so we have mnCheckboxItemWidth which becomes valid when something is added. 1475 long nCheckWidth = 0; 1476 if( nTreeFlags & SvTreeFlags::CHKBTN ) 1477 nCheckWidth = mnCheckboxItemWidth; 1478 long nCheckWidthDIV2 = nCheckWidth / 2; 1479 1480 long nContextWidth = nContextBmpWidthMax; 1481 long nContextWidthDIV2 = nContextWidth / 2; 1482 1483 ClearTabList(); 1484 1485 int nCase = NO_BUTTONS; 1486 if( !(nTreeFlags & SvTreeFlags::CHKBTN) ) 1487 { 1488 if( bHasButtons ) 1489 nCase = NODE_BUTTONS; 1490 } 1491 else 1492 { 1493 if( bHasButtons ) 1494 nCase = NODE_AND_CHECK_BUTTONS; 1495 else 1496 nCase = CHECK_BUTTONS; 1497 } 1498 1499 switch( nCase ) 1500 { 1501 case NO_BUTTONS : 1502 nStartPos += nContextWidthDIV2; // because of centering 1503 AddTab( nStartPos, TABFLAGS_CONTEXTBMP ); 1504 nStartPos += nContextWidthDIV2; // right edge of context bitmap 1505 // only set a distance if there are bitmaps 1506 if( nContextBmpWidthMax ) 1507 nStartPos += 5; // distance context bitmap to text 1508 AddTab( nStartPos, TABFLAGS_TEXT ); 1509 break; 1510 1511 case NODE_BUTTONS : 1512 if( bHasButtonsAtRoot ) 1513 nStartPos += ( nIndent + (nNodeWidthPixel/2) ); 1514 else 1515 nStartPos += nContextWidthDIV2; 1516 AddTab( nStartPos, TABFLAGS_CONTEXTBMP ); 1517 nStartPos += nContextWidthDIV2; // right edge of context bitmap 1518 // only set a distance if there are bitmaps 1519 if( nContextBmpWidthMax ) 1520 nStartPos += 5; // distance context bitmap to text 1521 AddTab( nStartPos, TABFLAGS_TEXT ); 1522 break; 1523 1524 case NODE_AND_CHECK_BUTTONS : 1525 if( bHasButtonsAtRoot ) 1526 nStartPos += ( nIndent + nNodeWidthPixel ); 1527 else 1528 nStartPos += nCheckWidthDIV2; 1529 AddTab( nStartPos, TABFLAGS_CHECKBTN ); 1530 nStartPos += nCheckWidthDIV2; // right edge of CheckButton 1531 nStartPos += 3; // distance CheckButton to context bitmap 1532 nStartPos += nContextWidthDIV2; // center of context bitmap 1533 AddTab( nStartPos, TABFLAGS_CONTEXTBMP ); 1534 nStartPos += nContextWidthDIV2; // right edge of context bitmap 1535 // only set a distance if there are bitmaps 1536 if( nContextBmpWidthMax ) 1537 nStartPos += 5; // distance context bitmap to text 1538 AddTab( nStartPos, TABFLAGS_TEXT ); 1539 break; 1540 1541 case CHECK_BUTTONS : 1542 nStartPos += nCheckWidthDIV2; 1543 AddTab( nStartPos, TABFLAGS_CHECKBTN ); 1544 nStartPos += nCheckWidthDIV2; // right edge of CheckButton 1545 nStartPos += 3; // distance CheckButton to context bitmap 1546 nStartPos += nContextWidthDIV2; // center of context bitmap 1547 AddTab( nStartPos, TABFLAGS_CONTEXTBMP ); 1548 nStartPos += nContextWidthDIV2; // right edge of context bitmap 1549 // only set a distance if there are bitmaps 1550 if( nContextBmpWidthMax ) 1551 nStartPos += 5; // distance context bitmap to text 1552 AddTab( nStartPos, TABFLAGS_TEXT ); 1553 break; 1554 } 1555 pImpl->NotifyTabsChanged(); 1556 } 1557 1558 void SvTreeListBox::InitEntry(SvTreeListEntry* pEntry, 1559 const OUString& aStr, const Image& aCollEntryBmp, const Image& aExpEntryBmp) 1560 { 1561 if( nTreeFlags & SvTreeFlags::CHKBTN ) 1562 { 1563 pEntry->AddItem(std::make_unique<SvLBoxButton>(pCheckButtonData)); 1564 } 1565 1566 pEntry->AddItem(std::make_unique<SvLBoxContextBmp>( aCollEntryBmp,aExpEntryBmp, mbContextBmpExpanded)); 1567 1568 pEntry->AddItem(std::make_unique<SvLBoxString>(aStr)); 1569 } 1570 1571 OUString SvTreeListBox::GetEntryText(SvTreeListEntry* pEntry) const 1572 { 1573 assert(pEntry); 1574 SvLBoxString* pItem = static_cast<SvLBoxString*>(pEntry->GetFirstItem(SvLBoxItemType::String)); 1575 assert(pItem); 1576 return pItem->GetText(); 1577 } 1578 1579 const Image& SvTreeListBox::GetExpandedEntryBmp(const SvTreeListEntry* pEntry) 1580 { 1581 assert(pEntry); 1582 const SvLBoxContextBmp* pItem = static_cast<const SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp)); 1583 assert(pItem); 1584 return pItem->GetBitmap2( ); 1585 } 1586 1587 const Image& SvTreeListBox::GetCollapsedEntryBmp( const SvTreeListEntry* pEntry ) 1588 { 1589 assert(pEntry); 1590 const SvLBoxContextBmp* pItem = static_cast<const SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp)); 1591 assert(pItem); 1592 return pItem->GetBitmap1( ); 1593 } 1594 1595 IMPL_LINK( SvTreeListBox, CheckButtonClick, SvLBoxButtonData *, pData, void ) 1596 { 1597 pHdlEntry = pData->GetActEntry(); 1598 CheckButtonHdl(); 1599 } 1600 1601 SvTreeListEntry* SvTreeListBox::InsertEntry( 1602 const OUString& rText, 1603 SvTreeListEntry* pParent, 1604 bool bChildrenOnDemand, sal_uLong nPos, 1605 void* pUser 1606 ) 1607 { 1608 nTreeFlags |= SvTreeFlags::MANINS; 1609 1610 const Image& rDefExpBmp = pImpl->GetDefaultEntryExpBmp( ); 1611 const Image& rDefColBmp = pImpl->GetDefaultEntryColBmp( ); 1612 1613 aCurInsertedExpBmp = rDefExpBmp; 1614 aCurInsertedColBmp = rDefColBmp; 1615 1616 SvTreeListEntry* pEntry = new SvTreeListEntry; 1617 pEntry->SetUserData( pUser ); 1618 InitEntry( pEntry, rText, rDefColBmp, rDefExpBmp ); 1619 pEntry->EnableChildrenOnDemand( bChildrenOnDemand ); 1620 1621 if( !pParent ) 1622 Insert( pEntry, nPos ); 1623 else 1624 Insert( pEntry, pParent, nPos ); 1625 1626 aPrevInsertedExpBmp = rDefExpBmp; 1627 aPrevInsertedColBmp = rDefColBmp; 1628 1629 nTreeFlags &= ~SvTreeFlags::MANINS; 1630 1631 return pEntry; 1632 } 1633 1634 SvTreeListEntry* SvTreeListBox::InsertEntry( const OUString& rText, 1635 const Image& aExpEntryBmp, const Image& aCollEntryBmp, 1636 SvTreeListEntry* pParent, bool bChildrenOnDemand, sal_uLong nPos, void* pUser ) 1637 { 1638 nTreeFlags |= SvTreeFlags::MANINS; 1639 1640 aCurInsertedExpBmp = aExpEntryBmp; 1641 aCurInsertedColBmp = aCollEntryBmp; 1642 1643 SvTreeListEntry* pEntry = new SvTreeListEntry; 1644 pEntry->SetUserData( pUser ); 1645 InitEntry( pEntry, rText, aCollEntryBmp, aExpEntryBmp ); 1646 1647 pEntry->EnableChildrenOnDemand( bChildrenOnDemand ); 1648 1649 if( !pParent ) 1650 Insert( pEntry, nPos ); 1651 else 1652 Insert( pEntry, pParent, nPos ); 1653 1654 aPrevInsertedExpBmp = aExpEntryBmp; 1655 aPrevInsertedColBmp = aCollEntryBmp; 1656 1657 nTreeFlags &= ~SvTreeFlags::MANINS; 1658 1659 return pEntry; 1660 } 1661 1662 void SvTreeListBox::SetEntryText(SvTreeListEntry* pEntry, const OUString& rStr) 1663 { 1664 SvLBoxString* pItem = static_cast<SvLBoxString*>(pEntry->GetFirstItem(SvLBoxItemType::String)); 1665 assert(pItem); 1666 pItem->SetText(rStr); 1667 pItem->InitViewData( this, pEntry ); 1668 GetModel()->InvalidateEntry( pEntry ); 1669 } 1670 1671 void SvTreeListBox::SetExpandedEntryBmp( SvTreeListEntry* pEntry, const Image& aBmp ) 1672 { 1673 SvLBoxContextBmp* pItem = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp)); 1674 1675 assert(pItem); 1676 pItem->SetBitmap2( aBmp ); 1677 1678 GetModel()->InvalidateEntry( pEntry ); 1679 SetEntryHeight( pEntry ); 1680 Size aSize = aBmp.GetSizePixel(); 1681 short nWidth = pImpl->UpdateContextBmpWidthVector( pEntry, static_cast<short>(aSize.Width()) ); 1682 if( nWidth > nContextBmpWidthMax ) 1683 { 1684 nContextBmpWidthMax = nWidth; 1685 SetTabs(); 1686 } 1687 } 1688 1689 void SvTreeListBox::SetCollapsedEntryBmp(SvTreeListEntry* pEntry,const Image& aBmp ) 1690 { 1691 SvLBoxContextBmp* pItem = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp)); 1692 1693 assert(pItem); 1694 pItem->SetBitmap1( aBmp ); 1695 1696 GetModel()->InvalidateEntry( pEntry ); 1697 SetEntryHeight( pEntry ); 1698 Size aSize = aBmp.GetSizePixel(); 1699 short nWidth = pImpl->UpdateContextBmpWidthVector( pEntry, static_cast<short>(aSize.Width()) ); 1700 if( nWidth > nContextBmpWidthMax ) 1701 { 1702 nContextBmpWidthMax = nWidth; 1703 SetTabs(); 1704 } 1705 } 1706 1707 void SvTreeListBox::CheckBoxInserted(SvTreeListEntry* pEntry) 1708 { 1709 SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button)); 1710 if( pItem ) 1711 { 1712 auto nWidth = pItem->GetWidth(this, pEntry); 1713 if( mnCheckboxItemWidth < nWidth ) 1714 { 1715 mnCheckboxItemWidth = nWidth; 1716 nTreeFlags |= SvTreeFlags::RECALCTABS; 1717 } 1718 } 1719 } 1720 1721 void SvTreeListBox::ImpEntryInserted( SvTreeListEntry* pEntry ) 1722 { 1723 1724 SvTreeListEntry* pParent = pModel->GetParent( pEntry ); 1725 if( pParent ) 1726 { 1727 SvTLEntryFlags nFlags = pParent->GetFlags(); 1728 nFlags &= ~SvTLEntryFlags::NO_NODEBMP; 1729 pParent->SetFlags( nFlags ); 1730 } 1731 1732 if(!((nTreeFlags & SvTreeFlags::MANINS) && 1733 (aPrevInsertedExpBmp == aCurInsertedExpBmp) && 1734 (aPrevInsertedColBmp == aCurInsertedColBmp) )) 1735 { 1736 Size aSize = GetCollapsedEntryBmp( pEntry ).GetSizePixel(); 1737 if( aSize.Width() > nContextBmpWidthMax ) 1738 { 1739 nContextBmpWidthMax = static_cast<short>(aSize.Width()); 1740 nTreeFlags |= SvTreeFlags::RECALCTABS; 1741 } 1742 aSize = GetExpandedEntryBmp( pEntry ).GetSizePixel(); 1743 if( aSize.Width() > nContextBmpWidthMax ) 1744 { 1745 nContextBmpWidthMax = static_cast<short>(aSize.Width()); 1746 nTreeFlags |= SvTreeFlags::RECALCTABS; 1747 } 1748 } 1749 SetEntryHeight( pEntry ); 1750 1751 if( !(nTreeFlags & SvTreeFlags::CHKBTN) ) 1752 return; 1753 1754 CheckBoxInserted(pEntry); 1755 } 1756 1757 void SvTreeListBox::SetCheckButtonState( SvTreeListEntry* pEntry, SvButtonState eState) 1758 { 1759 if( !(nTreeFlags & SvTreeFlags::CHKBTN) ) 1760 return; 1761 1762 SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button)); 1763 if(!pItem) 1764 return ; 1765 switch( eState ) 1766 { 1767 case SvButtonState::Checked: 1768 pItem->SetStateChecked(); 1769 break; 1770 1771 case SvButtonState::Unchecked: 1772 pItem->SetStateUnchecked(); 1773 break; 1774 1775 case SvButtonState::Tristate: 1776 pItem->SetStateTristate(); 1777 break; 1778 } 1779 InvalidateEntry( pEntry ); 1780 } 1781 1782 SvButtonState SvTreeListBox::GetCheckButtonState( SvTreeListEntry* pEntry ) const 1783 { 1784 SvButtonState eState = SvButtonState::Unchecked; 1785 if( pEntry && ( nTreeFlags & SvTreeFlags::CHKBTN ) ) 1786 { 1787 SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button)); 1788 if(!pItem) 1789 return SvButtonState::Tristate; 1790 SvItemStateFlags nButtonFlags = pItem->GetButtonFlags(); 1791 eState = SvLBoxButtonData::ConvertToButtonState( nButtonFlags ); 1792 } 1793 return eState; 1794 } 1795 1796 void SvTreeListBox::CheckButtonHdl() 1797 { 1798 aCheckButtonHdl.Call( this ); 1799 if ( pCheckButtonData ) 1800 pImpl->CallEventListeners( VclEventId::CheckboxToggle, static_cast<void*>(pCheckButtonData->GetActEntry()) ); 1801 } 1802 1803 1804 // TODO: Currently all data is cloned so that they conform to the default tree 1805 // view format. Actually, the model should be used as a reference here. This 1806 // leads to us _not_ calling SvTreeListEntry::Clone, but only its base class 1807 // SvTreeListEntry. 1808 1809 1810 SvTreeListEntry* SvTreeListBox::CloneEntry( SvTreeListEntry* pSource ) 1811 { 1812 OUString aStr; 1813 Image aCollEntryBmp; 1814 Image aExpEntryBmp; 1815 1816 SvLBoxString* pStringItem = static_cast<SvLBoxString*>(pSource->GetFirstItem(SvLBoxItemType::String)); 1817 if( pStringItem ) 1818 aStr = pStringItem->GetText(); 1819 SvLBoxContextBmp* pBmpItem = static_cast<SvLBoxContextBmp*>(pSource->GetFirstItem(SvLBoxItemType::ContextBmp)); 1820 if( pBmpItem ) 1821 { 1822 aCollEntryBmp = pBmpItem->GetBitmap1( ); 1823 aExpEntryBmp = pBmpItem->GetBitmap2( ); 1824 } 1825 SvTreeListEntry* pClone = new SvTreeListEntry; 1826 InitEntry( pClone, aStr, aCollEntryBmp, aExpEntryBmp ); 1827 pClone->SvTreeListEntry::Clone( pSource ); 1828 pClone->EnableChildrenOnDemand( pSource->HasChildrenOnDemand() ); 1829 pClone->SetUserData( pSource->GetUserData() ); 1830 1831 return pClone; 1832 } 1833 1834 void SvTreeListBox::SetIndent( short nNewIndent ) 1835 { 1836 nIndent = nNewIndent; 1837 SetTabs(); 1838 if( IsUpdateMode() ) 1839 Invalidate(); 1840 } 1841 1842 const Image& SvTreeListBox::GetDefaultExpandedEntryBmp( ) const 1843 { 1844 return pImpl->GetDefaultEntryExpBmp( ); 1845 } 1846 1847 const Image& SvTreeListBox::GetDefaultCollapsedEntryBmp( ) const 1848 { 1849 return pImpl->GetDefaultEntryColBmp( ); 1850 } 1851 1852 void SvTreeListBox::SetDefaultExpandedEntryBmp( const Image& aBmp ) 1853 { 1854 Size aSize = aBmp.GetSizePixel(); 1855 if( aSize.Width() > nContextBmpWidthMax ) 1856 nContextBmpWidthMax = static_cast<short>(aSize.Width()); 1857 SetTabs(); 1858 1859 pImpl->SetDefaultEntryExpBmp( aBmp ); 1860 } 1861 1862 void SvTreeListBox::SetDefaultCollapsedEntryBmp( const Image& aBmp ) 1863 { 1864 Size aSize = aBmp.GetSizePixel(); 1865 if( aSize.Width() > nContextBmpWidthMax ) 1866 nContextBmpWidthMax = static_cast<short>(aSize.Width()); 1867 SetTabs(); 1868 1869 pImpl->SetDefaultEntryColBmp( aBmp ); 1870 } 1871 1872 void SvTreeListBox::EnableCheckButton( SvLBoxButtonData* pData ) 1873 { 1874 if( !pData ) 1875 nTreeFlags &= ~SvTreeFlags::CHKBTN; 1876 else 1877 { 1878 SetCheckButtonData( pData ); 1879 nTreeFlags |= SvTreeFlags::CHKBTN; 1880 pData->SetLink( LINK(this, SvTreeListBox, CheckButtonClick)); 1881 } 1882 1883 SetTabs(); 1884 if( IsUpdateMode() ) 1885 Invalidate(); 1886 } 1887 1888 void SvTreeListBox::SetCheckButtonData( SvLBoxButtonData* pData ) 1889 { 1890 if ( pData ) 1891 pCheckButtonData = pData; 1892 } 1893 1894 const Image& SvTreeListBox::GetDefaultExpandedNodeImage( ) 1895 { 1896 return SvImpLBox::GetDefaultExpandedNodeImage( ); 1897 } 1898 1899 const Image& SvTreeListBox::GetDefaultCollapsedNodeImage( ) 1900 { 1901 return SvImpLBox::GetDefaultCollapsedNodeImage( ); 1902 } 1903 1904 void SvTreeListBox::SetNodeBitmaps( const Image& rCollapsedNodeBmp, const Image& rExpandedNodeBmp ) 1905 { 1906 SetExpandedNodeBmp( rExpandedNodeBmp ); 1907 SetCollapsedNodeBmp( rCollapsedNodeBmp ); 1908 SetTabs(); 1909 } 1910 1911 bool SvTreeListBox::EditingEntry( SvTreeListEntry*, Selection& ) 1912 { 1913 return true; 1914 } 1915 1916 bool SvTreeListBox::EditedEntry( SvTreeListEntry* /*pEntry*/,const OUString& /*rNewText*/) 1917 { 1918 return true; 1919 } 1920 1921 void SvTreeListBox::EnableInplaceEditing( bool bOn ) 1922 { 1923 if (bOn) 1924 nImpFlags |= SvTreeListBoxFlags::EDT_ENABLED; 1925 else 1926 nImpFlags &= ~SvTreeListBoxFlags::EDT_ENABLED; 1927 } 1928 1929 void SvTreeListBox::KeyInput( const KeyEvent& rKEvt ) 1930 { 1931 // under OS/2, we get key up/down even while editing 1932 if( IsEditingActive() ) 1933 return; 1934 1935 if( !pImpl->KeyInput( rKEvt ) ) 1936 { 1937 bool bHandled = HandleKeyInput( rKEvt ); 1938 if ( !bHandled ) 1939 Control::KeyInput( rKEvt ); 1940 } 1941 } 1942 1943 void SvTreeListBox::RequestingChildren( SvTreeListEntry* pParent ) 1944 { 1945 if( !pParent->HasChildren() ) 1946 InsertEntry( "<dummy>", pParent ); 1947 } 1948 1949 void SvTreeListBox::GetFocus() 1950 { 1951 //If there is no item in the tree, draw focus. 1952 if( !First()) 1953 { 1954 Invalidate(); 1955 } 1956 pImpl->GetFocus(); 1957 Control::GetFocus(); 1958 1959 SvTreeListEntry* pEntry = FirstSelected(); 1960 if ( !pEntry ) 1961 { 1962 pEntry = pImpl->GetCurrentEntry(); 1963 } 1964 if (pImpl->m_pCursor) 1965 { 1966 if (pEntry != pImpl->m_pCursor) 1967 pEntry = pImpl->m_pCursor; 1968 } 1969 if ( pEntry ) 1970 pImpl->CallEventListeners( VclEventId::ListboxTreeFocus, pEntry ); 1971 1972 } 1973 1974 void SvTreeListBox::LoseFocus() 1975 { 1976 // If there is no item in the tree, delete visual focus. 1977 if ( !First() ) 1978 Invalidate(); 1979 if ( pImpl ) 1980 pImpl->LoseFocus(); 1981 Control::LoseFocus(); 1982 } 1983 1984 void SvTreeListBox::ModelHasCleared() 1985 { 1986 pImpl->m_pCursor = nullptr; // else we crash in GetFocus when editing in-place 1987 pEdCtrl.reset(); 1988 pImpl->Clear(); 1989 nFocusWidth = -1; 1990 1991 nContextBmpWidthMax = 0; 1992 SetDefaultExpandedEntryBmp( GetDefaultExpandedEntryBmp() ); 1993 SetDefaultCollapsedEntryBmp( GetDefaultCollapsedEntryBmp() ); 1994 1995 if( !(nTreeFlags & SvTreeFlags::FIXEDHEIGHT )) 1996 nEntryHeight = 0; 1997 AdjustEntryHeight(); 1998 AdjustEntryHeight( GetDefaultExpandedEntryBmp() ); 1999 AdjustEntryHeight( GetDefaultCollapsedEntryBmp() ); 2000 2001 SvListView::ModelHasCleared(); 2002 } 2003 2004 void SvTreeListBox::ScrollOutputArea( short nDeltaEntries ) 2005 { 2006 if( !nDeltaEntries || !pImpl->m_aVerSBar->IsVisible() ) 2007 return; 2008 2009 long nThumb = pImpl->m_aVerSBar->GetThumbPos(); 2010 long nMax = pImpl->m_aVerSBar->GetRange().Max(); 2011 2012 if( nDeltaEntries < 0 ) 2013 { 2014 // move window up 2015 nDeltaEntries *= -1; 2016 long nVis = pImpl->m_aVerSBar->GetVisibleSize(); 2017 long nTemp = nThumb + nVis; 2018 if( nDeltaEntries > (nMax - nTemp) ) 2019 nDeltaEntries = static_cast<short>(nMax - nTemp); 2020 pImpl->PageDown( static_cast<sal_uInt16>(nDeltaEntries) ); 2021 } 2022 else 2023 { 2024 if( nDeltaEntries > nThumb ) 2025 nDeltaEntries = static_cast<short>(nThumb); 2026 pImpl->PageUp( static_cast<sal_uInt16>(nDeltaEntries) ); 2027 } 2028 pImpl->SyncVerThumb(); 2029 NotifyEndScroll(); 2030 } 2031 2032 void SvTreeListBox::ScrollToAbsPos( long nPos ) 2033 { 2034 pImpl->ScrollToAbsPos( nPos ); 2035 } 2036 2037 void SvTreeListBox::SetSelectionMode( SelectionMode eSelectMode ) 2038 { 2039 eSelMode = eSelectMode; 2040 pImpl->SetSelectionMode( eSelectMode ); 2041 } 2042 2043 void SvTreeListBox::SetDragDropMode( DragDropMode nDDMode ) 2044 { 2045 nDragDropMode = nDDMode; 2046 pImpl->SetDragDropMode( nDDMode ); 2047 } 2048 2049 void SvTreeListBox::SetEntryHeight( SvTreeListEntry const * pEntry ) 2050 { 2051 short nHeightMax=0; 2052 sal_uInt16 nCount = pEntry->ItemCount(); 2053 sal_uInt16 nCur = 0; 2054 SvViewDataEntry* pViewData = GetViewDataEntry( pEntry ); 2055 while( nCur < nCount ) 2056 { 2057 auto nHeight = SvLBoxItem::GetHeight(pViewData, nCur); 2058 if( nHeight > nHeightMax ) 2059 nHeightMax = nHeight; 2060 nCur++; 2061 } 2062 2063 if( nHeightMax > nEntryHeight ) 2064 { 2065 nEntryHeight = nHeightMax; 2066 Control::SetFont( GetFont() ); 2067 pImpl->SetEntryHeight(); 2068 } 2069 } 2070 2071 void SvTreeListBox::SetEntryHeight( short nHeight, bool bForce ) 2072 { 2073 if( nHeight > nEntryHeight || bForce ) 2074 { 2075 nEntryHeight = nHeight; 2076 if( nEntryHeight ) 2077 nTreeFlags |= SvTreeFlags::FIXEDHEIGHT; 2078 else 2079 nTreeFlags &= ~SvTreeFlags::FIXEDHEIGHT; 2080 Control::SetFont( GetFont() ); 2081 pImpl->SetEntryHeight(); 2082 } 2083 } 2084 2085 void SvTreeListBox::SetEntryWidth( short nWidth ) 2086 { 2087 nEntryWidth = nWidth; 2088 } 2089 2090 void SvTreeListBox::AdjustEntryHeight( const Image& rBmp ) 2091 { 2092 const Size aSize( rBmp.GetSizePixel() ); 2093 if( aSize.Height() > nEntryHeight ) 2094 { 2095 nEntryHeight = static_cast<short>(aSize.Height()) + nEntryHeightOffs; 2096 pImpl->SetEntryHeight(); 2097 } 2098 } 2099 2100 void SvTreeListBox::AdjustEntryHeight() 2101 { 2102 Size aSize( GetTextWidth(OUString('X')), GetTextHeight() ); 2103 if( aSize.Height() > nEntryHeight ) 2104 { 2105 nEntryHeight = static_cast<short>(aSize.Height()) + nEntryHeightOffs; 2106 pImpl->SetEntryHeight(); 2107 } 2108 } 2109 2110 bool SvTreeListBox::Expand( SvTreeListEntry* pParent ) 2111 { 2112 pHdlEntry = pParent; 2113 bool bExpanded = false; 2114 SvTLEntryFlags nFlags; 2115 2116 if( pParent->HasChildrenOnDemand() ) 2117 RequestingChildren( pParent ); 2118 bool bExpandAllowed = pParent->HasChildren() && ExpandingHdl(); 2119 // double check if the expander callback ended up removing all children 2120 if (pParent->HasChildren()) 2121 { 2122 if (bExpandAllowed) 2123 { 2124 bExpanded = true; 2125 ExpandListEntry( pParent ); 2126 pImpl->EntryExpanded( pParent ); 2127 pHdlEntry = pParent; 2128 ExpandedHdl(); 2129 SetAlternatingRowColors( mbAlternatingRowColors ); 2130 } 2131 nFlags = pParent->GetFlags(); 2132 nFlags &= ~SvTLEntryFlags::NO_NODEBMP; 2133 nFlags |= SvTLEntryFlags::HAD_CHILDREN; 2134 pParent->SetFlags( nFlags ); 2135 } 2136 else 2137 { 2138 nFlags = pParent->GetFlags(); 2139 nFlags |= SvTLEntryFlags::NO_NODEBMP; 2140 pParent->SetFlags( nFlags ); 2141 GetModel()->InvalidateEntry( pParent ); // repaint 2142 } 2143 2144 // #i92103# 2145 if ( bExpanded ) 2146 { 2147 pImpl->CallEventListeners( VclEventId::ItemExpanded, pParent ); 2148 } 2149 2150 return bExpanded; 2151 } 2152 2153 bool SvTreeListBox::Collapse( SvTreeListEntry* pParent ) 2154 { 2155 pHdlEntry = pParent; 2156 bool bCollapsed = false; 2157 2158 if( ExpandingHdl() ) 2159 { 2160 bCollapsed = true; 2161 pImpl->CollapsingEntry( pParent ); 2162 CollapseListEntry( pParent ); 2163 pImpl->EntryCollapsed( pParent ); 2164 pHdlEntry = pParent; 2165 ExpandedHdl(); 2166 SetAlternatingRowColors( mbAlternatingRowColors ); 2167 } 2168 2169 // #i92103# 2170 if ( bCollapsed ) 2171 { 2172 pImpl->CallEventListeners( VclEventId::ItemCollapsed, pParent ); 2173 } 2174 2175 return bCollapsed; 2176 } 2177 2178 bool SvTreeListBox::Select( SvTreeListEntry* pEntry, bool bSelect ) 2179 { 2180 DBG_ASSERT(pEntry,"Select: Null-Ptr"); 2181 bool bRetVal = SelectListEntry( pEntry, bSelect ); 2182 DBG_ASSERT(IsSelected(pEntry)==bSelect,"Select failed"); 2183 if( bRetVal ) 2184 { 2185 pImpl->EntrySelected( pEntry, bSelect ); 2186 pHdlEntry = pEntry; 2187 if( bSelect ) 2188 { 2189 SelectHdl(); 2190 CallEventListeners( VclEventId::ListboxTreeSelect, pEntry); 2191 } 2192 else 2193 DeselectHdl(); 2194 } 2195 return bRetVal; 2196 } 2197 2198 sal_uLong SvTreeListBox::SelectChildren( SvTreeListEntry* pParent, bool bSelect ) 2199 { 2200 pImpl->DestroyAnchor(); 2201 sal_uLong nRet = 0; 2202 if( !pParent->HasChildren() ) 2203 return 0; 2204 sal_uInt16 nRefDepth = pModel->GetDepth( pParent ); 2205 SvTreeListEntry* pChild = FirstChild( pParent ); 2206 do { 2207 nRet++; 2208 Select( pChild, bSelect ); 2209 pChild = Next( pChild ); 2210 } while( pChild && pModel->GetDepth( pChild ) > nRefDepth ); 2211 return nRet; 2212 } 2213 2214 void SvTreeListBox::SelectAll( bool bSelect, bool ) 2215 { 2216 pImpl->SelAllDestrAnch( 2217 bSelect, 2218 true, // delete anchor, 2219 true ); // even when using SelectionMode::Single, deselect the cursor 2220 } 2221 2222 void SvTreeListBox::ModelHasInsertedTree( SvTreeListEntry* pEntry ) 2223 { 2224 sal_uInt16 nRefDepth = pModel->GetDepth( pEntry ); 2225 SvTreeListEntry* pTmp = pEntry; 2226 do 2227 { 2228 ImpEntryInserted( pTmp ); 2229 pTmp = Next( pTmp ); 2230 } while( pTmp && nRefDepth < pModel->GetDepth( pTmp ) ); 2231 pImpl->TreeInserted( pEntry ); 2232 } 2233 2234 void SvTreeListBox::ModelHasInserted( SvTreeListEntry* pEntry ) 2235 { 2236 ImpEntryInserted( pEntry ); 2237 pImpl->EntryInserted( pEntry ); 2238 } 2239 2240 void SvTreeListBox::ModelIsMoving(SvTreeListEntry* pSource ) 2241 { 2242 pImpl->MovingEntry( pSource ); 2243 } 2244 2245 void SvTreeListBox::ModelHasMoved( SvTreeListEntry* pSource ) 2246 { 2247 pImpl->EntryMoved( pSource ); 2248 } 2249 2250 void SvTreeListBox::ModelIsRemoving( SvTreeListEntry* pEntry ) 2251 { 2252 if(pEdEntry == pEntry) 2253 pEdEntry = nullptr; 2254 2255 pImpl->RemovingEntry( pEntry ); 2256 } 2257 2258 void SvTreeListBox::ModelHasRemoved( SvTreeListEntry* pEntry ) 2259 { 2260 if ( pEntry == pHdlEntry) 2261 pHdlEntry = nullptr; 2262 pImpl->EntryRemoved(); 2263 } 2264 2265 void SvTreeListBox::SetCollapsedNodeBmp( const Image& rBmp) 2266 { 2267 AdjustEntryHeight( rBmp ); 2268 pImpl->SetCollapsedNodeBmp( rBmp ); 2269 } 2270 2271 void SvTreeListBox::SetExpandedNodeBmp( const Image& rBmp ) 2272 { 2273 AdjustEntryHeight( rBmp ); 2274 pImpl->SetExpandedNodeBmp( rBmp ); 2275 } 2276 2277 2278 void SvTreeListBox::SetFont( const vcl::Font& rFont ) 2279 { 2280 vcl::Font aTempFont( rFont ); 2281 vcl::Font aOrigFont( GetFont() ); 2282 aTempFont.SetTransparent( true ); 2283 if (aTempFont == aOrigFont) 2284 return; 2285 Control::SetFont( aTempFont ); 2286 2287 aTempFont.SetColor(aOrigFont.GetColor()); 2288 aTempFont.SetFillColor(aOrigFont.GetFillColor()); 2289 aTempFont.SetTransparent(aOrigFont.IsTransparent()); 2290 2291 if (aTempFont == aOrigFont) 2292 return; 2293 2294 AdjustEntryHeightAndRecalc(); 2295 } 2296 2297 void SvTreeListBox::AdjustEntryHeightAndRecalc() 2298 { 2299 AdjustEntryHeight(); 2300 // always invalidate, else things go wrong in SetEntryHeight 2301 RecalcViewData(); 2302 } 2303 2304 void SvTreeListBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) 2305 { 2306 Control::Paint(rRenderContext, rRect); 2307 if (nTreeFlags & SvTreeFlags::RECALCTABS) 2308 SetTabs(); 2309 pImpl->Paint(rRenderContext, rRect); 2310 2311 //Add visual focus draw 2312 if (First()) 2313 return; 2314 2315 if (HasFocus()) 2316 { 2317 long nHeight = rRenderContext.GetTextHeight(); 2318 tools::Rectangle aRect(Point(0, 0), Size(GetSizePixel().Width(), nHeight)); 2319 ShowFocus(aRect); 2320 } 2321 else 2322 { 2323 HideFocus(); 2324 } 2325 } 2326 2327 void SvTreeListBox::MouseButtonDown( const MouseEvent& rMEvt ) 2328 { 2329 pImpl->MouseButtonDown( rMEvt ); 2330 } 2331 2332 void SvTreeListBox::MouseButtonUp( const MouseEvent& rMEvt ) 2333 { 2334 pImpl->MouseButtonUp( rMEvt ); 2335 } 2336 2337 void SvTreeListBox::MouseMove( const MouseEvent& rMEvt ) 2338 { 2339 pImpl->MouseMove( rMEvt ); 2340 } 2341 2342 2343 void SvTreeListBox::SetUpdateMode( bool bUpdate ) 2344 { 2345 pImpl->SetUpdateMode( bUpdate ); 2346 mbUpdateAlternatingRows = bUpdate; 2347 SetAlternatingRowColors( mbAlternatingRowColors ); 2348 } 2349 2350 void SvTreeListBox::SetSpaceBetweenEntries( short nOffsLogic ) 2351 { 2352 if( nOffsLogic != nEntryHeightOffs ) 2353 { 2354 nEntryHeight = nEntryHeight - nEntryHeightOffs; 2355 nEntryHeightOffs = nOffsLogic; 2356 nEntryHeight = nEntryHeight + nOffsLogic; 2357 AdjustEntryHeightAndRecalc(); 2358 pImpl->SetEntryHeight(); 2359 } 2360 } 2361 2362 void SvTreeListBox::SetCursor( SvTreeListEntry* pEntry, bool bForceNoSelect ) 2363 { 2364 pImpl->SetCursor(pEntry, bForceNoSelect); 2365 } 2366 2367 void SvTreeListBox::SetCurEntry( SvTreeListEntry* pEntry ) 2368 { 2369 pImpl->SetCurEntry( pEntry ); 2370 } 2371 2372 Image const & SvTreeListBox::GetExpandedNodeBmp( ) const 2373 { 2374 return pImpl->GetExpandedNodeBmp( ); 2375 } 2376 2377 Point SvTreeListBox::GetEntryPosition( SvTreeListEntry* pEntry ) const 2378 { 2379 return pImpl->GetEntryPosition( pEntry ); 2380 } 2381 2382 void SvTreeListBox::MakeVisible( SvTreeListEntry* pEntry ) 2383 { 2384 pImpl->MakeVisible(pEntry); 2385 } 2386 2387 void SvTreeListBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop ) 2388 { 2389 pImpl->MakeVisible( pEntry, bMoveToTop ); 2390 } 2391 2392 void SvTreeListBox::ModelHasEntryInvalidated( SvTreeListEntry* pEntry ) 2393 { 2394 2395 // reinitialize the separate items of the entries 2396 sal_uInt16 nCount = pEntry->ItemCount(); 2397 for( sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++ ) 2398 { 2399 SvLBoxItem& rItem = pEntry->GetItem( nIdx ); 2400 rItem.InitViewData( this, pEntry ); 2401 } 2402 2403 // repaint 2404 pImpl->InvalidateEntry( pEntry ); 2405 } 2406 2407 void SvTreeListBox::EditItemText(SvTreeListEntry* pEntry, SvLBoxString* pItem, const Selection& rSelection) 2408 { 2409 assert(pEntry && pItem); 2410 if( IsSelected( pEntry )) 2411 { 2412 pImpl->ShowCursor( false ); 2413 SelectListEntry( pEntry, false ); 2414 pImpl->InvalidateEntry(pEntry); 2415 SelectListEntry( pEntry, true ); 2416 pImpl->ShowCursor( true ); 2417 } 2418 pEdEntry = pEntry; 2419 pEdItem = pItem; 2420 SvLBoxTab* pTab = GetTab( pEntry, pItem ); 2421 DBG_ASSERT(pTab,"EditItemText:Tab not found"); 2422 2423 auto nItemHeight( pItem->GetHeight(this, pEntry) ); 2424 Point aPos = GetEntryPosition( pEntry ); 2425 aPos.AdjustY(( nEntryHeight - nItemHeight ) / 2 ); 2426 aPos.setX( GetTabPos( pEntry, pTab ) ); 2427 long nOutputWidth = pImpl->GetOutputSize().Width(); 2428 Size aSize( nOutputWidth - aPos.X(), nItemHeight ); 2429 sal_uInt16 nPos = std::find_if( aTabs.begin(), aTabs.end(), 2430 [pTab](const std::unique_ptr<SvLBoxTab>& p) { return p.get() == pTab; }) 2431 - aTabs.begin(); 2432 if( nPos+1 < static_cast<sal_uInt16>(aTabs.size()) ) 2433 { 2434 SvLBoxTab* pRightTab = aTabs[ nPos + 1 ].get(); 2435 long nRight = GetTabPos( pEntry, pRightTab ); 2436 if( nRight <= nOutputWidth ) 2437 aSize.setWidth( nRight - aPos.X() ); 2438 } 2439 Point aOrigin( GetMapMode().GetOrigin() ); 2440 aPos += aOrigin; // convert to win coordinates 2441 aSize.AdjustWidth( -(aOrigin.X()) ); 2442 tools::Rectangle aRect( aPos, aSize ); 2443 EditText( pItem->GetText(), aRect, rSelection ); 2444 } 2445 2446 void SvTreeListBox::EditEntry( SvTreeListEntry* pEntry ) 2447 { 2448 pImpl->m_aEditClickPos = Point( -1, -1 ); 2449 ImplEditEntry( pEntry ); 2450 } 2451 2452 void SvTreeListBox::ImplEditEntry( SvTreeListEntry* pEntry ) 2453 { 2454 if( IsEditingActive() ) 2455 EndEditing(); 2456 if( !pEntry ) 2457 pEntry = GetCurEntry(); 2458 if( !pEntry ) 2459 return; 2460 2461 long nClickX = pImpl->m_aEditClickPos.X(); 2462 bool bIsMouseTriggered = nClickX >= 0; 2463 2464 SvLBoxString* pItem = nullptr; 2465 sal_uInt16 nCount = pEntry->ItemCount(); 2466 long nTabPos, nNextTabPos = 0; 2467 for( sal_uInt16 i = 0 ; i < nCount ; i++ ) 2468 { 2469 SvLBoxItem& rTmpItem = pEntry->GetItem( i ); 2470 if (rTmpItem.GetType() != SvLBoxItemType::String) 2471 continue; 2472 2473 SvLBoxTab* pTab = GetTab( pEntry, &rTmpItem ); 2474 nNextTabPos = -1; 2475 if( i < nCount - 1 ) 2476 { 2477 SvLBoxItem& rNextItem = pEntry->GetItem( i + 1 ); 2478 SvLBoxTab* pNextTab = GetTab( pEntry, &rNextItem ); 2479 nNextTabPos = pNextTab->GetPos(); 2480 } 2481 2482 if( pTab && pTab->IsEditable() ) 2483 { 2484 nTabPos = pTab->GetPos(); 2485 if( !bIsMouseTriggered || (nClickX > nTabPos && (nNextTabPos == -1 || nClickX < nNextTabPos ) ) ) 2486 { 2487 pItem = static_cast<SvLBoxString*>( &rTmpItem ); 2488 break; 2489 } 2490 } 2491 } 2492 2493 Selection aSel( SELECTION_MIN, SELECTION_MAX ); 2494 if( pItem && EditingEntry( pEntry, aSel ) ) 2495 { 2496 SelectAll( false ); 2497 MakeVisible( pEntry ); 2498 EditItemText( pEntry, pItem, aSel ); 2499 } 2500 } 2501 2502 void SvTreeListBox::EditedText( const OUString& rStr ) 2503 2504 { 2505 if(pEdEntry) // we have to check if this entry is null that means that it is removed while editing 2506 { 2507 if( EditedEntry( pEdEntry, rStr ) ) 2508 { 2509 static_cast<SvLBoxString*>(pEdItem)->SetText( rStr ); 2510 pModel->InvalidateEntry( pEdEntry ); 2511 } 2512 if( GetSelectionCount() == 0 ) 2513 Select( pEdEntry ); 2514 if( GetSelectionMode() == SelectionMode::Multiple && !GetCurEntry() ) 2515 SetCurEntry( pEdEntry ); 2516 } 2517 } 2518 2519 SvTreeListEntry* SvTreeListBox::GetDropTarget( const Point& rPos ) 2520 { 2521 // scroll 2522 if( rPos.Y() < 12 ) 2523 { 2524 ImplShowTargetEmphasis(pTargetEntry, false); 2525 ScrollOutputArea( +1 ); 2526 } 2527 else 2528 { 2529 Size aSize( pImpl->GetOutputSize() ); 2530 if( rPos.Y() > aSize.Height() - 12 ) 2531 { 2532 ImplShowTargetEmphasis(pTargetEntry, false); 2533 ScrollOutputArea( -1 ); 2534 } 2535 } 2536 2537 SvTreeListEntry* pTarget = pImpl->GetEntry( rPos ); 2538 // when dropping in a vacant space, use the last entry 2539 if( !pTarget ) 2540 return LastVisible(); 2541 else if( (GetDragDropMode() & DragDropMode::ENABLE_TOP) && 2542 pTarget == First() && rPos.Y() < 6 ) 2543 return nullptr; 2544 2545 return pTarget; 2546 } 2547 2548 2549 SvTreeListEntry* SvTreeListBox::GetEntry( const Point& rPos, bool bHit ) const 2550 { 2551 SvTreeListEntry* pEntry = pImpl->GetEntry( rPos ); 2552 if( pEntry && bHit ) 2553 { 2554 long nLine = pImpl->GetEntryLine( pEntry ); 2555 if( !(pImpl->EntryReallyHit( pEntry, rPos, nLine)) ) 2556 return nullptr; 2557 } 2558 return pEntry; 2559 } 2560 2561 SvTreeListEntry* SvTreeListBox::GetCurEntry() const 2562 { 2563 return pImpl ? pImpl->GetCurEntry() : nullptr; 2564 } 2565 2566 void SvTreeListBox::ImplInitStyle() 2567 { 2568 const WinBits nWindowStyle = GetStyle(); 2569 2570 nTreeFlags |= SvTreeFlags::RECALCTABS; 2571 if (nWindowStyle & WB_SORT) 2572 { 2573 GetModel()->SetSortMode(SortAscending); 2574 GetModel()->SetCompareHdl(LINK(this, SvTreeListBox, DefaultCompare)); 2575 } 2576 else 2577 { 2578 GetModel()->SetSortMode(SortNone); 2579 GetModel()->SetCompareHdl(Link<const SvSortData&,sal_Int32>()); 2580 } 2581 pImpl->SetStyle(nWindowStyle); 2582 pImpl->Resize(); 2583 Invalidate(); 2584 } 2585 2586 void SvTreeListBox::InvalidateEntry(SvTreeListEntry* pEntry) 2587 { 2588 DBG_ASSERT(pEntry,"InvalidateEntry:No Entry"); 2589 if (pEntry) 2590 { 2591 GetModel()->InvalidateEntry(pEntry); 2592 } 2593 } 2594 2595 void SvTreeListBox::PaintEntry1(SvTreeListEntry& rEntry, long nLine, vcl::RenderContext& rRenderContext) 2596 { 2597 tools::Rectangle aRect; // multi purpose 2598 2599 bool bHorSBar = pImpl->HasHorScrollBar(); 2600 PreparePaint(rRenderContext, rEntry); 2601 2602 pImpl->UpdateContextBmpWidthMax(&rEntry); 2603 2604 if (nTreeFlags & SvTreeFlags::RECALCTABS) 2605 SetTabs(); 2606 2607 short nTempEntryHeight = GetEntryHeight(); 2608 long nWidth = pImpl->GetOutputSize().Width(); 2609 2610 // Did we turn on the scrollbar within PreparePaints? If yes, we have to set 2611 // the ClipRegion anew. 2612 if (!bHorSBar && pImpl->HasHorScrollBar()) 2613 rRenderContext.SetClipRegion(vcl::Region(pImpl->GetClipRegionRect())); 2614 2615 Point aEntryPos(rRenderContext.GetMapMode().GetOrigin()); 2616 aEntryPos.setX( aEntryPos.X() * -1 ); // conversion document coordinates 2617 long nMaxRight = nWidth + aEntryPos.X() - 1; 2618 2619 Color aBackupTextColor(rRenderContext.GetTextColor()); 2620 vcl::Font aBackupFont(rRenderContext.GetFont()); 2621 Color aBackupColor = rRenderContext.GetFillColor(); 2622 2623 bool bCurFontIsSel = false; 2624 // if a ClipRegion was set from outside, we don't have to reset it 2625 const WinBits nWindowStyle = GetStyle(); 2626 const bool bHideSelection = (nWindowStyle & WB_HIDESELECTION) !=0 && !HasFocus(); 2627 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings(); 2628 2629 vcl::Font aHighlightFont(rRenderContext.GetFont()); 2630 const Color aHighlightTextColor(rSettings.GetHighlightTextColor()); 2631 aHighlightFont.SetColor(aHighlightTextColor); 2632 2633 Size aRectSize(0, nTempEntryHeight); 2634 2635 SvViewDataEntry* pViewDataEntry = GetViewDataEntry( &rEntry ); 2636 2637 const size_t nTabCount = aTabs.size(); 2638 const size_t nItemCount = rEntry.ItemCount(); 2639 size_t nCurTab = 0; 2640 size_t nCurItem = 0; 2641 2642 while (nCurTab < nTabCount && nCurItem < nItemCount) 2643 { 2644 SvLBoxTab* pTab = aTabs[nCurTab].get(); 2645 const size_t nNextTab = nCurTab + 1; 2646 SvLBoxTab* pNextTab = nNextTab < nTabCount ? aTabs[nNextTab].get() : nullptr; 2647 SvLBoxItem& rItem = rEntry.GetItem(nCurItem); 2648 2649 SvLBoxTabFlags nFlags = pTab->nFlags; 2650 Size aSize(rItem.GetWidth(this, pViewDataEntry, nCurItem), 2651 SvLBoxItem::GetHeight(pViewDataEntry, nCurItem)); 2652 long nTabPos = GetTabPos(&rEntry, pTab); 2653 2654 long nNextTabPos; 2655 if (pNextTab) 2656 nNextTabPos = GetTabPos(&rEntry, pNextTab); 2657 else 2658 { 2659 nNextTabPos = nMaxRight; 2660 if (nTabPos > nMaxRight) 2661 nNextTabPos += 50; 2662 } 2663 2664 long nX; 2665 if( pTab->nFlags & SvLBoxTabFlags::ADJUST_RIGHT ) 2666 // avoid cutting the right edge off the tab separation 2667 nX = nTabPos + pTab->CalcOffset(aSize.Width(), (nNextTabPos - SV_TAB_BORDER - 1) - nTabPos); 2668 else 2669 nX = nTabPos + pTab->CalcOffset(aSize.Width(), nNextTabPos - nTabPos); 2670 2671 aEntryPos.setX( nX ); 2672 aEntryPos.setY( nLine ); 2673 2674 // set background pattern/color 2675 2676 Wallpaper aWallpaper = rRenderContext.GetBackground(); 2677 2678 bool bSelTab = bool(nFlags & SvLBoxTabFlags::SHOW_SELECTION); 2679 2680 if (pViewDataEntry->IsHighlighted() && bSelTab) 2681 { 2682 Color aNewWallColor = rSettings.GetHighlightColor(); 2683 // if the face color is bright then the deactivate color is also bright 2684 // -> so you can't see any deactivate selection 2685 if (bHideSelection && !rSettings.GetFaceColor().IsBright() 2686 && aWallpaper.GetColor().IsBright() != rSettings.GetDeactiveColor().IsBright()) 2687 { 2688 aNewWallColor = rSettings.GetDeactiveColor(); 2689 } 2690 // set font color to highlight 2691 if (!bCurFontIsSel) 2692 { 2693 rRenderContext.SetTextColor(aHighlightTextColor); 2694 rRenderContext.SetFont(aHighlightFont); 2695 bCurFontIsSel = true; 2696 } 2697 aWallpaper.SetColor(aNewWallColor); 2698 } 2699 else // no selection 2700 { 2701 if (bCurFontIsSel || rEntry.GetTextColor()) 2702 { 2703 bCurFontIsSel = false; 2704 if (const auto & xCustomTextColor = rEntry.GetTextColor()) 2705 rRenderContext.SetTextColor(*xCustomTextColor); 2706 else 2707 rRenderContext.SetTextColor(aBackupTextColor); 2708 rRenderContext.SetFont(aBackupFont); 2709 } 2710 else 2711 { 2712 aWallpaper.SetColor(rEntry.GetBackColor()); 2713 } 2714 } 2715 2716 // draw background 2717 if (!(nTreeFlags & SvTreeFlags::USESEL)) 2718 { 2719 // only draw the area that is used by the item 2720 aRectSize.setWidth( aSize.Width() ); 2721 aRect.SetPos(aEntryPos); 2722 aRect.SetSize(aRectSize); 2723 } 2724 else 2725 { 2726 // draw from the current to the next tab 2727 if (nCurTab != 0) 2728 aRect.SetLeft( nTabPos ); 2729 else 2730 // if we're in the 0th tab, always draw from column 0 -- 2731 // else we get problems with centered tabs 2732 aRect.SetLeft( 0 ); 2733 aRect.SetTop( nLine ); 2734 aRect.SetBottom( nLine + nTempEntryHeight - 1 ); 2735 if (pNextTab) 2736 { 2737 long nRight; 2738 nRight = GetTabPos(&rEntry, pNextTab) - 1; 2739 if (nRight > nMaxRight) 2740 nRight = nMaxRight; 2741 aRect.SetRight( nRight ); 2742 } 2743 else 2744 { 2745 aRect.SetRight( nMaxRight ); 2746 } 2747 } 2748 // A custom selection that starts at a tab position > 0, do not fill 2749 // the background of the 0th item, else e.g. we might not be able to 2750 // realize tab listboxes with lines. 2751 if (!(nCurTab == 0 && (nTreeFlags & SvTreeFlags::USESEL) && nFirstSelTab)) 2752 { 2753 Color aBackgroundColor = aWallpaper.GetColor(); 2754 if (aBackgroundColor != COL_TRANSPARENT) 2755 { 2756 rRenderContext.SetFillColor(aBackgroundColor); 2757 // this case may occur for smaller horizontal resizes 2758 if (aRect.Left() < aRect.Right()) 2759 rRenderContext.DrawRect(aRect); 2760 } 2761 } 2762 // draw item 2763 // center vertically 2764 aEntryPos.AdjustY((nTempEntryHeight - aSize.Height()) / 2 ); 2765 pViewDataEntry->SetPaintRectangle(aRect); 2766 2767 rItem.Paint(aEntryPos, *this, rRenderContext, pViewDataEntry, rEntry); 2768 2769 // division line between tabs 2770 if (pNextTab && rItem.GetType() == SvLBoxItemType::String && 2771 // not at the right edge of the window! 2772 aRect.Right() < nMaxRight) 2773 { 2774 aRect.SetLeft( aRect.Right() - SV_TAB_BORDER ); 2775 rRenderContext.DrawRect(aRect); 2776 } 2777 2778 rRenderContext.SetFillColor(aBackupColor); 2779 2780 nCurItem++; 2781 nCurTab++; 2782 } 2783 2784 if (pViewDataEntry->IsDragTarget()) 2785 { 2786 rRenderContext.Push(); 2787 rRenderContext.SetLineColor(rSettings.GetDeactiveColor()); 2788 rRenderContext.SetFillColor(rSettings.GetDeactiveColor()); 2789 rRenderContext.DrawRect(tools::Rectangle(Point(0, nLine + nTempEntryHeight - 2), Size(nWidth, 2))); 2790 rRenderContext.Pop(); 2791 } 2792 2793 if (bCurFontIsSel || rEntry.GetTextColor()) 2794 { 2795 rRenderContext.SetTextColor(aBackupTextColor); 2796 rRenderContext.SetFont(aBackupFont); 2797 } 2798 2799 sal_uInt16 nFirstDynTabPos(0); 2800 SvLBoxTab* pFirstDynamicTab = GetFirstDynamicTab(nFirstDynTabPos); 2801 long nDynTabPos = GetTabPos(&rEntry, pFirstDynamicTab); 2802 nDynTabPos += pImpl->m_nNodeBmpTabDistance; 2803 nDynTabPos += pImpl->m_nNodeBmpWidth / 2; 2804 nDynTabPos += 4; // 4 pixels of buffer, so the node bitmap is not too close 2805 // to the next tab 2806 2807 if( !((!(rEntry.GetFlags() & SvTLEntryFlags::NO_NODEBMP)) && 2808 (nWindowStyle & WB_HASBUTTONS) && pFirstDynamicTab && 2809 (rEntry.HasChildren() || rEntry.HasChildrenOnDemand()))) 2810 return; 2811 2812 // find first tab and check if the node bitmap extends into it 2813 sal_uInt16 nNextTab = nFirstDynTabPos; 2814 SvLBoxTab* pNextTab; 2815 do 2816 { 2817 nNextTab++; 2818 pNextTab = nNextTab < nTabCount ? aTabs[nNextTab].get() : nullptr; 2819 } while (pNextTab && pNextTab->IsDynamic()); 2820 2821 if (!(!pNextTab || (GetTabPos( &rEntry, pNextTab ) > nDynTabPos))) 2822 return; 2823 2824 if (!((nWindowStyle & WB_HASBUTTONSATROOT) || pModel->GetDepth(&rEntry) > 0)) 2825 return; 2826 2827 Point aPos(GetTabPos(&rEntry, pFirstDynamicTab), nLine); 2828 aPos.AdjustX(pImpl->m_nNodeBmpTabDistance ); 2829 2830 const Image* pImg = nullptr; 2831 2832 if (IsExpanded(&rEntry)) 2833 pImg = &pImpl->GetExpandedNodeBmp(); 2834 else 2835 { 2836 if ((!rEntry.HasChildren()) && rEntry.HasChildrenOnDemand() && 2837 (!(rEntry.GetFlags() & SvTLEntryFlags::HAD_CHILDREN)) && 2838 pImpl->GetDontKnowNodeBmp().GetSizePixel().Width()) 2839 { 2840 pImg = &pImpl->GetDontKnowNodeBmp( ); 2841 } 2842 else 2843 { 2844 pImg = &pImpl->GetCollapsedNodeBmp( ); 2845 } 2846 } 2847 aPos.AdjustY((nTempEntryHeight - pImg->GetSizePixel().Height()) / 2 ); 2848 2849 DrawImageFlags nStyle = DrawImageFlags::NONE; 2850 if (!IsEnabled()) 2851 nStyle |= DrawImageFlags::Disable; 2852 2853 //native 2854 bool bNativeOK = false; 2855 if (rRenderContext.IsNativeControlSupported(ControlType::ListNode, ControlPart::Entire)) 2856 { 2857 ImplControlValue aControlValue; 2858 tools::Rectangle aCtrlRegion(aPos, pImg->GetSizePixel()); 2859 ControlState nState = ControlState::NONE; 2860 2861 if (IsEnabled()) 2862 nState |= ControlState::ENABLED; 2863 2864 if (IsExpanded(&rEntry)) 2865 aControlValue.setTristateVal(ButtonValue::On); //expanded node 2866 else 2867 { 2868 if ((!rEntry.HasChildren()) && rEntry.HasChildrenOnDemand() && 2869 (!(rEntry.GetFlags() & SvTLEntryFlags::HAD_CHILDREN)) && 2870 pImpl->GetDontKnowNodeBmp().GetSizePixel().Width()) 2871 { 2872 aControlValue.setTristateVal( ButtonValue::DontKnow ); //don't know 2873 } 2874 else 2875 { 2876 aControlValue.setTristateVal( ButtonValue::Off ); //collapsed node 2877 } 2878 } 2879 2880 bNativeOK = rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion, nState, aControlValue, OUString()); 2881 } 2882 2883 if (!bNativeOK) 2884 { 2885 rRenderContext.DrawImage(aPos, *pImg ,nStyle); 2886 } 2887 } 2888 2889 void SvTreeListBox::PreparePaint(vcl::RenderContext& /*rRenderContext*/, SvTreeListEntry& /*rEntry*/) 2890 { 2891 } 2892 2893 tools::Rectangle SvTreeListBox::GetFocusRect( SvTreeListEntry* pEntry, long nLine ) 2894 { 2895 pImpl->UpdateContextBmpWidthMax( pEntry ); 2896 2897 Size aSize; 2898 tools::Rectangle aRect; 2899 aRect.SetTop( nLine ); 2900 aSize.setHeight( GetEntryHeight() ); 2901 2902 long nRealWidth = pImpl->GetOutputSize().Width(); 2903 nRealWidth -= GetMapMode().GetOrigin().X(); 2904 2905 sal_uInt16 nCurTab; 2906 SvLBoxTab* pTab = GetFirstTab( SvLBoxTabFlags::SHOW_SELECTION, nCurTab ); 2907 long nTabPos = 0; 2908 if( pTab ) 2909 nTabPos = GetTabPos( pEntry, pTab ); 2910 long nNextTabPos; 2911 if( pTab && nCurTab < aTabs.size() - 1 ) 2912 { 2913 SvLBoxTab* pNextTab = aTabs[ nCurTab + 1 ].get(); 2914 nNextTabPos = GetTabPos( pEntry, pNextTab ); 2915 } 2916 else 2917 { 2918 nNextTabPos = nRealWidth; 2919 if( nTabPos > nRealWidth ) 2920 nNextTabPos += 50; 2921 } 2922 2923 bool bUserSelection = bool( nTreeFlags & SvTreeFlags::USESEL ); 2924 if( !bUserSelection ) 2925 { 2926 if( pTab && nCurTab < pEntry->ItemCount() ) 2927 { 2928 SvLBoxItem& rItem = pEntry->GetItem( nCurTab ); 2929 aSize.setWidth(rItem.GetWidth(this, pEntry)); 2930 if( !aSize.Width() ) 2931 aSize.setWidth( 15 ); 2932 long nX = nTabPos; //GetTabPos( pEntry, pTab ); 2933 // alignment 2934 nX += pTab->CalcOffset( aSize.Width(), nNextTabPos - nTabPos ); 2935 aRect.SetLeft( nX ); 2936 // make sure that first and last letter aren't cut off slightly 2937 aRect.SetSize( aSize ); 2938 if( aRect.Left() > 0 ) 2939 aRect.AdjustLeft( -1 ); 2940 aRect.AdjustRight( 1 ); 2941 } 2942 } 2943 else 2944 { 2945 // if SelTab != 0, we have to calculate also 2946 if( nFocusWidth == -1 || nFirstSelTab ) 2947 { 2948 SvLBoxTab* pLastTab = nullptr; // default to select whole width 2949 2950 sal_uInt16 nLastTab; 2951 GetLastTab(SvLBoxTabFlags::SHOW_SELECTION,nLastTab); 2952 nLastTab++; 2953 if( nLastTab < aTabs.size() ) // is there another one? 2954 pLastTab = aTabs[ nLastTab ].get(); 2955 2956 aSize.setWidth( pLastTab ? pLastTab->GetPos() : 0x0fffffff ); 2957 nFocusWidth = static_cast<short>(aSize.Width()); 2958 if( pTab ) 2959 nFocusWidth = nFocusWidth - static_cast<short>(nTabPos); //pTab->GetPos(); 2960 } 2961 else 2962 { 2963 aSize.setWidth( nFocusWidth ); 2964 if( pTab ) 2965 { 2966 if( nCurTab ) 2967 aSize.AdjustWidth(nTabPos ); 2968 else 2969 aSize.AdjustWidth(pTab->GetPos() ); // Tab0 always from the leftmost position 2970 } 2971 } 2972 // if selection starts with 0th tab, draw from column 0 on 2973 if( nCurTab != 0 ) 2974 { 2975 aRect.SetLeft( nTabPos ); 2976 aSize.AdjustWidth( -nTabPos ); 2977 } 2978 aRect.SetSize( aSize ); 2979 } 2980 // adjust right edge because of clipping 2981 if( aRect.Right() >= nRealWidth ) 2982 { 2983 aRect.SetRight( nRealWidth-1 ); 2984 nFocusWidth = static_cast<short>(aRect.GetWidth()); 2985 } 2986 return aRect; 2987 } 2988 2989 2990 sal_IntPtr SvTreeListBox::GetTabPos( SvTreeListEntry* pEntry, SvLBoxTab* pTab) 2991 { 2992 assert(pTab); 2993 sal_IntPtr nPos = pTab->GetPos(); 2994 if( pTab->IsDynamic() ) 2995 { 2996 sal_uInt16 nDepth = pModel->GetDepth( pEntry ); 2997 nDepth = nDepth * static_cast<sal_uInt16>(nIndent); 2998 nPos += static_cast<sal_IntPtr>(nDepth); 2999 } 3000 return nPos; 3001 } 3002 3003 SvLBoxItem* SvTreeListBox::GetItem_Impl( SvTreeListEntry* pEntry, long nX, 3004 SvLBoxTab** ppTab ) 3005 { 3006 SvLBoxItem* pItemClicked = nullptr; 3007 sal_uInt16 nTabCount = aTabs.size(); 3008 sal_uInt16 nItemCount = pEntry->ItemCount(); 3009 SvLBoxTab* pTab = aTabs.front().get(); 3010 SvLBoxItem* pItem = &pEntry->GetItem(0); 3011 sal_uInt16 nNextItem = 1; 3012 nX -= GetMapMode().GetOrigin().X(); 3013 long nRealWidth = pImpl->GetOutputSize().Width(); 3014 nRealWidth -= GetMapMode().GetOrigin().X(); 3015 3016 while( true ) 3017 { 3018 SvLBoxTab* pNextTab=nNextItem<nTabCount ? aTabs[nNextItem].get() : nullptr; 3019 long nStart = GetTabPos( pEntry, pTab ); 3020 3021 long nNextTabPos; 3022 if( pNextTab ) 3023 nNextTabPos = GetTabPos( pEntry, pNextTab ); 3024 else 3025 { 3026 nNextTabPos = nRealWidth; 3027 if( nStart > nRealWidth ) 3028 nNextTabPos += 50; 3029 } 3030 3031 auto nItemWidth(pItem->GetWidth(this, pEntry)); 3032 nStart += pTab->CalcOffset(nItemWidth, nNextTabPos - nStart); 3033 auto nLen = nItemWidth; 3034 if( pNextTab ) 3035 { 3036 long nTabWidth = GetTabPos( pEntry, pNextTab ) - nStart; 3037 if( nTabWidth < nLen ) 3038 nLen = nTabWidth; 3039 } 3040 3041 if( nX >= nStart && nX < (nStart+nLen ) ) 3042 { 3043 pItemClicked = pItem; 3044 if( ppTab ) 3045 { 3046 *ppTab = pTab; 3047 break; 3048 } 3049 } 3050 if( nNextItem >= nItemCount || nNextItem >= nTabCount) 3051 break; 3052 pTab = aTabs[ nNextItem ].get(); 3053 pItem = &pEntry->GetItem( nNextItem ); 3054 nNextItem++; 3055 } 3056 return pItemClicked; 3057 } 3058 3059 long SvTreeListBox::getPreferredDimensions(std::vector<long> &rWidths) const 3060 { 3061 long nHeight = 0; 3062 rWidths.clear(); 3063 SvTreeListEntry* pEntry = First(); 3064 while (pEntry) 3065 { 3066 sal_uInt16 nCount = pEntry->ItemCount(); 3067 sal_uInt16 nCurPos = 0; 3068 if (nCount > rWidths.size()) 3069 rWidths.resize(nCount); 3070 while (nCurPos < nCount) 3071 { 3072 SvLBoxItem& rItem = pEntry->GetItem( nCurPos ); 3073 auto nWidth = rItem.GetWidth(this, pEntry); 3074 if (nWidth) 3075 { 3076 nWidth += SV_TAB_BORDER * 2; 3077 if (nWidth > rWidths[nCurPos]) 3078 rWidths[nCurPos] = nWidth; 3079 } 3080 ++nCurPos; 3081 } 3082 pEntry = Next( pEntry ); 3083 nHeight += GetEntryHeight(); 3084 } 3085 return nHeight; 3086 } 3087 3088 Size SvTreeListBox::GetOptimalSize() const 3089 { 3090 std::vector<long> aWidths; 3091 Size aRet(0, getPreferredDimensions(aWidths)); 3092 for (long aWidth : aWidths) 3093 aRet.AdjustWidth(aWidth ); 3094 if (GetStyle() & WB_BORDER) 3095 { 3096 aRet.AdjustWidth(StyleSettings::GetBorderSize() * 2 ); 3097 aRet.AdjustHeight(StyleSettings::GetBorderSize() * 2 ); 3098 } 3099 long nMinWidth = nMinWidthInChars * approximate_char_width(); 3100 aRet.setWidth( std::max(aRet.Width(), nMinWidth) ); 3101 3102 if (GetStyle() & WB_VSCROLL) 3103 aRet.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize()); 3104 3105 return aRet; 3106 } 3107 3108 void SvTreeListBox::SetAlternatingRowColors( bool bEnable ) 3109 { 3110 if( !mbUpdateAlternatingRows ) 3111 { 3112 mbAlternatingRowColors = bEnable; 3113 return; 3114 } 3115 3116 if( bEnable ) 3117 { 3118 SvTreeListEntry* pEntry = pModel->First(); 3119 for(size_t i = 0; pEntry; ++i) 3120 { 3121 pEntry->SetBackColor( i % 2 == 0 ? GetBackground().GetColor() : GetSettings().GetStyleSettings().GetAlternatingRowColor()); 3122 SvTreeListEntry *pNextEntry = nullptr; 3123 if( IsExpanded( pEntry ) ) 3124 pNextEntry = pModel->FirstChild( pEntry ); 3125 else 3126 pNextEntry = pEntry->NextSibling(); 3127 3128 if( !pNextEntry ) 3129 pEntry = pModel->Next( pEntry ); 3130 else 3131 pEntry = pNextEntry; 3132 } 3133 } 3134 else if( mbAlternatingRowColors ) 3135 for(SvTreeListEntry* pEntry = pModel->First(); pEntry; pEntry = pModel->Next(pEntry)) 3136 pEntry->SetBackColor( GetBackground().GetColor() ); 3137 3138 mbAlternatingRowColors = bEnable; 3139 pImpl->UpdateAll(true); 3140 } 3141 3142 void SvTreeListBox::SetForceMakeVisible( bool bEnable ) 3143 { 3144 pImpl->SetForceMakeVisible(bEnable); 3145 } 3146 3147 SvLBoxItem* SvTreeListBox::GetItem(SvTreeListEntry* pEntry,long nX,SvLBoxTab** ppTab) 3148 { 3149 return GetItem_Impl( pEntry, nX, ppTab ); 3150 } 3151 3152 SvLBoxItem* SvTreeListBox::GetItem(SvTreeListEntry* pEntry,long nX ) 3153 { 3154 SvLBoxTab* pDummyTab; 3155 return GetItem_Impl( pEntry, nX, &pDummyTab ); 3156 } 3157 3158 void SvTreeListBox::AddTab(long nTabPos, SvLBoxTabFlags nFlags ) 3159 { 3160 nFocusWidth = -1; 3161 SvLBoxTab* pTab = new SvLBoxTab( nTabPos, nFlags ); 3162 aTabs.emplace_back( pTab ); 3163 if( nTreeFlags & SvTreeFlags::USESEL ) 3164 { 3165 sal_uInt16 nPos = aTabs.size() - 1; 3166 if( nPos >= nFirstSelTab && nPos <= nLastSelTab ) 3167 pTab->nFlags |= SvLBoxTabFlags::SHOW_SELECTION; 3168 else 3169 // string items usually have to be selected -- turn this off 3170 // explicitly 3171 pTab->nFlags &= ~SvLBoxTabFlags::SHOW_SELECTION; 3172 } 3173 } 3174 3175 3176 SvLBoxTab* SvTreeListBox::GetFirstDynamicTab( sal_uInt16& rPos ) const 3177 { 3178 sal_uInt16 nCurTab = 0; 3179 sal_uInt16 nTabCount = aTabs.size(); 3180 while( nCurTab < nTabCount ) 3181 { 3182 SvLBoxTab* pTab = aTabs[nCurTab].get(); 3183 if( pTab->nFlags & SvLBoxTabFlags::DYNAMIC ) 3184 { 3185 rPos = nCurTab; 3186 return pTab; 3187 } 3188 nCurTab++; 3189 } 3190 return nullptr; 3191 } 3192 3193 SvLBoxTab* SvTreeListBox::GetFirstDynamicTab() const 3194 { 3195 sal_uInt16 nDummy; 3196 return GetFirstDynamicTab( nDummy ); 3197 } 3198 3199 SvLBoxTab* SvTreeListBox::GetTab( SvTreeListEntry const * pEntry, SvLBoxItem const * pItem) const 3200 { 3201 sal_uInt16 nPos = pEntry->GetPos( pItem ); 3202 return aTabs[ nPos ].get(); 3203 } 3204 3205 void SvTreeListBox::ClearTabList() 3206 { 3207 aTabs.clear(); 3208 } 3209 3210 3211 Size SvTreeListBox::GetOutputSizePixel() const 3212 { 3213 Size aSize = pImpl->GetOutputSize(); 3214 return aSize; 3215 } 3216 3217 void SvTreeListBox::NotifyEndScroll() 3218 { 3219 } 3220 3221 void SvTreeListBox::NotifyScrolled() 3222 { 3223 aScrolledHdl.Call( this ); 3224 } 3225 3226 void SvTreeListBox::Invalidate( InvalidateFlags nInvalidateFlags ) 3227 { 3228 if (!pImpl) 3229 return; 3230 if( nFocusWidth == -1 ) 3231 // to make sure that the control doesn't show the wrong focus rectangle 3232 // after painting 3233 pImpl->RecalcFocusRect(); 3234 Control::Invalidate( nInvalidateFlags ); 3235 pImpl->Invalidate(); 3236 } 3237 3238 void SvTreeListBox::Invalidate( const tools::Rectangle& rRect, InvalidateFlags nInvalidateFlags ) 3239 { 3240 if( nFocusWidth == -1 ) 3241 // to make sure that the control doesn't show the wrong focus rectangle 3242 // after painting 3243 pImpl->RecalcFocusRect(); 3244 Control::Invalidate( rRect, nInvalidateFlags ); 3245 } 3246 3247 3248 void SvTreeListBox::SetHighlightRange( sal_uInt16 nStart, sal_uInt16 nEnd) 3249 { 3250 3251 sal_uInt16 nTemp; 3252 nTreeFlags |= SvTreeFlags::USESEL; 3253 if( nStart > nEnd ) 3254 { 3255 nTemp = nStart; 3256 nStart = nEnd; 3257 nEnd = nTemp; 3258 } 3259 // select all tabs that lie within the area 3260 nTreeFlags |= SvTreeFlags::RECALCTABS; 3261 nFirstSelTab = nStart; 3262 nLastSelTab = nEnd; 3263 pImpl->RecalcFocusRect(); 3264 } 3265 3266 void SvTreeListBox::Command(const CommandEvent& rCEvt) 3267 { 3268 if (!aPopupMenuHdl.Call(rCEvt)) 3269 pImpl->Command(rCEvt); 3270 //pass at least alt press/release to parent impl 3271 if (rCEvt.GetCommand() == CommandEventId::ModKeyChange) 3272 Control::Command(rCEvt); 3273 } 3274 3275 void SvTreeListBox::RemoveParentKeepChildren( SvTreeListEntry* pParent ) 3276 { 3277 assert(pParent); 3278 SvTreeListEntry* pNewParent = GetParent( pParent ); 3279 if( pParent->HasChildren()) 3280 { 3281 SvTreeListEntry* pChild = FirstChild( pParent ); 3282 while( pChild ) 3283 { 3284 pModel->Move( pChild, pNewParent, TREELIST_APPEND ); 3285 pChild = FirstChild( pParent ); 3286 } 3287 } 3288 pModel->Remove( pParent ); 3289 } 3290 3291 SvLBoxTab* SvTreeListBox::GetFirstTab( SvLBoxTabFlags nFlagMask, sal_uInt16& rPos ) 3292 { 3293 sal_uInt16 nTabCount = aTabs.size(); 3294 for( sal_uInt16 nPos = 0; nPos < nTabCount; nPos++ ) 3295 { 3296 SvLBoxTab* pTab = aTabs[ nPos ].get(); 3297 if( pTab->nFlags & nFlagMask ) 3298 { 3299 rPos = nPos; 3300 return pTab; 3301 } 3302 } 3303 rPos = 0xffff; 3304 return nullptr; 3305 } 3306 3307 void SvTreeListBox::GetLastTab( SvLBoxTabFlags nFlagMask, sal_uInt16& rTabPos ) 3308 { 3309 sal_uInt16 nPos = static_cast<sal_uInt16>(aTabs.size()); 3310 while( nPos ) 3311 { 3312 --nPos; 3313 SvLBoxTab* pTab = aTabs[ nPos ].get(); 3314 if( pTab->nFlags & nFlagMask ) 3315 { 3316 rTabPos = nPos; 3317 return; 3318 } 3319 } 3320 rTabPos = 0xffff; 3321 } 3322 3323 void SvTreeListBox::RequestHelp( const HelpEvent& rHEvt ) 3324 { 3325 if (aTooltipHdl.IsSet() && aTooltipHdl.Call(rHEvt)) 3326 return; 3327 3328 if( !pImpl->RequestHelp( rHEvt ) ) 3329 Control::RequestHelp( rHEvt ); 3330 } 3331 3332 sal_Int32 SvTreeListBox::DefaultCompare(const SvLBoxString* pLeftText, const SvLBoxString* pRightText) 3333 { 3334 OUString aLeft = pLeftText ? pLeftText->GetText() : OUString(); 3335 OUString aRight = pRightText ? pRightText->GetText() : OUString(); 3336 pImpl->UpdateStringSorter(); 3337 return pImpl->m_pStringSorter->compare(aLeft, aRight); 3338 } 3339 3340 IMPL_LINK( SvTreeListBox, DefaultCompare, const SvSortData&, rData, sal_Int32 ) 3341 { 3342 const SvTreeListEntry* pLeft = rData.pLeft; 3343 const SvTreeListEntry* pRight = rData.pRight; 3344 const SvLBoxString* pLeftText = static_cast<const SvLBoxString*>(pLeft->GetFirstItem(SvLBoxItemType::String)); 3345 const SvLBoxString* pRightText = static_cast<const SvLBoxString*>(pRight->GetFirstItem(SvLBoxItemType::String)); 3346 return DefaultCompare(pLeftText, pRightText); 3347 } 3348 3349 void SvTreeListBox::ModelNotification( SvListAction nActionId, SvTreeListEntry* pEntry1, 3350 SvTreeListEntry* pEntry2, sal_uLong nPos ) 3351 { 3352 SolarMutexGuard aSolarGuard; 3353 3354 if( nActionId == SvListAction::CLEARING ) 3355 CancelTextEditing(); 3356 3357 SvListView::ModelNotification( nActionId, pEntry1, pEntry2, nPos ); 3358 switch( nActionId ) 3359 { 3360 case SvListAction::INSERTED: 3361 { 3362 SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry1->GetFirstItem( SvLBoxItemType::ContextBmp ) ); 3363 if ( !pBmpItem ) 3364 break; 3365 const Image& rBitmap1( pBmpItem->GetBitmap1() ); 3366 const Image& rBitmap2( pBmpItem->GetBitmap2() ); 3367 short nMaxWidth = short( std::max( rBitmap1.GetSizePixel().Width(), rBitmap2.GetSizePixel().Width() ) ); 3368 nMaxWidth = pImpl->UpdateContextBmpWidthVector( pEntry1, nMaxWidth ); 3369 if( nMaxWidth > nContextBmpWidthMax ) 3370 { 3371 nContextBmpWidthMax = nMaxWidth; 3372 SetTabs(); 3373 } 3374 if (get_width_request() == -1) 3375 queue_resize(); 3376 } 3377 break; 3378 3379 case SvListAction::RESORTING: 3380 SetUpdateMode( false ); 3381 break; 3382 3383 case SvListAction::RESORTED: 3384 // after a selection: show first entry and also keep the selection 3385 MakeVisible( pModel->First(), true ); 3386 SetUpdateMode( true ); 3387 break; 3388 3389 case SvListAction::CLEARED: 3390 if( IsUpdateMode() ) 3391 Update(); 3392 break; 3393 3394 default: break; 3395 } 3396 } 3397 3398 void SvTreeListBox::EndSelection() 3399 { 3400 pImpl->EndSelection(); 3401 } 3402 3403 ScrollBar *SvTreeListBox::GetVScroll() 3404 { 3405 return pImpl->m_aVerSBar.get(); 3406 } 3407 3408 ScrollBar *SvTreeListBox::GetHScroll() 3409 { 3410 return pImpl->m_aHorSBar.get(); 3411 } 3412 3413 void SvTreeListBox::EnableAsyncDrag( bool b ) 3414 { 3415 pImpl->EnableAsyncDrag( b ); 3416 } 3417 3418 SvTreeListEntry* SvTreeListBox::GetFirstEntryInView() const 3419 { 3420 return GetEntry( Point() ); 3421 } 3422 3423 SvTreeListEntry* SvTreeListBox::GetNextEntryInView(SvTreeListEntry* pEntry ) const 3424 { 3425 SvTreeListEntry* pNext = NextVisible( pEntry ); 3426 if( pNext ) 3427 { 3428 Point aPos( GetEntryPosition(pNext) ); 3429 const Size& rSize = pImpl->GetOutputSize(); 3430 if( aPos.Y() < 0 || aPos.Y() >= rSize.Height() ) 3431 return nullptr; 3432 } 3433 return pNext; 3434 } 3435 3436 SvTreeListEntry* SvTreeListBox::GetPrevEntryInView(SvTreeListEntry* pEntry ) const 3437 { 3438 SvTreeListEntry* pPrev = PrevVisible( pEntry ); 3439 if( pPrev ) 3440 { 3441 Point aPos( GetEntryPosition(pPrev) ); 3442 const Size& rSize = pImpl->GetOutputSize(); 3443 if( aPos.Y() < 0 || aPos.Y() >= rSize.Height() ) 3444 return nullptr; 3445 } 3446 return pPrev; 3447 } 3448 3449 SvTreeListEntry* SvTreeListBox::GetLastEntryInView() const 3450 { 3451 SvTreeListEntry* pEntry = GetFirstEntryInView(); 3452 SvTreeListEntry* pNext = nullptr; 3453 while( pEntry ) 3454 { 3455 pNext = NextVisible( pEntry ); 3456 if( pNext ) 3457 { 3458 Point aPos( GetEntryPosition(pNext) ); 3459 const Size& rSize = pImpl->GetOutputSize(); 3460 if( aPos.Y() < 0 || aPos.Y() + GetEntryHeight() >= rSize.Height() ) 3461 break; 3462 else 3463 pEntry = pNext; 3464 } 3465 else 3466 break; 3467 } 3468 return pEntry; 3469 } 3470 3471 void SvTreeListBox::ShowFocusRect( const SvTreeListEntry* pEntry ) 3472 { 3473 pImpl->ShowFocusRect( pEntry ); 3474 } 3475 3476 void SvTreeListBox::DataChanged( const DataChangedEvent& rDCEvt ) 3477 { 3478 if( (rDCEvt.GetType()==DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) 3479 { 3480 nEntryHeight = 0; // _together_ with true of 1. par (bFont) of InitSettings() a zero-height 3481 // forces complete recalc of heights! 3482 InitSettings(); 3483 Invalidate(); 3484 } 3485 else 3486 Control::DataChanged( rDCEvt ); 3487 } 3488 3489 void SvTreeListBox::StateChanged( StateChangedType eType ) 3490 { 3491 if( eType == StateChangedType::Enable ) 3492 Invalidate( InvalidateFlags::Children ); 3493 3494 Control::StateChanged( eType ); 3495 3496 if ( eType == StateChangedType::Style ) 3497 ImplInitStyle(); 3498 } 3499 3500 void SvTreeListBox::ApplySettings(vcl::RenderContext& rRenderContext) 3501 { 3502 SetPointFont(rRenderContext, GetPointFont(*this)); 3503 3504 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); 3505 rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor()); 3506 rRenderContext.SetTextFillColor(); 3507 rRenderContext.SetBackground(rStyleSettings.GetFieldColor()); 3508 3509 // always try to re-create default-SvLBoxButtonData 3510 if (pCheckButtonData && pCheckButtonData->HasDefaultImages()) 3511 pCheckButtonData->SetDefaultImages(this); 3512 } 3513 3514 void SvTreeListBox::InitSettings() 3515 { 3516 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); 3517 vcl::Font aFont = rStyleSettings.GetFieldFont(); 3518 aFont.SetColor(rStyleSettings.GetWindowTextColor()); 3519 SetPointFont(*this, aFont); 3520 AdjustEntryHeightAndRecalc(); 3521 3522 SetTextColor(rStyleSettings.GetFieldTextColor()); 3523 SetTextFillColor(); 3524 3525 SetBackground(rStyleSettings.GetFieldColor()); 3526 3527 // always try to re-create default-SvLBoxButtonData 3528 if( pCheckButtonData && pCheckButtonData->HasDefaultImages() ) 3529 pCheckButtonData->SetDefaultImages(this); 3530 } 3531 3532 sal_uInt16 SvTreeListBox::GetCurrentTabPos() const 3533 { 3534 return pImpl->GetCurrentTabPos(); 3535 } 3536 3537 VclPtr<PopupMenu> SvTreeListBox::CreateContextMenu() 3538 { 3539 return nullptr; 3540 } 3541 3542 void SvTreeListBox::ExecuteContextMenuAction( sal_uInt16 ) 3543 { 3544 SAL_INFO( "svtools.contnr", "SvTreeListBox::ExecuteContextMenuAction(): now there's happening nothing!" ); 3545 } 3546 3547 void SvTreeListBox::EnableContextMenuHandling() 3548 { 3549 assert(pImpl && "-SvTreeListBox::EnableContextMenuHandling(): No implementation!"); 3550 pImpl->m_bContextMenuHandling = true; 3551 } 3552 3553 css::uno::Reference< XAccessible > SvTreeListBox::CreateAccessible() 3554 { 3555 vcl::Window* pParent = GetAccessibleParentWindow(); 3556 DBG_ASSERT( pParent, "SvTreeListBox::CreateAccessible - accessible parent not found" ); 3557 3558 css::uno::Reference< XAccessible > xAccessible; 3559 if ( pParent ) 3560 { 3561 css::uno::Reference< XAccessible > xAccParent = pParent->GetAccessible(); 3562 if ( xAccParent.is() ) 3563 { 3564 // need to be done here to get the vclxwindow later on in the accessible 3565 css::uno::Reference< css::awt::XWindowPeer > xTemp(GetComponentInterface()); 3566 xAccessible = pImpl->m_aFactoryAccess.getFactory().createAccessibleTreeListBox( *this, xAccParent ); 3567 } 3568 } 3569 return xAccessible; 3570 } 3571 3572 void SvTreeListBox::FillAccessibleEntryStateSet( SvTreeListEntry* pEntry, ::utl::AccessibleStateSetHelper& rStateSet ) const 3573 { 3574 assert(pEntry && "SvTreeListBox::FillAccessibleEntryStateSet: invalid entry"); 3575 3576 if ( pEntry->HasChildrenOnDemand() || pEntry->HasChildren() ) 3577 { 3578 rStateSet.AddState( AccessibleStateType::EXPANDABLE ); 3579 if ( IsExpanded( pEntry ) ) 3580 rStateSet.AddState( sal_Int16(AccessibleStateType::EXPANDED) ); 3581 } 3582 3583 if ( GetCheckButtonState( pEntry ) == SvButtonState::Checked ) 3584 rStateSet.AddState( AccessibleStateType::CHECKED ); 3585 if ( IsEntryVisible( pEntry ) ) 3586 rStateSet.AddState( AccessibleStateType::VISIBLE ); 3587 if ( IsSelected( pEntry ) ) 3588 rStateSet.AddState( AccessibleStateType::SELECTED ); 3589 if ( IsEnabled() ) 3590 { 3591 rStateSet.AddState( AccessibleStateType::ENABLED ); 3592 rStateSet.AddState( AccessibleStateType::FOCUSABLE ); 3593 rStateSet.AddState( AccessibleStateType::SELECTABLE ); 3594 SvViewDataEntry* pViewDataNewCur = GetViewDataEntry(pEntry); 3595 if (pViewDataNewCur && pViewDataNewCur->HasFocus()) 3596 rStateSet.AddState( AccessibleStateType::FOCUSED ); 3597 } 3598 } 3599 3600 tools::Rectangle SvTreeListBox::GetBoundingRect( SvTreeListEntry* pEntry ) 3601 { 3602 Point aPos = GetEntryPosition( pEntry ); 3603 tools::Rectangle aRect = GetFocusRect( pEntry, aPos.Y() ); 3604 return aRect; 3605 } 3606 3607 void SvTreeListBox::CallImplEventListeners(VclEventId nEvent, void* pData) 3608 { 3609 CallEventListeners(nEvent, pData); 3610 } 3611 3612 void SvTreeListBox::set_min_width_in_chars(sal_Int32 nChars) 3613 { 3614 nMinWidthInChars = nChars; 3615 queue_resize(); 3616 } 3617 3618 bool SvTreeListBox::set_property(const OString &rKey, const OUString &rValue) 3619 { 3620 if (rKey == "min-width-chars") 3621 { 3622 set_min_width_in_chars(rValue.toInt32()); 3623 } 3624 else if (rKey == "enable-tree-lines") 3625 { 3626 auto nStyle = GetStyle(); 3627 nStyle &= ~(WB_HASLINES | WB_HASLINESATROOT); 3628 if (toBool(rValue)) 3629 nStyle |= (WB_HASLINES | WB_HASLINESATROOT); 3630 SetStyle(nStyle); 3631 } 3632 else if (rKey == "show-expanders") 3633 { 3634 auto nStyle = GetStyle(); 3635 nStyle &= ~(WB_HASBUTTONS | WB_HASBUTTONSATROOT); 3636 if (toBool(rValue)) 3637 nStyle |= (WB_HASBUTTONS | WB_HASBUTTONSATROOT); 3638 SetStyle(nStyle); 3639 } 3640 else if (rKey == "rules-hint") 3641 { 3642 SetAlternatingRowColors(toBool(rValue)); 3643 } 3644 else if (rKey == "enable-search") 3645 { 3646 SetQuickSearch(toBool(rValue)); 3647 } 3648 else if (rKey == "reorderable") 3649 { 3650 if (toBool(rValue)) 3651 SetDragDropMode(DragDropMode::CTRL_MOVE | DragDropMode::ENABLE_TOP); 3652 } 3653 else 3654 return Control::set_property(rKey, rValue); 3655 return true; 3656 } 3657 3658 FactoryFunction SvTreeListBox::GetUITestFactory() const 3659 { 3660 return TreeListUIObject::create; 3661 } 3662 3663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 3664
