1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_svtools.hxx" 26 27 #define _TREELIST_CXX 28 29 #ifndef GCC 30 #endif 31 32 #include <svtools/treelist.hxx> 33 34 #ifdef DBG_UTIL 35 // Prueft Integritaet der Liste nach jeder Operation 36 //#define CHECK_INTEGRITY 37 #endif 38 39 40 DBG_NAME(SvListEntry); 41 42 SvListEntry::SvListEntry() 43 { 44 DBG_CTOR(SvListEntry,0); 45 pChilds = 0; 46 pParent = 0; 47 nListPos = 0; 48 nAbsPos = 0; 49 } 50 51 SvListEntry::SvListEntry( const SvListEntry& rEntry ) 52 { 53 DBG_CTOR(SvListEntry,0); 54 pChilds = 0; 55 pParent = 0; 56 nListPos &= 0x80000000; 57 nListPos |= ( rEntry.nListPos & 0x7fffffff); 58 nAbsPos = rEntry.nAbsPos; 59 } 60 61 SvListEntry::~SvListEntry() 62 { 63 DBG_DTOR(SvListEntry,0); 64 if ( pChilds ) 65 { 66 pChilds->DestroyAll(); 67 delete pChilds; 68 } 69 #ifdef DBG_UTIL 70 pChilds = 0; 71 pParent = 0; 72 #endif 73 } 74 75 void SvListEntry::Clone( SvListEntry* pSource) 76 { 77 DBG_CHKTHIS(SvListEntry,0); 78 nListPos &= 0x80000000; 79 nListPos |= ( pSource->nListPos & 0x7fffffff); 80 nAbsPos = pSource->nAbsPos; 81 } 82 83 void SvListEntry::SetListPositions() 84 { 85 if( pChilds ) 86 { 87 SvListEntry *pEntry = (SvListEntry*)pChilds->First(); 88 sal_uLong nCur = 0; 89 while ( pEntry ) 90 { 91 pEntry->nListPos &= 0x80000000; 92 pEntry->nListPos |= nCur; 93 nCur++; 94 pEntry = (SvListEntry*)pChilds->Next(); 95 } 96 } 97 nListPos &= (~0x80000000); 98 } 99 100 101 DBG_NAME(SvViewData); 102 103 SvViewData::SvViewData() 104 { 105 DBG_CTOR(SvViewData,0); 106 nFlags = 0; 107 nVisPos = 0; 108 } 109 110 SvViewData::SvViewData( const SvViewData& rData ) 111 { 112 DBG_CTOR(SvViewData,0); 113 nFlags = rData.nFlags; 114 nFlags &= ~( SVLISTENTRYFLAG_SELECTED | SVLISTENTRYFLAG_FOCUSED ); 115 nVisPos = rData.nVisPos; 116 } 117 118 SvViewData::~SvViewData() 119 { 120 DBG_DTOR(SvViewData,0); 121 #ifdef DBG_UTIL 122 nVisPos = 0x12345678; 123 nFlags = 0x1234; 124 #endif 125 } 126 127 void SvTreeEntryList::DestroyAll() 128 { 129 SvListEntry* pPtr = (SvListEntry*)First(); 130 while( pPtr ) 131 { 132 delete pPtr; 133 pPtr = (SvListEntry*)Next(); 134 } 135 } 136 137 138 /************************************************************************* 139 |* 140 |* SvTreeList:: 141 |* 142 |* Beschreibung 143 |* Ersterstellung 17.08.94 144 |* Letzte Aenderung 17.08.94 145 |* 146 *************************************************************************/ 147 148 SvTreeList::SvTreeList() 149 { 150 nEntryCount = 0; 151 bAbsPositionsValid = sal_False; 152 nRefCount = 1; 153 pRootItem = new SvListEntry; 154 eSortMode = SortNone; 155 } 156 157 158 /************************************************************************* 159 |* 160 |* SvTreeList::~SvTreeList 161 |* 162 |* Beschreibung 163 |* Ersterstellung 17.08.94 164 |* Letzte Aenderung 17.08.94 165 |* 166 *************************************************************************/ 167 168 SvTreeList::~SvTreeList() 169 { 170 Clear(); 171 delete pRootItem; 172 #ifdef DBG_UTIL 173 pRootItem = 0; 174 #endif 175 } 176 177 /************************************************************************* 178 |* 179 |* SvTreeList::Broadcast 180 |* 181 |* Beschreibung 182 |* Ersterstellung 17.08.94 183 |* Letzte Aenderung 17.08.94 184 |* 185 *************************************************************************/ 186 187 void SvTreeList::Broadcast( sal_uInt16 nActionId, SvListEntry* pEntry1, 188 SvListEntry* pEntry2, sal_uLong nPos ) 189 { 190 sal_uLong nViewCount = aViewList.Count(); 191 for( sal_uLong nCurView = 0; nCurView < nViewCount; nCurView++ ) 192 { 193 SvListView* pView = (SvListView*)aViewList.GetObject( nCurView ); 194 if( pView ) 195 pView->ModelNotification( nActionId, pEntry1, pEntry2, nPos ); 196 } 197 } 198 199 void SvTreeList::InsertView( SvListView* pView) 200 { 201 sal_uLong nPos = aViewList.GetPos( pView ); 202 if ( nPos == LIST_ENTRY_NOTFOUND ) 203 { 204 aViewList.Insert( pView, LIST_APPEND ); 205 nRefCount++; 206 } 207 } 208 209 void SvTreeList::RemoveView( SvListView* pView ) 210 { 211 sal_uLong nPos = aViewList.GetPos( pView ); 212 if ( nPos != LIST_ENTRY_NOTFOUND ) 213 { 214 aViewList.Remove( pView ); 215 nRefCount--; 216 } 217 } 218 219 220 // Ein Entry ist sichtbar, wenn alle Parents expandiert sind 221 sal_Bool SvTreeList::IsEntryVisible( const SvListView* pView, SvListEntry* pEntry ) const 222 { 223 DBG_ASSERT(pView&&pEntry,"IsVisible:Invalid Params"); 224 sal_Bool bRetVal=sal_False; 225 do 226 { 227 if ( pEntry == pRootItem ) 228 { 229 bRetVal=sal_True; 230 break; 231 } 232 pEntry = pEntry->pParent; 233 } while( pView->IsExpanded( pEntry ) ); 234 return bRetVal; 235 } 236 237 sal_uInt16 SvTreeList::GetDepth( SvListEntry* pEntry ) const 238 { 239 DBG_ASSERT(pEntry&&pEntry!=pRootItem,"GetDepth:Bad Entry"); 240 sal_uInt16 nDepth = 0; 241 while( pEntry->pParent != pRootItem ) 242 { 243 nDepth++; 244 pEntry = pEntry->pParent; 245 } 246 return nDepth; 247 } 248 249 /************************************************************************* 250 |* 251 |* SvTreeList:: 252 |* 253 |* Beschreibung 254 |* Ersterstellung 17.08.94 255 |* Letzte Aenderung 17.08.94 256 |* 257 *************************************************************************/ 258 259 void SvTreeList::Clear() 260 { 261 Broadcast( LISTACTION_CLEARING ); 262 SvTreeEntryList* pRootList = pRootItem->pChilds; 263 if ( pRootList ) 264 { 265 SvListEntry* pEntry = (SvListEntry*)(pRootList->First()); 266 while( pEntry ) 267 { 268 delete pEntry; 269 pEntry = (SvListEntry*)(pRootList->Next()); 270 } 271 delete pRootItem->pChilds; 272 pRootItem->pChilds = 0; 273 } 274 nEntryCount = 0; 275 Broadcast( LISTACTION_CLEARED ); 276 } 277 278 279 /************************************************************************* 280 |* 281 |* SvTreeList:: 282 |* 283 |* Beschreibung 284 |* Ersterstellung 17.08.94 285 |* Letzte Aenderung 17.08.94 286 |* 287 *************************************************************************/ 288 289 sal_Bool SvTreeList::IsChild( SvListEntry* pParent, SvListEntry* pChild ) const 290 { 291 if ( !pParent ) 292 pParent = pRootItem; 293 294 sal_Bool bIsChild = sal_False; 295 SvTreeEntryList* pList = pParent->pChilds; 296 if ( !pList ) 297 return sal_False; 298 SvListEntry* pActualChild = (SvListEntry*)(pList->First()); 299 while( !bIsChild && pActualChild ) 300 { 301 if ( pActualChild == pChild ) 302 bIsChild = sal_True; 303 else 304 { 305 if ( pActualChild->pChilds ) 306 bIsChild = IsChild( pActualChild, pChild ); 307 pActualChild = (SvListEntry*)(pList->Next()); 308 } 309 } 310 return bIsChild; 311 } 312 313 sal_uLong SvTreeList::Move(SvListEntry* pSrcEntry,SvListEntry* pTargetParent,sal_uLong nListPos) 314 { 315 // pDest darf Null sein! 316 DBG_ASSERT(pSrcEntry,"Entry?"); 317 if ( !pTargetParent ) 318 pTargetParent = pRootItem; 319 DBG_ASSERT(pSrcEntry!=pTargetParent,"Move:Source=Target"); 320 321 Broadcast( LISTACTION_MOVING, pSrcEntry, pTargetParent, nListPos ); 322 323 if ( !pTargetParent->pChilds ) 324 pTargetParent->pChilds = new SvTreeEntryList; 325 if ( pSrcEntry == pTargetParent ) 326 return pSrcEntry->GetChildListPos(); 327 328 bAbsPositionsValid = sal_False; 329 330 SvTreeEntryList* pDstList = pTargetParent->pChilds; 331 SvTreeEntryList* pSrcList = pSrcEntry->pParent->pChilds; 332 333 // Dummy-Ptr einfuegen, weil nListPos durch das 334 // folgende Remove ungueltig werden koennte 335 SvListEntry* pDummy = 0; pDstList->Insert( pDummy, nListPos ); 336 337 // loeschen 338 pSrcList->Remove( pSrcEntry ); 339 // Hat Parent noch Childs ? 340 if ( pSrcList->Count() == 0 ) 341 { 342 // Keine Childs, deshalb Child-List loeschen 343 SvListEntry* pParent = pSrcEntry->pParent; 344 pParent->pChilds = 0; 345 delete pSrcList; 346 pSrcList = 0; 347 } 348 349 // Parent umsetzen (erst hier, weil wir zum Loeschen 350 // der ChildList den alten Parent noch benoetigen!) 351 pSrcEntry->pParent = pTargetParent; 352 353 pDstList->Replace( pSrcEntry, pDummy ); 354 355 // Listenpositionen in Zielliste korrigieren 356 SetListPositions( pDstList ); 357 if ( pSrcList && (sal_uLong)pSrcList != (sal_uLong)pDstList ) 358 SetListPositions( pSrcList ); 359 360 #ifdef CHECK_INTEGRITY 361 CheckIntegrity(); 362 #endif 363 364 sal_uLong nRetVal = pDstList->GetPos( pSrcEntry ); 365 DBG_ASSERT(nRetVal==pSrcEntry->GetChildListPos(),"ListPos not valid"); 366 Broadcast( LISTACTION_MOVED,pSrcEntry,pTargetParent,nRetVal); 367 return nRetVal; 368 } 369 370 sal_uLong SvTreeList::Copy(SvListEntry* pSrcEntry,SvListEntry* pTargetParent,sal_uLong nListPos) 371 { 372 // pDest darf Null sein! 373 DBG_ASSERT(pSrcEntry,"Entry?"); 374 if ( !pTargetParent ) 375 pTargetParent = pRootItem; 376 if ( !pTargetParent->pChilds ) 377 pTargetParent->pChilds = new SvTreeEntryList; 378 379 bAbsPositionsValid = sal_False; 380 381 sal_uLong nCloneCount = 0; 382 SvListEntry* pClonedEntry = Clone( pSrcEntry, nCloneCount ); 383 nEntryCount += nCloneCount; 384 385 SvTreeEntryList* pDstList = pTargetParent->pChilds; 386 pClonedEntry->pParent = pTargetParent; // Parent umsetzen 387 pDstList->Insert( pClonedEntry, nListPos ); // Einfuegen 388 SetListPositions( pDstList ); // Listenpositionen in Zielliste korrigieren 389 390 #ifdef CHECK_INTEGRITY 391 CheckIntegrity(); 392 #endif 393 Broadcast( LISTACTION_INSERTED_TREE, pClonedEntry ); 394 sal_uLong nRetVal = pDstList->GetPos( pClonedEntry ); 395 return nRetVal; 396 } 397 398 399 400 /************************************************************************* 401 |* 402 |* SvTreeList:: 403 |* 404 |* Beschreibung 405 |* Ersterstellung 17.08.94 406 |* Letzte Aenderung 17.08.94 407 |* 408 *************************************************************************/ 409 410 void SvTreeList::Move( SvListEntry* pSrcEntry, SvListEntry* pDstEntry ) 411 { 412 SvListEntry* pParent; 413 sal_uLong nPos; 414 415 if ( !pDstEntry ) 416 { 417 pParent = pRootItem; 418 nPos = 0UL; 419 } 420 else 421 { 422 pParent = pDstEntry->pParent; 423 nPos = pDstEntry->GetChildListPos(); 424 nPos++; // UNTER (Bildschirm) pDstEntry einfuegen 425 } 426 Move( pSrcEntry, pParent, nPos ); 427 } 428 429 /************************************************************************* 430 |* 431 |* SvTreeList:: 432 |* 433 |* Beschreibung 434 |* Ersterstellung 17.08.94 435 |* Letzte Aenderung 17.08.94 436 |* 437 *************************************************************************/ 438 439 void SvTreeList::Copy( SvListEntry* pSrcEntry, SvListEntry* pDstEntry ) 440 { 441 SvListEntry* pParent; 442 sal_uLong nPos; 443 444 if ( !pDstEntry ) 445 { 446 pParent = pRootItem; 447 nPos = 0UL; 448 } 449 else 450 { 451 pParent = pDstEntry->pParent; 452 nPos = pDstEntry->GetChildListPos()+1; 453 } 454 Copy( pSrcEntry, pParent, nPos ); 455 } 456 457 /************************************************************************* 458 |* 459 |* SvTreeList:: 460 |* 461 |* Beschreibung 462 |* Ersterstellung 17.08.94 463 |* Letzte Aenderung 17.08.94 464 |* 465 *************************************************************************/ 466 void SvTreeList::InsertTree( SvListEntry* pSrcEntry, SvListEntry* pDstEntry) 467 { 468 SvListEntry* pParent; 469 sal_uLong nPos; 470 471 if ( !pDstEntry ) 472 { 473 pParent = pRootItem; 474 nPos = 0UL; 475 } 476 else 477 { 478 pParent = pDstEntry->pParent; 479 nPos = pDstEntry->GetChildListPos()+1; 480 } 481 InsertTree( pSrcEntry, pParent, nPos ); 482 } 483 484 485 void SvTreeList::InsertTree(SvListEntry* pSrcEntry, 486 SvListEntry* pTargetParent,sal_uLong nListPos) 487 { 488 DBG_ASSERT(pSrcEntry,"InsertTree:Entry?"); 489 if ( !pSrcEntry ) 490 return; 491 492 if ( !pTargetParent ) 493 pTargetParent = pRootItem; 494 if ( !pTargetParent->pChilds ) 495 pTargetParent->pChilds = new SvTreeEntryList; 496 497 // Sortierung beruecksichtigen 498 GetInsertionPos( pSrcEntry, pTargetParent, nListPos ); 499 500 bAbsPositionsValid = sal_False; 501 502 pSrcEntry->pParent = pTargetParent; // Parent umsetzen 503 SvTreeEntryList* pDstList = pTargetParent->pChilds; 504 pDstList->Insert( pSrcEntry, nListPos ); // einfuegen 505 SetListPositions(pDstList); // Listenpositionen in Zielliste korrigieren 506 nEntryCount += GetChildCount( pSrcEntry ); 507 nEntryCount++; // der Parent ist ja auch neu 508 509 #ifdef CHECK_INTEGRITY 510 CheckIntegrity(); 511 #endif 512 Broadcast(LISTACTION_INSERTED_TREE, pSrcEntry ); 513 } 514 515 SvListEntry* SvTreeList::CloneEntry( SvListEntry* pSource ) const 516 { 517 if( aCloneLink.IsSet() ) 518 return (SvListEntry*)aCloneLink.Call( pSource ); 519 SvListEntry* pEntry = CreateEntry(); 520 pSource->Clone( pEntry ); 521 return pSource; 522 } 523 524 SvListEntry* SvTreeList::CreateEntry() const 525 { 526 return new SvListEntry; 527 } 528 529 /************************************************************************* 530 |* 531 |* SvTreeList:: 532 |* 533 |* Beschreibung 534 |* Ersterstellung 17.08.94 535 |* Letzte Aenderung 17.08.94 536 |* 537 *************************************************************************/ 538 539 SvListEntry* SvTreeList::Clone( SvListEntry* pEntry, sal_uLong& nCloneCount ) const 540 { 541 SvListEntry* pClonedEntry = CloneEntry( pEntry ); 542 nCloneCount = 1; 543 SvTreeEntryList* pChilds = pEntry->pChilds; 544 if ( pChilds ) 545 pClonedEntry->pChilds=CloneChilds(pChilds,pClonedEntry,nCloneCount); 546 return pClonedEntry; 547 } 548 549 /************************************************************************* 550 |* 551 |* SvTreeList:: 552 |* 553 |* Beschreibung 554 |* Ersterstellung 17.08.94 555 |* Letzte Aenderung 17.08.94 556 |* 557 *************************************************************************/ 558 559 SvTreeEntryList* SvTreeList::CloneChilds( SvTreeEntryList* pChilds, 560 SvListEntry* pNewParent, 561 sal_uLong& nCloneCount ) const 562 { 563 DBG_ASSERT(pChilds->Count(),"Childs?"); 564 SvTreeEntryList* pClonedChilds = new SvTreeEntryList; 565 SvListEntry* pChild = (SvListEntry*)pChilds->First(); 566 while ( pChild ) 567 { 568 SvListEntry* pNewChild = CloneEntry( pChild ); 569 nCloneCount++; 570 pNewChild->pParent = pNewParent; 571 SvTreeEntryList* pSubChilds = pChild->pChilds; 572 if ( pSubChilds ) 573 { 574 pSubChilds = CloneChilds( pSubChilds, pNewChild, nCloneCount ); 575 pNewChild->pChilds = pSubChilds; 576 } 577 578 pClonedChilds->Insert( pNewChild, LIST_APPEND ); 579 pChild = (SvListEntry*)pChilds->Next(); 580 } 581 return pClonedChilds; 582 } 583 584 585 /************************************************************************* 586 |* 587 |* SvTreeList::GetChildCount 588 |* 589 |* Beschreibung 590 |* Ersterstellung 17.08.94 591 |* Letzte Aenderung 17.08.94 592 |* 593 *************************************************************************/ 594 595 sal_uLong SvTreeList::GetChildCount( SvListEntry* pParent ) const 596 { 597 if ( !pParent ) 598 return GetEntryCount(); 599 600 if ( !pParent || !pParent->pChilds) 601 return 0; 602 sal_uLong nCount = 0; 603 sal_uInt16 nRefDepth = GetDepth( pParent ); 604 sal_uInt16 nActDepth = nRefDepth; 605 do 606 { 607 pParent = Next( pParent, &nActDepth ); 608 nCount++; 609 } while( pParent && nRefDepth < nActDepth ); 610 nCount--; 611 return nCount; 612 } 613 614 /************************************************************************* 615 |* 616 |* SvTreeList:: 617 |* 618 |* Beschreibung 619 |* Ersterstellung 17.08.94 620 |* Letzte Aenderung 17.08.94 621 |* 622 *************************************************************************/ 623 624 sal_uLong SvTreeList::GetVisibleChildCount(const SvListView* pView, SvListEntry* pParent) const 625 { 626 DBG_ASSERT(pView,"GetVisChildCount:No View"); 627 if ( !pParent ) 628 pParent = pRootItem; 629 if ( !pParent || !pView->IsExpanded(pParent) || !pParent->pChilds ) 630 return 0; 631 sal_uLong nCount = 0; 632 sal_uInt16 nRefDepth = GetDepth( pParent ); 633 sal_uInt16 nActDepth = nRefDepth; 634 do 635 { 636 pParent = NextVisible( pView, pParent, &nActDepth ); 637 nCount++; 638 } while( pParent && nRefDepth < nActDepth ); 639 nCount--; 640 return nCount; 641 } 642 643 sal_uLong SvTreeList::GetChildSelectionCount(const SvListView* pView,SvListEntry* pParent) const 644 { 645 DBG_ASSERT(pView,"GetChildSelCount:No View"); 646 if ( !pParent ) 647 pParent = pRootItem; 648 if ( !pParent || !pParent->pChilds) 649 return 0; 650 sal_uLong nCount = 0; 651 sal_uInt16 nRefDepth = GetDepth( pParent ); 652 sal_uInt16 nActDepth = nRefDepth; 653 do 654 { 655 pParent = Next( pParent, &nActDepth ); 656 if( pParent && pView->IsSelected( pParent ) && nRefDepth < nActDepth) 657 nCount++; 658 } while( pParent && nRefDepth < nActDepth ); 659 // nCount--; 660 return nCount; 661 } 662 663 664 /************************************************************************* 665 |* 666 |* SvTreeList:: 667 |* 668 |* Beschreibung 669 |* Ersterstellung 17.08.94 670 |* Letzte Aenderung 17.08.94 671 |* 672 *************************************************************************/ 673 674 SvListEntry* SvTreeList::First() const 675 { 676 if ( nEntryCount ) 677 return (SvListEntry*)(pRootItem->pChilds->GetObject(0)); 678 else 679 return 0; 680 } 681 682 /************************************************************************* 683 |* 684 |* SvTreeList::Next 685 |* 686 |* Beschreibung 687 |* Ersterstellung 17.08.94 688 |* Letzte Aenderung 17.08.94 689 |* 690 *************************************************************************/ 691 SvListEntry* SvTreeList::Next( SvListEntry* pActEntry, sal_uInt16* pDepth ) const 692 { 693 DBG_ASSERT( pActEntry && pActEntry->pParent, "SvTreeList::Next: invalid entry/parent!" ); 694 if ( !pActEntry || !pActEntry->pParent ) 695 return NULL; 696 697 sal_uInt16 nDepth = 0; 698 int bWithDepth = sal_False; 699 if ( pDepth ) 700 { 701 nDepth = *pDepth; 702 bWithDepth = sal_True; 703 } 704 705 SvTreeEntryList* pActualList = pActEntry->pParent->pChilds; 706 sal_uLong nActualPos = pActEntry->GetChildListPos(); 707 708 if ( pActEntry->pChilds /* && pActEntry->pChilds->Count() */ ) 709 { 710 nDepth++; 711 pActEntry = (SvListEntry*)(pActEntry->pChilds->GetObject(0)); 712 if ( bWithDepth ) 713 *pDepth = nDepth; 714 return pActEntry; 715 } 716 717 if ( pActualList->Count() > ( nActualPos + 1 ) ) 718 { 719 pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos + 1 )); 720 if ( bWithDepth ) 721 *pDepth = nDepth; 722 return pActEntry; 723 } 724 725 SvListEntry* pParent = pActEntry->pParent; 726 nDepth--; 727 while( pParent != pRootItem && pParent != 0 ) 728 { 729 DBG_ASSERT(pParent!=0,"TreeData corrupt!"); 730 pActualList = pParent->pParent->pChilds; 731 DBG_ASSERT(pActualList,"TreeData corrupt!"); 732 nActualPos = pParent->GetChildListPos(); 733 if ( pActualList->Count() > ( nActualPos + 1 ) ) 734 { 735 pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos + 1 )); 736 if ( bWithDepth ) 737 *pDepth = nDepth; 738 return pActEntry; 739 } 740 pParent = pParent->pParent; 741 nDepth--; 742 } 743 return 0; 744 } 745 746 /************************************************************************* 747 |* 748 |* SvTreeList::Prev 749 |* 750 |* Beschreibung 751 |* Ersterstellung 17.08.94 752 |* Letzte Aenderung 17.08.94 753 |* 754 *************************************************************************/ 755 SvListEntry* SvTreeList::Prev( SvListEntry* pActEntry, sal_uInt16* pDepth ) const 756 { 757 DBG_ASSERT(pActEntry!=0,"Entry?"); 758 759 sal_uInt16 nDepth = 0; 760 int bWithDepth = sal_False; 761 if ( pDepth ) 762 { 763 nDepth = *pDepth; 764 bWithDepth = sal_True; 765 } 766 767 SvTreeEntryList* pActualList = pActEntry->pParent->pChilds; 768 sal_uLong nActualPos = pActEntry->GetChildListPos(); 769 770 if ( nActualPos > 0 ) 771 { 772 pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos - 1 )); 773 while( pActEntry->pChilds /* && pActEntry->pChilds->Count() */ ) 774 { 775 pActualList = pActEntry->pChilds; 776 nDepth++; 777 pActEntry = (SvListEntry*)(pActualList->Last()); 778 } 779 if ( bWithDepth ) 780 *pDepth = nDepth; 781 return pActEntry; 782 } 783 if ( pActEntry->pParent == pRootItem ) 784 return 0; 785 786 pActEntry = pActEntry->pParent; 787 788 if ( pActEntry ) 789 { 790 nDepth--; 791 if ( bWithDepth ) 792 *pDepth = nDepth; 793 return pActEntry; 794 } 795 return 0; 796 } 797 798 /************************************************************************* 799 |* 800 |* SvTreeList:: 801 |* 802 |* Beschreibung 803 |* Ersterstellung 17.08.94 804 |* Letzte Aenderung 17.08.94 805 |* 806 *************************************************************************/ 807 808 SvListEntry* SvTreeList::Last( sal_uInt16* /* nDepth */ ) const 809 { 810 SvTreeEntryList* pActList = pRootItem->pChilds; 811 // if ( pActList->Count() == 0 ) 812 // return 0; 813 SvListEntry* pEntry = 0; 814 while( pActList ) 815 { 816 pEntry = (SvListEntry*)(pActList->Last()); 817 pActList = pEntry->pChilds; 818 // if ( pActList->Count() == 0 ) 819 // pActList = 0; 820 } 821 return pEntry; 822 } 823 824 /************************************************************************* 825 |* 826 |* SvTreeList:: 827 |* 828 |* Beschreibung 829 |* Ersterstellung 17.08.94 830 |* Letzte Aenderung 17.08.94 831 |* 832 *************************************************************************/ 833 834 sal_uLong SvTreeList::GetVisiblePos( const SvListView* pView, SvListEntry* pEntry ) const 835 { 836 DBG_ASSERT(pView&&pEntry,"View/Entry?"); 837 838 if ( !pView->bVisPositionsValid ) 839 { 840 // damit GetVisibleCount die Positionen aktualisiert 841 ((SvListView*)pView)->nVisibleCount = 0; 842 GetVisibleCount( pView ); 843 } 844 SvViewData* pViewData = pView->GetViewData( pEntry ); 845 return pViewData->nVisPos; 846 } 847 848 /************************************************************************* 849 |* 850 |* SvTreeList:: 851 |* 852 |* Beschreibung 853 |* Ersterstellung 17.08.94 854 |* Letzte Aenderung 17.08.94 855 |* 856 *************************************************************************/ 857 858 sal_uLong SvTreeList::GetVisibleCount( const SvListView* pView ) const 859 { 860 DBG_ASSERT(pView,"GetVisCount:No View"); 861 if( !pView->HasViewData() ) 862 return 0; 863 if ( pView->nVisibleCount ) 864 return pView->nVisibleCount; 865 866 sal_uLong nPos = 0; 867 SvListEntry* pEntry = First(); // erster Eintrag immer sichtbar 868 while ( pEntry ) 869 { 870 SvViewData* pViewData = pView->GetViewData( pEntry ); 871 pViewData->nVisPos = nPos; 872 nPos++; 873 pEntry = NextVisible( pView, pEntry ); 874 } 875 #ifdef DBG_UTIL 876 if( nPos > 10000000 ) 877 { 878 DBG_ERROR("nVisibleCount bad"); 879 } 880 #endif 881 ((SvListView*)pView)->nVisibleCount = nPos; 882 ((SvListView*)pView)->bVisPositionsValid = sal_True; 883 return nPos; 884 } 885 886 887 /************************************************************************* 888 |* 889 |* SvTreeList:: 890 |* 891 |* Beschreibung 892 |* Ersterstellung 17.08.94 893 |* Letzte Aenderung 17.08.94 894 |* 895 *************************************************************************/ 896 897 // Funktion geht aus Geschwindigkeitsgruenden davon aus, 898 // das der uebergebene Eintrag bereits sichtbar ist 899 900 SvListEntry* SvTreeList::NextVisible(const SvListView* pView,SvListEntry* pActEntry,sal_uInt16* pActDepth) const 901 { 902 DBG_ASSERT(pView,"NextVisible:No View"); 903 if ( !pActEntry ) 904 return 0; 905 906 sal_uInt16 nDepth = 0; 907 int bWithDepth = sal_False; 908 if ( pActDepth ) 909 { 910 nDepth = *pActDepth; 911 bWithDepth = sal_True; 912 } 913 914 SvTreeEntryList* pActualList = pActEntry->pParent->pChilds; 915 sal_uLong nActualPos = pActEntry->GetChildListPos(); 916 917 if ( pView->IsExpanded(pActEntry) ) 918 { 919 DBG_ASSERT(pActEntry->pChilds,"Childs?"); 920 nDepth++; 921 pActEntry = (SvListEntry*)(pActEntry->pChilds->GetObject(0)); 922 if ( bWithDepth ) 923 *pActDepth = nDepth; 924 return pActEntry; 925 } 926 927 nActualPos++; 928 if ( pActualList->Count() > nActualPos ) 929 { 930 pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos )); 931 if ( bWithDepth ) 932 *pActDepth = nDepth; 933 return pActEntry; 934 } 935 936 SvListEntry* pParent = pActEntry->pParent; 937 nDepth--; 938 while( pParent != pRootItem ) 939 { 940 pActualList = pParent->pParent->pChilds; 941 nActualPos = pParent->GetChildListPos(); 942 nActualPos++; 943 if ( pActualList->Count() > nActualPos ) 944 { 945 pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos )); 946 if ( bWithDepth ) 947 *pActDepth = nDepth; 948 return pActEntry; 949 } 950 pParent = pParent->pParent; 951 nDepth--; 952 } 953 return 0; 954 } 955 956 957 /************************************************************************* 958 |* 959 |* SvTreeList:: 960 |* 961 |* Beschreibung 962 |* Ersterstellung 17.08.94 963 |* Letzte Aenderung 17.08.94 964 |* 965 *************************************************************************/ 966 967 // Funktion geht aus Geschwindigkeitsgruenden davon aus, 968 // das der uebergebene Eintrag bereits sichtbar ist 969 970 SvListEntry* SvTreeList::PrevVisible(const SvListView* pView, SvListEntry* pActEntry, sal_uInt16* pActDepth) const 971 { 972 DBG_ASSERT(pView&&pActEntry,"PrevVis:View/Entry?"); 973 974 sal_uInt16 nDepth = 0; 975 int bWithDepth = sal_False; 976 if ( pActDepth ) 977 { 978 nDepth = *pActDepth; 979 bWithDepth = sal_True; 980 } 981 982 SvTreeEntryList* pActualList = pActEntry->pParent->pChilds; 983 sal_uLong nActualPos = pActEntry->GetChildListPos(); 984 985 if ( nActualPos > 0 ) 986 { 987 pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos - 1 )); 988 while( pView->IsExpanded(pActEntry) ) 989 { 990 pActualList = pActEntry->pChilds; 991 nDepth++; 992 pActEntry = (SvListEntry*)(pActualList->Last()); 993 } 994 if ( bWithDepth ) 995 *pActDepth = nDepth; 996 return pActEntry; 997 } 998 999 if ( pActEntry->pParent == pRootItem ) 1000 return 0; 1001 1002 pActEntry = pActEntry->pParent; 1003 if ( pActEntry ) 1004 { 1005 nDepth--; 1006 if ( bWithDepth ) 1007 *pActDepth = nDepth; 1008 return pActEntry; 1009 } 1010 return 0; 1011 } 1012 1013 /************************************************************************* 1014 |* 1015 |* SvTreeList:: 1016 |* 1017 |* Beschreibung 1018 |* Ersterstellung 17.08.94 1019 |* Letzte Aenderung 17.08.94 1020 |* 1021 *************************************************************************/ 1022 1023 SvListEntry* SvTreeList::LastVisible( const SvListView* pView, sal_uInt16* pDepth) const 1024 { 1025 DBG_ASSERT(pView,"LastVis:No View"); 1026 SvListEntry* pEntry = Last(); 1027 while( pEntry && !IsEntryVisible( pView, pEntry ) ) 1028 pEntry = PrevVisible( pView, pEntry ); 1029 if ( pEntry && pDepth ) 1030 *pDepth = GetDepth( pEntry ); 1031 return pEntry; 1032 } 1033 1034 /************************************************************************* 1035 |* 1036 |* SvTreeList:: 1037 |* 1038 |* Beschreibung 1039 |* Ersterstellung 17.08.94 1040 |* Letzte Aenderung 17.08.94 1041 |* 1042 *************************************************************************/ 1043 1044 SvListEntry* SvTreeList::NextVisible(const SvListView* pView,SvListEntry* pEntry,sal_uInt16& nDelta) const 1045 { 1046 DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"NextVis:Wrong Prms/!Vis"); 1047 1048 sal_uLong nVisPos = GetVisiblePos( pView, pEntry ); 1049 // nDelta Eintraege vorhanden ? 1050 // Beispiel: 0,1,2,3,4,5,6,7,8,9 nVisPos=5 nDelta=7 1051 // nNewDelta = 10-nVisPos-1 == 4 1052 if ( nVisPos+nDelta >= pView->nVisibleCount ) 1053 { 1054 nDelta = (sal_uInt16)(pView->nVisibleCount-nVisPos); 1055 nDelta--; 1056 } 1057 sal_uInt16 nDeltaTmp = nDelta; 1058 while( nDeltaTmp ) 1059 { 1060 pEntry = NextVisible( pView, pEntry ); 1061 nDeltaTmp--; 1062 DBG_ASSERT(pEntry,"Entry?"); 1063 } 1064 return pEntry; 1065 } 1066 1067 /************************************************************************* 1068 |* 1069 |* SvTreeList:: 1070 |* 1071 |* Beschreibung 1072 |* Ersterstellung 17.08.94 1073 |* Letzte Aenderung 17.08.94 1074 |* 1075 *************************************************************************/ 1076 1077 SvListEntry* SvTreeList::PrevVisible( const SvListView* pView, SvListEntry* pEntry, sal_uInt16& nDelta ) const 1078 { 1079 DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"PrevVis:Parms/!Vis"); 1080 1081 sal_uLong nVisPos = GetVisiblePos( pView, pEntry ); 1082 // nDelta Eintraege vorhanden ? 1083 // Beispiel: 0,1,2,3,4,5,6,7,8,9 nVisPos=8 nDelta=20 1084 // nNewDelta = nNewVisPos 1085 if ( nDelta > nVisPos ) 1086 nDelta = (sal_uInt16)nVisPos; 1087 sal_uInt16 nDeltaTmp = nDelta; 1088 while( nDeltaTmp ) 1089 { 1090 pEntry = PrevVisible( pView, pEntry ); 1091 nDeltaTmp--; 1092 DBG_ASSERT(pEntry,"Entry?"); 1093 } 1094 return pEntry; 1095 } 1096 1097 /************************************************************************* 1098 |* 1099 |* SvTreeList:: 1100 |* 1101 |* Beschreibung 1102 |* Ersterstellung 17.08.94 1103 |* Letzte Aenderung 17.08.94 1104 |* 1105 *************************************************************************/ 1106 1107 SvListEntry* SvTreeList::FirstSelected( const SvListView* pView) const 1108 { 1109 DBG_ASSERT(pView,"FirstSel:No View"); 1110 if( !pView ) 1111 return 0; 1112 SvListEntry* pActSelEntry = First(); 1113 while( pActSelEntry && !pView->IsSelected(pActSelEntry) ) 1114 pActSelEntry = NextVisible( pView, pActSelEntry ); 1115 return pActSelEntry; 1116 } 1117 1118 1119 SvListEntry* SvTreeList::FirstChild( SvListEntry* pParent ) const 1120 { 1121 if ( !pParent ) 1122 pParent = pRootItem; 1123 SvListEntry* pResult; 1124 if ( pParent->pChilds ) 1125 pResult = (SvListEntry*)(pParent->pChilds->GetObject( 0 )); 1126 else 1127 pResult = 0; 1128 return pResult; 1129 } 1130 1131 SvListEntry* SvTreeList::NextSibling( SvListEntry* pEntry ) const 1132 { 1133 DBG_ASSERT(pEntry,"Entry?"); 1134 if( !pEntry ) 1135 return 0; 1136 SvTreeEntryList* pList = pEntry->pParent->pChilds; 1137 // sal_uLong nPos = pList->GetPos( pEntry ); 1138 sal_uLong nPos = pEntry->GetChildListPos(); 1139 nPos++; 1140 pEntry = (SvListEntry*)(pList->GetObject( nPos )); 1141 return pEntry; 1142 } 1143 1144 SvListEntry* SvTreeList::PrevSibling( SvListEntry* pEntry ) const 1145 { 1146 DBG_ASSERT(pEntry,"Entry?"); 1147 if( !pEntry ) 1148 return 0; 1149 1150 SvTreeEntryList* pList = pEntry->pParent->pChilds; 1151 // sal_uLong nPos = pList->GetPos( pEntry ); 1152 sal_uLong nPos = pEntry->GetChildListPos(); 1153 if ( nPos == 0 ) 1154 return 0; 1155 nPos--; 1156 pEntry = (SvListEntry*)(pList->GetObject( nPos )); 1157 return pEntry; 1158 } 1159 1160 1161 SvListEntry* SvTreeList::LastSibling( SvListEntry* pEntry ) const 1162 { 1163 DBG_ASSERT(pEntry,"LastSibling:Entry?"); 1164 if( !pEntry ) 1165 return 0; 1166 SvListEntry* pSib = 0; 1167 SvTreeEntryList* pSibs = pEntry->pParent->pChilds; 1168 if ( pSibs ) 1169 pSib = (SvListEntry*)(pSibs->Last()); 1170 return pSib; 1171 } 1172 1173 1174 1175 /************************************************************************* 1176 |* 1177 |* SvTreeList:: 1178 |* 1179 |* Beschreibung 1180 |* Ersterstellung 17.08.94 1181 |* Letzte Aenderung 17.08.94 1182 |* 1183 *************************************************************************/ 1184 1185 SvListEntry* SvTreeList::NextSelected( const SvListView* pView, SvListEntry* pEntry ) const 1186 { 1187 DBG_ASSERT(pView&&pEntry,"NextSel:View/Entry?"); 1188 pEntry = Next( pEntry ); 1189 while( pEntry && !pView->IsSelected(pEntry) ) 1190 pEntry = Next( pEntry ); 1191 return pEntry; 1192 } 1193 1194 /************************************************************************* 1195 |* 1196 |* SvTreeList:: 1197 |* 1198 |* Beschreibung 1199 |* Ersterstellung 17.08.94 1200 |* Letzte Aenderung 17.08.94 1201 |* 1202 *************************************************************************/ 1203 1204 SvListEntry* SvTreeList::PrevSelected( const SvListView* pView, SvListEntry* pEntry) const 1205 { 1206 DBG_ASSERT(pView&&pEntry,"PrevSel:View/Entry?"); 1207 pEntry = Prev( pEntry ); 1208 while( pEntry && !pView->IsSelected(pEntry) ) 1209 pEntry = Prev( pEntry ); 1210 1211 return pEntry; 1212 } 1213 1214 /************************************************************************* 1215 |* 1216 |* SvTreeList:: 1217 |* 1218 |* Beschreibung 1219 |* Ersterstellung 17.08.94 1220 |* Letzte Aenderung 17.08.94 1221 |* 1222 *************************************************************************/ 1223 1224 SvListEntry* SvTreeList::LastSelected( const SvListView* pView ) const 1225 { 1226 DBG_ASSERT(pView,"LastSel:No View"); 1227 SvListEntry* pEntry = Last(); 1228 while( pEntry && !pView->IsSelected(pEntry) ) 1229 pEntry = Prev( pEntry ); 1230 return pEntry; 1231 } 1232 1233 /************************************************************************* 1234 |* 1235 |* SvTreeList::Insert 1236 |* 1237 |* Beschreibung 1238 |* Ersterstellung 17.08.94 1239 |* Letzte Aenderung 17.08.94 1240 |* 1241 *************************************************************************/ 1242 sal_uLong SvTreeList::Insert( SvListEntry* pEntry,SvListEntry* pParent,sal_uLong nPos ) 1243 { 1244 DBG_ASSERT( pEntry,"Entry?"); 1245 1246 if ( !pParent ) 1247 pParent = pRootItem; 1248 1249 1250 SvTreeEntryList* pList = pParent->pChilds; 1251 if ( !pList ) 1252 { 1253 // Parent bekommt zum erstenmal ein Kind 1254 pList = new SvTreeEntryList; 1255 pParent->pChilds = pList; 1256 } 1257 1258 // Sortierung beruecksichtigen 1259 GetInsertionPos( pEntry, pParent, nPos ); 1260 1261 bAbsPositionsValid = sal_False; 1262 pEntry->pParent = pParent; 1263 1264 pList->Insert( pEntry, nPos ); 1265 nEntryCount++; 1266 if( nPos != LIST_APPEND && (nPos != (pList->Count()-1)) ) 1267 SetListPositions( pList ); 1268 else 1269 pEntry->nListPos = pList->Count()-1; 1270 1271 #ifdef CHECK_INTEGRITY 1272 CheckIntegrity(); 1273 #endif 1274 Broadcast( LISTACTION_INSERTED, pEntry ); 1275 return nPos; // pEntry->nListPos; 1276 } 1277 1278 /************************************************************************* 1279 |* 1280 |* SvTreeList:: 1281 |* 1282 |* Beschreibung 1283 |* Ersterstellung 17.08.94 1284 |* Letzte Aenderung 17.08.94 1285 |* 1286 *************************************************************************/ 1287 1288 sal_uLong SvTreeList::GetAbsPos( SvListEntry* pEntry) const 1289 { 1290 if ( !bAbsPositionsValid ) 1291 ((SvTreeList*)this)->SetAbsolutePositions(); 1292 return pEntry->nAbsPos; 1293 } 1294 1295 /************************************************************************* 1296 |* 1297 |* SvTreeList:: 1298 |* 1299 |* Beschreibung 1300 |* Ersterstellung 17.08.94 1301 |* Letzte Aenderung 17.08.94 1302 |* 1303 *************************************************************************/ 1304 1305 void SvTreeList::SetAbsolutePositions() 1306 { 1307 sal_uLong nPos = 0; 1308 SvListEntry* pEntry = First(); 1309 while ( pEntry ) 1310 { 1311 pEntry->nAbsPos = nPos; 1312 nPos++; 1313 pEntry = Next( pEntry ); 1314 } 1315 bAbsPositionsValid = sal_True; 1316 #ifdef CHECK_INTEGRITY 1317 CheckIntegrity(); 1318 #endif 1319 } 1320 1321 1322 /************************************************************************* 1323 |* 1324 |* SvTreeList::Expand 1325 |* 1326 |* Beschreibung 1327 |* Ersterstellung 17.08.94 1328 |* Letzte Aenderung 17.08.94 1329 |* 1330 *************************************************************************/ 1331 1332 void SvTreeList::Expand( SvListView* pView, SvListEntry* pEntry ) 1333 { 1334 DBG_ASSERT(pEntry&&pView,"Expand:View/Entry?"); 1335 if ( pView->IsExpanded(pEntry) ) 1336 return; 1337 1338 DBG_ASSERT(pEntry->pChilds,"Expand:No Childs!"); 1339 1340 SvViewData* pViewData = pView->GetViewData(pEntry); 1341 pViewData->nFlags |= SVLISTENTRYFLAG_EXPANDED; 1342 SvListEntry* pParent = pEntry->pParent; 1343 // wenn Parent sichtbar dann Statusdaten invalidieren 1344 if ( pView->IsExpanded( pParent ) ) 1345 { 1346 pView->bVisPositionsValid = sal_False; 1347 pView->nVisibleCount = 0; 1348 } 1349 #ifdef CHECK_INTEGRITY 1350 CheckIntegrity(); 1351 #endif 1352 } 1353 1354 /************************************************************************* 1355 |* 1356 |* SvTreeList::Collapse 1357 |* 1358 |* Beschreibung 1359 |* Ersterstellung 17.08.94 1360 |* Letzte Aenderung 17.08.94 1361 |* 1362 *************************************************************************/ 1363 1364 void SvTreeList::Collapse( SvListView* pView, SvListEntry* pEntry ) 1365 { 1366 DBG_ASSERT(pView&&pEntry,"Collapse:View/Entry?"); 1367 if ( !pView->IsExpanded(pEntry) ) 1368 return; 1369 1370 DBG_ASSERT(pEntry->pChilds,"Collapse:No Childs!"); 1371 1372 SvViewData* pViewData = pView->GetViewData( pEntry ); 1373 pViewData->nFlags &=(~SVLISTENTRYFLAG_EXPANDED); 1374 1375 SvListEntry* pParent = pEntry->pParent; 1376 if ( pView->IsExpanded(pParent) ) 1377 { 1378 pView->nVisibleCount = 0; 1379 pView->bVisPositionsValid = sal_False; 1380 } 1381 #ifdef CHECK_INTEGRITY 1382 CheckIntegrity(); 1383 #endif 1384 } 1385 1386 1387 /************************************************************************* 1388 |* 1389 |* SvTreeList:: 1390 |* 1391 |* Beschreibung 1392 |* Ersterstellung 17.08.94 1393 |* Letzte Aenderung 17.08.94 1394 |* 1395 *************************************************************************/ 1396 1397 sal_Bool SvTreeList::Select( SvListView* pView, SvListEntry* pEntry, sal_Bool bSelect ) 1398 { 1399 DBG_ASSERT(pView&&pEntry,"Select:View/Entry?"); 1400 SvViewData* pViewData = pView->GetViewData( pEntry ); 1401 if ( bSelect ) 1402 { 1403 if ( pViewData->IsSelected() || !pViewData->IsSelectable() ) 1404 return sal_False; 1405 else 1406 { 1407 pViewData->nFlags |= SVLISTENTRYFLAG_SELECTED; 1408 pView->nSelectionCount++; 1409 } 1410 } 1411 else 1412 { 1413 if ( !pViewData->IsSelected() ) 1414 return sal_False; 1415 else 1416 { 1417 pViewData->nFlags &= ~( SVLISTENTRYFLAG_SELECTED ); 1418 pView->nSelectionCount--; 1419 } 1420 } 1421 #ifdef CHECK_INTEGRITY 1422 CheckIntegrity(); 1423 #endif 1424 return sal_True; 1425 } 1426 1427 /************************************************************************* 1428 |* 1429 |* SvTreeList::Remove 1430 |* 1431 |* Beschreibung 1432 |* Ersterstellung 17.08.94 1433 |* Letzte Aenderung 05.04.01 1434 |* 1435 *************************************************************************/ 1436 sal_Bool SvTreeList::Remove( SvListEntry* pEntry ) 1437 { 1438 DBG_ASSERT(pEntry,"Cannot remove root, use clear"); 1439 1440 if( !pEntry->pParent ) 1441 { 1442 DBG_ERROR("Removing entry not in model!"); 1443 // unter gewissen Umstaenden (welche?) loescht der 1444 // Explorer aus der View Eintraege, die er nicht in die View 1445 // eingefuegt hat. Da sich der Kunde fuer ein platzendes 1446 // Office nichts kaufen kann, fange ich diesen Fall ab. 1447 return sal_False; 1448 } 1449 1450 Broadcast( LISTACTION_REMOVING, pEntry ); 1451 sal_uLong nRemoved = 1 + GetChildCount(pEntry); 1452 bAbsPositionsValid = sal_False; 1453 1454 SvListEntry* pParent = pEntry->pParent; 1455 SvTreeEntryList* pList = pParent->pChilds; 1456 DBG_ASSERT(pList,"Remove:No Childlist"); 1457 sal_Bool bLastEntry = sal_False; 1458 1459 if ( pEntry->HasChildListPos() ) 1460 { 1461 sal_uLong nListPos = pEntry->GetChildListPos(); 1462 bLastEntry = (nListPos == (pList->Count()-1) ) ? sal_True : sal_False; 1463 pList->Remove( nListPos ); 1464 } 1465 else 1466 { 1467 pList->Remove( (void*) pEntry ); 1468 } 1469 1470 1471 // moved to end of method because it is used later with Broadcast 1472 // delete pEntry; // loescht auch alle Childs 1473 1474 if ( pList->Count() == 0 ) 1475 { 1476 pParent->pChilds = 0; 1477 delete pList; 1478 } 1479 else 1480 { 1481 if( !bLastEntry ) 1482 SetListPositions( pList ); 1483 } 1484 nEntryCount -= nRemoved; 1485 1486 #ifdef CHECK_INTEGRITY 1487 CheckIntegrity(); 1488 #endif 1489 Broadcast( LISTACTION_REMOVED, pEntry ); 1490 1491 delete pEntry; // loescht auch alle Childs 1492 return sal_True; 1493 } 1494 1495 /************************************************************************* 1496 |* 1497 |* SvTreeList:: 1498 |* 1499 |* Beschreibung 1500 |* Ersterstellung 17.08.94 1501 |* Letzte Aenderung 17.08.94 1502 |* 1503 *************************************************************************/ 1504 1505 sal_uLong SvTreeList::SelectChilds(SvListView* pView, SvListEntry* pParent,sal_Bool bSelect ) 1506 { 1507 DBG_ASSERT(pView&&pParent,"SelChilds:View/Parent?"); 1508 if ( !pParent->pChilds ) 1509 return 0; 1510 if ( pParent->pChilds->Count() == 0 ) 1511 return 0; 1512 1513 sal_uInt16 nRefDepth = GetDepth( pParent ); 1514 sal_uInt16 nDepth = nRefDepth; 1515 sal_uLong nCount = 0; 1516 pParent = Next( pParent ); 1517 do 1518 { 1519 if ( Select( pView, pParent, bSelect ) ) 1520 nCount++; // nur die tatsaechlichen Selektierungen zaehlen 1521 pParent = Next( pParent, &nDepth ); 1522 } 1523 while( pParent && nDepth > nRefDepth ); 1524 #ifdef CHECK_INTEGRITY 1525 CheckIntegrity(); 1526 #endif 1527 return nCount; 1528 } 1529 1530 void SvTreeList::SelectAll( SvListView* pView, sal_Bool bSelect ) 1531 { 1532 DBG_ASSERT(pView,"SelectAll:NoView"); 1533 SvListEntry* pEntry = First(); 1534 while ( pEntry ) 1535 { 1536 SvViewData* pViewData = pView->GetViewData( pEntry ); 1537 if ( bSelect ) 1538 pViewData->nFlags |= SVLISTENTRYFLAG_SELECTED; 1539 else 1540 pViewData->nFlags &= (~SVLISTENTRYFLAG_SELECTED); 1541 1542 pEntry = Next( pEntry ); 1543 } 1544 if ( bSelect ) 1545 pView->nSelectionCount = nEntryCount; 1546 else 1547 pView->nSelectionCount = 0; 1548 #ifdef CHECK_INTEGRITY 1549 CheckIntegrity(); 1550 #endif 1551 } 1552 1553 1554 SvListEntry* SvTreeList::GetEntryAtAbsPos( sal_uLong nAbsPos ) const 1555 { 1556 SvListEntry* pEntry = First(); 1557 while ( nAbsPos && pEntry ) 1558 { 1559 pEntry = Next( pEntry ); 1560 nAbsPos--; 1561 } 1562 return pEntry; 1563 } 1564 1565 SvListEntry* SvTreeList::GetEntryAtVisPos( const SvListView* pView, sal_uLong nVisPos ) const 1566 { 1567 DBG_ASSERT(pView,"GetEntryAtVisPos:No View"); 1568 SvListEntry* pEntry = First(); 1569 while ( nVisPos && pEntry ) 1570 { 1571 pEntry = NextVisible( pView, pEntry ); 1572 nVisPos--; 1573 } 1574 return pEntry; 1575 } 1576 1577 void SvTreeList::SetListPositions( SvTreeEntryList* pList ) 1578 { 1579 if( pList->Count() ) 1580 { 1581 SvListEntry* pEntry = (SvListEntry*)(pList->GetObject(0)); 1582 if( pEntry->pParent ) 1583 pEntry->pParent->InvalidateChildrensListPositions(); 1584 } 1585 /* 1586 sal_uLong nListPos = 0; 1587 SvListEntry* pEntry = (SvListEntry*)(pList->First()); 1588 while( pEntry ) 1589 { 1590 pEntry->nListPos = nListPos; 1591 nListPos++; 1592 pEntry = (SvListEntry*)(pList->Next()); 1593 } 1594 */ 1595 } 1596 1597 1598 void SvTreeList::InvalidateEntry( SvListEntry* pEntry ) 1599 { 1600 Broadcast( LISTACTION_INVALIDATE_ENTRY, pEntry ); 1601 } 1602 1603 sal_Bool SvTreeList::IsInChildList( SvListEntry* pParent, SvListEntry* pChild) const 1604 { 1605 if ( !pParent ) 1606 pParent = pRootItem; 1607 sal_Bool bIsChild = sal_False; 1608 if ( pParent->pChilds ) 1609 bIsChild = (sal_Bool)(pParent->pChilds->GetPos(pChild) != LIST_ENTRY_NOTFOUND); 1610 return bIsChild; 1611 } 1612 1613 1614 void lcl_CheckList( SvTreeEntryList* pList ) 1615 { 1616 SvListEntry* pEntry = (SvListEntry*)(pList->First()); 1617 sal_uLong nPos = 0; 1618 while ( pEntry ) 1619 { 1620 DBG_ASSERT(pEntry->GetChildListPos()==nPos,"Wrong ListPos"); 1621 pEntry = (SvListEntry*)(pList->Next()); 1622 nPos++; 1623 } 1624 } 1625 1626 void SvTreeList::CheckIntegrity() const 1627 { 1628 sal_uLong nMyEntryCount = 0; 1629 if ( pRootItem->pChilds ) 1630 { 1631 lcl_CheckList( pRootItem->pChilds ); 1632 SvListEntry* pEntry = First(); 1633 while( pEntry ) 1634 { 1635 nMyEntryCount++; 1636 if ( pEntry->pChilds ) 1637 lcl_CheckList( pEntry->pChilds ); 1638 pEntry = Next( pEntry ); 1639 } 1640 } 1641 DBG_ASSERT(nMyEntryCount==GetEntryCount(),"Entry count invalid"); 1642 } 1643 1644 SvListEntry* SvTreeList::GetRootLevelParent( SvListEntry* pEntry ) const 1645 { 1646 DBG_ASSERT(pEntry,"GetRootLevelParent:No Entry"); 1647 SvListEntry* pCurParent = 0; 1648 if ( pEntry ) 1649 { 1650 pCurParent = pEntry->pParent; 1651 if ( pCurParent == pRootItem ) 1652 return pEntry; // ist sein eigener Parent 1653 while( pCurParent && pCurParent->pParent != pRootItem ) 1654 pCurParent = pCurParent->pParent; 1655 } 1656 return pCurParent; 1657 } 1658 1659 1660 1661 1662 //************************************************************************* 1663 //************************************************************************* 1664 //************************************************************************* 1665 //************************************************************************* 1666 //************************************************************************* 1667 //************************************************************************* 1668 //************************************************************************* 1669 //************************************************************************* 1670 1671 DBG_NAME(SvListView); 1672 1673 SvListView::SvListView( SvTreeList* pModell ) 1674 { 1675 DBG_CTOR(SvListView,0); 1676 pModel = 0; 1677 nSelectionCount = 0; 1678 nVisibleCount = 0; 1679 bVisPositionsValid = sal_False; 1680 SetModel( pModell ); 1681 } 1682 1683 SvListView::SvListView() 1684 { 1685 DBG_CTOR(SvListView,0); 1686 pModel = 0; 1687 nSelectionCount = 0; 1688 nVisibleCount = 0; 1689 bVisPositionsValid = sal_False; 1690 } 1691 1692 1693 SvListView::~SvListView() 1694 { 1695 DBG_DTOR(SvListView,0); 1696 ClearTable(); 1697 } 1698 1699 void SvListView::InitTable() 1700 { 1701 DBG_CHKTHIS(SvListView,0); 1702 DBG_ASSERT(pModel,"InitTable:No Model"); 1703 DBG_ASSERT(!nSelectionCount&&!nVisibleCount&&!bVisPositionsValid,"InitTable: Not cleared!"); 1704 1705 if( aDataTable.Count() ) 1706 { 1707 DBG_ASSERT(aDataTable.Count()==1,"InitTable: TableCount != 1"); 1708 // die im Clear fuer die Root allozierten View-Daten loeschen 1709 // Achtung: Das zu dem RootEntry (und damit auch der Entry) 1710 // gehoerende Model kann bereits geloescht sein! 1711 SvViewData* pViewData = (SvViewData*)aDataTable.GetObject( 0 ); 1712 delete pViewData; 1713 aDataTable.Clear(); 1714 } 1715 1716 SvListEntry* pEntry; 1717 SvViewData* pViewData; 1718 1719 // RootEntry einfuegen 1720 pEntry = pModel->pRootItem; 1721 pViewData = new SvViewData; 1722 pViewData->nFlags = SVLISTENTRYFLAG_EXPANDED; 1723 aDataTable.Insert( (sal_uLong)pEntry, pViewData ); 1724 // Jetzt alle anderen Entries 1725 pEntry = pModel->First(); 1726 while( pEntry ) 1727 { 1728 pViewData = CreateViewData( pEntry ); 1729 DBG_ASSERT(pViewData,"InitTable:No ViewData"); 1730 InitViewData( pViewData, pEntry ); 1731 aDataTable.Insert( (sal_uLong)pEntry, pViewData ); 1732 pEntry = pModel->Next( pEntry ); 1733 } 1734 } 1735 1736 SvViewData* SvListView::CreateViewData( SvListEntry* ) 1737 { 1738 DBG_CHKTHIS(SvListView,0); 1739 return new SvViewData; 1740 } 1741 1742 void SvListView::ClearTable() 1743 { 1744 DBG_CHKTHIS(SvListView,0); 1745 SvViewData* pViewData = (SvViewData*)aDataTable.First(); 1746 while( pViewData ) 1747 { 1748 delete pViewData; 1749 pViewData = (SvViewData*)aDataTable.Next(); 1750 } 1751 aDataTable.Clear(); 1752 } 1753 1754 void SvListView::Clear() 1755 { 1756 ClearTable(); 1757 nSelectionCount = 0; 1758 nVisibleCount = 0; 1759 bVisPositionsValid = sal_False; 1760 if( pModel ) 1761 { 1762 // RootEntry einfuegen 1763 SvListEntry* pEntry = pModel->pRootItem; 1764 SvViewData* pViewData = new SvViewData; 1765 pViewData->nFlags = SVLISTENTRYFLAG_EXPANDED; 1766 aDataTable.Insert( (sal_uLong)pEntry, pViewData ); 1767 } 1768 } 1769 1770 void SvListView::SetModel( SvTreeList* pNewModel ) 1771 { 1772 DBG_CHKTHIS(SvListView,0); 1773 sal_Bool bBroadcastCleared = sal_False; 1774 if ( pModel ) 1775 { 1776 pModel->RemoveView( this ); 1777 bBroadcastCleared = sal_True; 1778 ModelNotification( LISTACTION_CLEARING,0,0,0 ); 1779 if ( pModel->GetRefCount() == 0 ) 1780 delete pModel; 1781 } 1782 pModel = pNewModel; 1783 InitTable(); 1784 pNewModel->InsertView( this ); 1785 if( bBroadcastCleared ) 1786 ModelNotification( LISTACTION_CLEARED,0,0,0 ); 1787 } 1788 1789 1790 void SvListView::ModelHasCleared() 1791 { 1792 DBG_CHKTHIS(SvListView,0); 1793 } 1794 1795 void SvListView::ModelHasInserted( SvListEntry* ) 1796 { 1797 DBG_CHKTHIS(SvListView,0); 1798 } 1799 1800 void SvListView::ModelHasInsertedTree( SvListEntry* ) 1801 { 1802 DBG_CHKTHIS(SvListView,0); 1803 } 1804 1805 void SvListView::ModelIsMoving( SvListEntry* /* pSource */ , 1806 SvListEntry* /* pTargetParent */ , sal_uLong /* nPos */ ) 1807 { 1808 DBG_CHKTHIS(SvListView,0); 1809 } 1810 1811 1812 void SvListView::ModelHasMoved( SvListEntry* ) 1813 { 1814 DBG_CHKTHIS(SvListView,0); 1815 } 1816 1817 void SvListView::ModelIsRemoving( SvListEntry* ) 1818 { 1819 DBG_CHKTHIS(SvListView,0); 1820 } 1821 1822 void SvListView::ModelHasRemoved( SvListEntry* ) 1823 { 1824 DBG_CHKTHIS(SvListView,0); 1825 } 1826 1827 void SvListView::ModelHasEntryInvalidated( SvListEntry*) 1828 { 1829 DBG_CHKTHIS(SvListView,0); 1830 } 1831 1832 void SvListView::ActionMoving( SvListEntry* pEntry,SvListEntry*,sal_uLong) 1833 { 1834 DBG_CHKTHIS(SvListView,0); 1835 SvListEntry* pParent = pEntry->pParent; 1836 DBG_ASSERT(pParent,"Model not consistent"); 1837 if( pParent != pModel->pRootItem && pParent->pChilds->Count() == 1 ) 1838 { 1839 SvViewData* pViewData = (SvViewData*)aDataTable.Get( (sal_uLong)pParent ); 1840 pViewData->nFlags &= (~SVLISTENTRYFLAG_EXPANDED); 1841 } 1842 // vorlaeufig 1843 nVisibleCount = 0; 1844 bVisPositionsValid = sal_False; 1845 } 1846 1847 void SvListView::ActionMoved( SvListEntry* /* pEntry */ , 1848 SvListEntry* /* pTargetPrnt */ , 1849 sal_uLong /* nChildPos */ ) 1850 { 1851 DBG_CHKTHIS(SvListView,0); 1852 nVisibleCount = 0; 1853 bVisPositionsValid = sal_False; 1854 } 1855 1856 void SvListView::ActionInserted( SvListEntry* pEntry ) 1857 { 1858 DBG_CHKTHIS(SvListView,0); 1859 DBG_ASSERT(pEntry,"Insert:No Entry"); 1860 SvViewData* pData = CreateViewData( pEntry ); 1861 InitViewData( pData, pEntry ); 1862 #ifdef DBG_UTIL 1863 sal_Bool bSuccess = 1864 #endif 1865 aDataTable.Insert( (sal_uLong)pEntry, pData ); 1866 DBG_ASSERT(bSuccess,"Entry already in View"); 1867 if ( nVisibleCount && pModel->IsEntryVisible( this, pEntry )) 1868 { 1869 nVisibleCount = 0; 1870 bVisPositionsValid = sal_False; 1871 } 1872 } 1873 1874 void SvListView::ActionInsertedTree( SvListEntry* pEntry ) 1875 { 1876 DBG_CHKTHIS(SvListView,0); 1877 if ( pModel->IsEntryVisible( this, pEntry )) 1878 { 1879 nVisibleCount = 0; 1880 bVisPositionsValid = sal_False; 1881 } 1882 // ueber Entry und seine Childs iterieren 1883 SvListEntry* pCurEntry = pEntry; 1884 sal_uInt16 nRefDepth = pModel->GetDepth( pCurEntry ); 1885 while( pCurEntry ) 1886 { 1887 DBG_ASSERT(aDataTable.Get((sal_uLong)pCurEntry)==0,"Entry already in Table"); 1888 SvViewData* pViewData = CreateViewData( pCurEntry ); 1889 DBG_ASSERT(pViewData,"No ViewData"); 1890 InitViewData( pViewData, pEntry ); 1891 aDataTable.Insert( (sal_uLong)pCurEntry, pViewData ); 1892 pCurEntry = pModel->Next( pCurEntry ); 1893 if ( pCurEntry && pModel->GetDepth(pCurEntry) <= nRefDepth) 1894 pCurEntry = 0; 1895 } 1896 } 1897 1898 void SvListView::RemoveViewData( SvListEntry* pParent ) 1899 { 1900 SvTreeEntryList* pChilds = pParent->pChilds; 1901 if( pChilds ) 1902 { 1903 SvListEntry* pCur = (SvListEntry*)pChilds->First(); 1904 while( pCur ) 1905 { 1906 SvViewData* pViewData = (SvViewData*)aDataTable.Get((sal_uLong)pCur); 1907 delete pViewData; 1908 aDataTable.Remove( (sal_uLong)pCur ); 1909 if( pCur->HasChilds()) 1910 RemoveViewData( pCur ); 1911 pCur = (SvListEntry*)pChilds->Next(); 1912 } 1913 } 1914 } 1915 1916 1917 1918 void SvListView::ActionRemoving( SvListEntry* pEntry ) 1919 { 1920 DBG_CHKTHIS(SvListView,0); 1921 DBG_ASSERT(pEntry,"Remove:No Entry"); 1922 1923 SvViewData* pViewData = (SvViewData*)aDataTable.Get( (sal_uLong)pEntry ); 1924 sal_uLong nSelRemoved = 0; 1925 if ( pViewData->IsSelected() ) 1926 nSelRemoved = 1 + pModel->GetChildSelectionCount( this, pEntry ); 1927 nSelectionCount -= nSelRemoved; 1928 sal_uLong nVisibleRemoved = 0; 1929 if ( pModel->IsEntryVisible( this, pEntry ) ) 1930 nVisibleRemoved = 1 + pModel->GetVisibleChildCount( this, pEntry ); 1931 if( nVisibleCount ) 1932 { 1933 #ifdef DBG_UTIL 1934 if( nVisibleCount < nVisibleRemoved ) 1935 { 1936 DBG_ERROR("nVisibleRemoved bad"); 1937 } 1938 #endif 1939 nVisibleCount -= nVisibleRemoved; 1940 } 1941 bVisPositionsValid = sal_False; 1942 1943 pViewData = (SvViewData*)aDataTable.Get((sal_uLong)pEntry); 1944 delete pViewData; 1945 aDataTable.Remove( (sal_uLong)pEntry ); 1946 RemoveViewData( pEntry ); 1947 1948 SvListEntry* pCurEntry = pEntry->pParent; 1949 if ( pCurEntry && pCurEntry != pModel->pRootItem && 1950 pCurEntry->pChilds->Count() == 1 ) 1951 { 1952 pViewData = (SvViewData*)aDataTable.Get((sal_uLong)pCurEntry); 1953 pViewData->nFlags &= (~SVLISTENTRYFLAG_EXPANDED); 1954 } 1955 } 1956 1957 void SvListView::ActionRemoved( SvListEntry* /* pEntry */ ) 1958 { 1959 DBG_CHKTHIS(SvListView,0); 1960 } 1961 1962 void SvListView::ActionClear() 1963 { 1964 DBG_CHKTHIS(SvListView,0); 1965 Clear(); 1966 } 1967 1968 void SvListView::ModelNotification( sal_uInt16 nActionId, SvListEntry* pEntry1, 1969 SvListEntry* pEntry2, sal_uLong nPos ) 1970 { 1971 DBG_CHKTHIS(SvListView,0); 1972 switch( nActionId ) 1973 { 1974 case LISTACTION_INSERTED: 1975 ActionInserted( pEntry1 ); 1976 ModelHasInserted( pEntry1 ); 1977 break; 1978 case LISTACTION_INSERTED_TREE: 1979 ActionInsertedTree( pEntry1 ); 1980 ModelHasInsertedTree( pEntry1 ); 1981 break; 1982 case LISTACTION_REMOVING: 1983 ModelIsRemoving( pEntry1 ); 1984 ActionRemoving( pEntry1 ); 1985 break; 1986 case LISTACTION_REMOVED: 1987 ActionRemoved( pEntry1 ); 1988 ModelHasRemoved( pEntry1 ); 1989 break; 1990 case LISTACTION_MOVING: 1991 ModelIsMoving( pEntry1, pEntry2, nPos ); 1992 ActionMoving( pEntry1, pEntry2, nPos ); 1993 break; 1994 case LISTACTION_MOVED: 1995 ActionMoved( pEntry1, pEntry2, nPos ); 1996 ModelHasMoved( pEntry1 ); 1997 break; 1998 case LISTACTION_CLEARING: 1999 ActionClear(); 2000 ModelHasCleared(); //sic! wg. Kompatibilitaet! 2001 break; 2002 case LISTACTION_CLEARED: 2003 break; 2004 case LISTACTION_INVALIDATE_ENTRY: 2005 // keine Action fuer die Basisklasse 2006 ModelHasEntryInvalidated( pEntry1 ); 2007 break; 2008 case LISTACTION_RESORTED: 2009 bVisPositionsValid = sal_False; 2010 break; 2011 case LISTACTION_RESORTING: 2012 break; 2013 default: 2014 DBG_ERROR("unknown ActionId"); 2015 } 2016 } 2017 2018 void SvListView::InitViewData( SvViewData*, SvListEntry* ) 2019 { 2020 } 2021 2022 StringCompare SvTreeList::Compare( SvListEntry* pLeft, SvListEntry* pRight) const 2023 { 2024 if( aCompareLink.IsSet()) 2025 { 2026 SvSortData aSortData; 2027 aSortData.pLeft = pLeft; 2028 aSortData.pRight = pRight; 2029 return (StringCompare)aCompareLink.Call( &aSortData ); 2030 } 2031 return COMPARE_EQUAL; 2032 } 2033 2034 void SvTreeList::Resort() 2035 { 2036 Broadcast( LISTACTION_RESORTING ); 2037 bAbsPositionsValid = sal_False; 2038 ResortChilds( pRootItem ); 2039 Broadcast( LISTACTION_RESORTED ); 2040 } 2041 2042 void SvTreeList::ResortChilds( SvListEntry* pParent ) 2043 { 2044 DBG_ASSERT(pParent,"Parent not set"); 2045 List* pChildList = pParent->pChilds; 2046 if( !pChildList ) 2047 return; 2048 List aList( *pChildList ); 2049 pChildList->Clear(); 2050 2051 sal_uLong nCount = aList.Count(); 2052 for( sal_uLong nCur = 0; nCur < nCount; nCur++ ) 2053 { 2054 SvListEntry* pCurEntry = (SvListEntry*)aList.GetObject( nCur ); 2055 sal_uLong nListPos = LIST_APPEND; 2056 GetInsertionPos( pCurEntry, pParent, nListPos ); 2057 pChildList->Insert( pCurEntry, nListPos ); 2058 if( pCurEntry->pChilds ) 2059 ResortChilds( pCurEntry ); 2060 } 2061 SetListPositions( (SvTreeEntryList*)pChildList ); 2062 } 2063 2064 void SvTreeList::GetInsertionPos( SvListEntry* pEntry, SvListEntry* pParent, 2065 sal_uLong& rPos ) 2066 { 2067 DBG_ASSERT(pEntry,"No Entry"); 2068 2069 if( eSortMode == SortNone ) 2070 return; 2071 2072 rPos = LIST_APPEND; 2073 SvTreeEntryList* pChildList = GetChildList( pParent ); 2074 2075 if( pChildList && pChildList->Count() ) 2076 { 2077 long i = 0; 2078 long j = pChildList->Count()-1; 2079 long k; 2080 StringCompare eCompare = COMPARE_GREATER; 2081 2082 do 2083 { 2084 k = (i+j)/2; 2085 SvListEntry* pTempEntry = (SvListEntry*)(pChildList->GetObject(k)); 2086 eCompare = Compare( pEntry, pTempEntry ); 2087 if( eSortMode == SortDescending && eCompare != COMPARE_EQUAL ) 2088 { 2089 if( eCompare == COMPARE_LESS ) 2090 eCompare = COMPARE_GREATER; 2091 else 2092 eCompare = COMPARE_LESS; 2093 } 2094 if( eCompare == COMPARE_GREATER ) 2095 i = k + 1; 2096 else 2097 j = k - 1; 2098 } while( (eCompare != COMPARE_EQUAL) && (i <= j) ); 2099 2100 if( eCompare != COMPARE_EQUAL ) 2101 { 2102 if(i > ((long)pChildList->Count() - 1)) // nicht gefunden, Ende der Liste 2103 rPos = LIST_APPEND; 2104 else 2105 rPos = i; // nicht gefunden, Mitte 2106 } 2107 else 2108 rPos = k; 2109 } 2110 } 2111 2112 2113