1 /* 2 * Copyright 2001-2008, Haiku Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Axel Dörfler, axeld@pinc-software.de 8 * Rene Gollent (rene@gollent.com) 9 */ 10 11 //! BOutlineListView represents a "nestable" list view. 12 13 #include <OutlineListView.h> 14 #include <Window.h> 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 19 #include <algorithm> 20 21 struct ListItemComparator { 22 ListItemComparator(int (*compareFunc)(const BListItem *, const BListItem *)) 23 : fCompareFunc(compareFunc) 24 { 25 } 26 27 bool operator()(const BListItem *a, const BListItem *b) const 28 { 29 return fCompareFunc(a, b) < 0; 30 } 31 32 private: 33 int (*fCompareFunc)(const BListItem *, const BListItem *); 34 }; 35 36 // #pragma mark - 37 38 39 BOutlineListView::BOutlineListView(BRect frame, const char* name, 40 list_view_type type, uint32 resizeMode, uint32 flags) 41 : BListView(frame, name, type, resizeMode, flags) 42 { 43 } 44 45 46 BOutlineListView::BOutlineListView(BMessage* archive) 47 : BListView(archive) 48 { 49 int32 i = 0; 50 BMessage subData; 51 while (archive->FindMessage("_l_full_items", i++, &subData) == B_OK) { 52 BArchivable *object = instantiate_object(&subData); 53 if (!object) 54 continue; 55 56 BListItem *item = dynamic_cast<BListItem*>(object); 57 if (item) 58 AddItem(item); 59 } 60 } 61 62 63 BOutlineListView::~BOutlineListView() 64 { 65 fFullList.MakeEmpty(); 66 } 67 68 69 BArchivable * 70 BOutlineListView::Instantiate(BMessage* archive) 71 { 72 if (validate_instantiation(archive, "BOutlineListView")) 73 return new BOutlineListView(archive); 74 75 return NULL; 76 } 77 78 79 status_t 80 BOutlineListView::Archive(BMessage* archive, bool deep) const 81 { 82 // Note: We can't call the BListView Archive function here, as we are also 83 // interested in subitems BOutlineListView can have. They are even stored 84 // with a different field name (_l_full_items vs. _l_items). 85 86 status_t status = BView::Archive(archive, deep); 87 if (status != B_OK) 88 return status; 89 90 status = archive->AddInt32("_lv_type", fListType); 91 if (status == B_OK && deep) { 92 int32 i = 0; 93 BListItem *item = NULL; 94 while ((item = static_cast<BListItem*>(fFullList.ItemAt(i++)))) { 95 BMessage subData; 96 status = item->Archive(&subData, true); 97 if (status >= B_OK) 98 status = archive->AddMessage("_l_full_items", &subData); 99 100 if (status < B_OK) 101 break; 102 } 103 } 104 105 if (status >= B_OK && InvocationMessage() != NULL) 106 status = archive->AddMessage("_msg", InvocationMessage()); 107 108 if (status == B_OK && fSelectMessage != NULL) 109 status = archive->AddMessage("_2nd_msg", fSelectMessage); 110 111 return status; 112 } 113 114 115 void 116 BOutlineListView::MouseDown(BPoint point) 117 { 118 MakeFocus(); 119 120 int32 index = IndexOf(point); 121 122 if (index != -1) { 123 BListItem *item = ItemAt(index); 124 125 if (item->fHasSubitems 126 && LatchRect(ItemFrame(index), item->fLevel).Contains(point)) { 127 if (item->IsExpanded()) 128 Collapse(item); 129 else 130 Expand(item); 131 } else 132 BListView::MouseDown(point); 133 } 134 } 135 136 137 void 138 BOutlineListView::KeyDown(const char* bytes, int32 numBytes) 139 { 140 if (numBytes == 1) { 141 switch (bytes[0]) { 142 case B_RIGHT_ARROW: 143 { 144 BListItem *item = ItemAt(CurrentSelection()); 145 if (item && item->fHasSubitems) 146 Expand(item); 147 return; 148 } 149 150 case B_LEFT_ARROW: 151 { 152 BListItem *item = ItemAt(CurrentSelection()); 153 if (item && item->fHasSubitems) 154 Collapse(item); 155 return; 156 } 157 } 158 } 159 160 BListView::KeyDown(bytes, numBytes); 161 } 162 163 164 void 165 BOutlineListView::FrameMoved(BPoint newPosition) 166 { 167 BListView::FrameMoved(newPosition); 168 } 169 170 171 void 172 BOutlineListView::FrameResized(float newWidth, float newHeight) 173 { 174 BListView::FrameResized(newWidth, newHeight); 175 } 176 177 178 void 179 BOutlineListView::MouseUp(BPoint where) 180 { 181 BListView::MouseUp(where); 182 } 183 184 185 bool 186 BOutlineListView::AddUnder(BListItem* item, BListItem* superitem) 187 { 188 if (superitem == NULL) 189 return AddItem(item); 190 191 fFullList.AddItem(item, FullListIndexOf(superitem) + 1); 192 193 item->fLevel = superitem->OutlineLevel() + 1; 194 superitem->fHasSubitems = true; 195 196 if (superitem->IsItemVisible() && superitem->IsExpanded()) { 197 item->SetItemVisible(true); 198 199 int32 index = BListView::IndexOf(superitem); 200 201 BListView::AddItem(item, index + 1); 202 Invalidate(LatchRect(ItemFrame(index), superitem->OutlineLevel())); 203 } else 204 item->SetItemVisible(false); 205 206 return true; 207 } 208 209 210 bool 211 BOutlineListView::AddItem(BListItem* item) 212 { 213 return AddItem(item, FullListCountItems()); 214 } 215 216 217 bool 218 BOutlineListView::AddItem(BListItem* item, int32 fullListIndex) 219 { 220 if (fullListIndex < 0) 221 fullListIndex = 0; 222 else if (fullListIndex > FullListCountItems()) 223 fullListIndex = FullListCountItems(); 224 225 if (!fFullList.AddItem(item, fullListIndex)) 226 return false; 227 228 // Check if this item is visible, and if it is, add it to the 229 // other list, too 230 231 if (item->fLevel > 0) { 232 BListItem *super = _SuperitemForIndex(fullListIndex, item->fLevel); 233 if (super == NULL) 234 return true; 235 236 bool hadSubitems = super->fHasSubitems; 237 super->fHasSubitems = true; 238 239 if (!super->IsItemVisible() || !super->IsExpanded()) { 240 item->SetItemVisible(false); 241 return true; 242 } 243 244 if (!hadSubitems) 245 Invalidate(LatchRect(ItemFrame(IndexOf(super)), super->OutlineLevel())); 246 } 247 248 int32 listIndex = _FindPreviousVisibleIndex(fullListIndex); 249 250 if (!BListView::AddItem(item, IndexOf(FullListItemAt(listIndex)) + 1)) { 251 // adding didn't work out, we need to remove it from the main list again 252 fFullList.RemoveItem(fullListIndex); 253 return false; 254 } 255 256 return true; 257 } 258 259 260 bool 261 BOutlineListView::AddList(BList* newItems) 262 { 263 return AddList(newItems, FullListCountItems()); 264 } 265 266 267 bool 268 BOutlineListView::AddList(BList* newItems, int32 fullListIndex) 269 { 270 if ((newItems == NULL) || (newItems->CountItems() == 0)) 271 return false; 272 273 for (int32 i = 0; i < newItems->CountItems(); i++) 274 AddItem((BListItem *)newItems->ItemAt(i), fullListIndex + i); 275 276 return true; 277 } 278 279 280 bool 281 BOutlineListView::RemoveItem(BListItem* item) 282 { 283 return _RemoveItem(item, FullListIndexOf(item)) != NULL; 284 } 285 286 287 BListItem* 288 BOutlineListView::RemoveItem(int32 fullIndex) 289 { 290 return _RemoveItem(FullListItemAt(fullIndex), fullIndex); 291 } 292 293 294 bool 295 BOutlineListView::RemoveItems(int32 fullIndex, int32 count) 296 { 297 if (fullIndex >= FullListCountItems()) 298 fullIndex = -1; 299 if (fullIndex < 0) 300 return false; 301 302 // TODO: very bad for performance!! 303 while (count--) { 304 BOutlineListView::RemoveItem(fullIndex); 305 } 306 307 return true; 308 } 309 310 311 BListItem * 312 BOutlineListView::FullListItemAt(int32 fullListIndex) const 313 { 314 return (BListItem*)fFullList.ItemAt(fullListIndex); 315 } 316 317 318 int32 319 BOutlineListView::FullListIndexOf(BPoint point) const 320 { 321 int32 index = BListView::IndexOf(point); 322 323 if (index > 0) 324 index = FullListIndex(index); 325 326 return index; 327 } 328 329 330 int32 331 BOutlineListView::FullListIndexOf(BListItem* item) const 332 { 333 return fFullList.IndexOf(item); 334 } 335 336 337 BListItem * 338 BOutlineListView::FullListFirstItem() const 339 { 340 return (BListItem*)fFullList.FirstItem(); 341 } 342 343 344 BListItem * 345 BOutlineListView::FullListLastItem() const 346 { 347 return (BListItem*)fFullList.LastItem(); 348 } 349 350 351 bool 352 BOutlineListView::FullListHasItem(BListItem *item) const 353 { 354 return fFullList.HasItem(item); 355 } 356 357 358 int32 359 BOutlineListView::FullListCountItems() const 360 { 361 return fFullList.CountItems(); 362 } 363 364 365 int32 366 BOutlineListView::FullListCurrentSelection(int32 index) const 367 { 368 int32 i = BListView::CurrentSelection(index); 369 370 BListItem *item = BListView::ItemAt(i); 371 if (item) 372 return fFullList.IndexOf(item); 373 374 return -1; 375 } 376 377 378 void 379 BOutlineListView::MakeEmpty() 380 { 381 fFullList.MakeEmpty(); 382 BListView::MakeEmpty(); 383 } 384 385 386 bool 387 BOutlineListView::FullListIsEmpty() const 388 { 389 return fFullList.IsEmpty(); 390 } 391 392 393 void 394 BOutlineListView::FullListDoForEach(bool(*func)(BListItem* item)) 395 { 396 fFullList.DoForEach(reinterpret_cast<bool (*)(void*)>(func)); 397 } 398 399 400 void 401 BOutlineListView::FullListDoForEach(bool (*func)(BListItem* item, void* arg), 402 void* arg) 403 { 404 fFullList.DoForEach(reinterpret_cast<bool (*)(void*, void*)>(func), arg); 405 } 406 407 408 BListItem * 409 BOutlineListView::Superitem(const BListItem* item) 410 { 411 int32 index = FullListIndexOf((BListItem*)item); 412 if (index == -1) 413 return NULL; 414 415 return _SuperitemForIndex(index, item->OutlineLevel()); 416 } 417 418 419 void 420 BOutlineListView::Expand(BListItem* item) 421 { 422 if (item->IsExpanded() || !FullListHasItem(item)) 423 return; 424 425 item->fExpanded = true; 426 427 uint32 level = item->fLevel; 428 int32 fullIndex = FullListIndexOf(item); 429 int32 index = IndexOf(item) + 1; 430 int32 startIndex = index; 431 int32 count = FullListCountItems() - fullIndex - 1; 432 BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1; 433 434 BFont font; 435 GetFont(&font); 436 while (count-- > 0) { 437 item = items[0]; 438 if (item->fLevel <= level) 439 break; 440 441 if (!item->IsItemVisible()) { 442 // fix selection hints 443 if (index <= fFirstSelected) 444 fFirstSelected++; 445 if (index <= fLastSelected) 446 fLastSelected++; 447 448 fList.AddItem(item, index++); 449 item->Update(this, &font); 450 item->SetItemVisible(true); 451 } 452 453 if (item->HasSubitems() && !item->IsExpanded()) { 454 // Skip hidden children 455 uint32 subLevel = item->fLevel; 456 items++; 457 458 while (--count > 0 && items[0]->fLevel > subLevel) 459 items++; 460 } else 461 items++; 462 } 463 _RecalcItemTops(startIndex); 464 _FixupScrollBar(); 465 Invalidate(); 466 } 467 468 469 void 470 BOutlineListView::Collapse(BListItem* item) 471 { 472 if (!item->IsExpanded() || !FullListHasItem(item)) 473 return; 474 475 item->fExpanded = false; 476 477 uint32 level = item->fLevel; 478 int32 fullIndex = FullListIndexOf(item); 479 int32 index = IndexOf(item); 480 int32 startIndex = index; 481 int32 max = FullListCountItems() - fullIndex - 1; 482 int32 count = 0; 483 BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1; 484 485 while (max-- > 0) { 486 item = items[0]; 487 if (item->fLevel <= level) 488 break; 489 490 if (item->IsItemVisible()) { 491 fList.RemoveItem(item); 492 item->SetItemVisible(false); 493 if (item->IsSelected()) 494 item->Deselect(); 495 496 count++; 497 } 498 499 items++; 500 } 501 502 _RecalcItemTops(startIndex); 503 // fix selection hints 504 // TODO: revise for multi selection lists 505 if (index < fFirstSelected) { 506 if (index + count < fFirstSelected) { 507 fFirstSelected -= count; 508 fLastSelected -= count; 509 } else { 510 // select top item 511 //fFirstSelected = fLastSelected = index; 512 //item->Select(); 513 Select(index); 514 } 515 } 516 517 _FixupScrollBar(); 518 Invalidate(); 519 } 520 521 522 bool 523 BOutlineListView::IsExpanded(int32 fullListIndex) 524 { 525 BListItem *item = FullListItemAt(fullListIndex); 526 if (!item) 527 return false; 528 529 return item->IsExpanded(); 530 } 531 532 533 BHandler * 534 BOutlineListView::ResolveSpecifier(BMessage* msg, int32 index, 535 BMessage* specifier, int32 what, const char* property) 536 { 537 return BListView::ResolveSpecifier(msg, index, specifier, what, property); 538 } 539 540 541 status_t 542 BOutlineListView::GetSupportedSuites(BMessage* data) 543 { 544 return BListView::GetSupportedSuites(data); 545 } 546 547 548 status_t 549 BOutlineListView::Perform(perform_code d, void* arg) 550 { 551 return BListView::Perform(d, arg); 552 } 553 554 555 void 556 BOutlineListView::ResizeToPreferred() 557 { 558 BListView::ResizeToPreferred(); 559 } 560 561 562 void 563 BOutlineListView::GetPreferredSize(float* _width, float* _height) 564 { 565 BListView::GetPreferredSize(_width, _height); 566 } 567 568 569 void 570 BOutlineListView::MakeFocus(bool state) 571 { 572 BListView::MakeFocus(state); 573 } 574 575 576 void 577 BOutlineListView::AllAttached() 578 { 579 BListView::AllAttached(); 580 } 581 582 583 void 584 BOutlineListView::AllDetached() 585 { 586 BListView::AllDetached(); 587 } 588 589 590 void 591 BOutlineListView::DetachedFromWindow() 592 { 593 BListView::DetachedFromWindow(); 594 } 595 596 597 void 598 BOutlineListView::FullListSortItems(int (*compareFunc)(const BListItem* a, 599 const BListItem* b)) 600 { 601 SortItemsUnder(NULL, false, compareFunc); 602 } 603 604 605 void 606 BOutlineListView::SortItemsUnder(BListItem* underItem, bool oneLevelOnly, 607 int (*compareFunc)(const BListItem* a, const BListItem* b)) 608 { 609 // This method is quite complicated: basically, it creates a real tree 610 // from the items of the full list, sorts them as needed, and then 611 // populates the entries back into the full and display lists 612 613 int32 firstIndex = FullListIndexOf(underItem) + 1; 614 int32 lastIndex = firstIndex; 615 BList* tree = _BuildTree(underItem, lastIndex); 616 617 _SortTree(tree, oneLevelOnly, compareFunc); 618 619 // Populate to the full list 620 _PopulateTree(tree, fFullList, firstIndex, false); 621 622 if (underItem == NULL || (underItem->IsItemVisible() && underItem->IsExpanded())) { 623 // Populate to BListView's list 624 firstIndex = fList.IndexOf(underItem) + 1; 625 lastIndex = firstIndex; 626 _PopulateTree(tree, fList, lastIndex, true); 627 628 if (fFirstSelected != -1) { 629 // update selection hints 630 fFirstSelected = _CalcFirstSelected(0); 631 fLastSelected = _CalcLastSelected(CountItems()); 632 } 633 634 // only invalidate what may have changed 635 _RecalcItemTops(firstIndex); 636 BRect top = ItemFrame(firstIndex); 637 BRect bottom = ItemFrame(lastIndex - 1); 638 BRect update(top.left, top.top, bottom.right, bottom.bottom); 639 Invalidate(update); 640 } 641 642 _DestructTree(tree); 643 } 644 645 void 646 BOutlineListView::_PopulateTree(BList* tree, BList& target, 647 int32& firstIndex, bool onlyVisible) 648 { 649 BListItem** items = (BListItem**)target.Items(); 650 int32 count = tree->CountItems(); 651 652 for (int32 index = 0; index < count; index++) { 653 BListItem* item = (BListItem*)tree->ItemAtFast(index); 654 655 items[firstIndex++] = item; 656 657 if (item->HasSubitems() && (!onlyVisible || item->IsExpanded())) 658 _PopulateTree(item->fTemporaryList, target, firstIndex, onlyVisible); 659 } 660 } 661 662 663 void 664 BOutlineListView::_SortTree(BList* tree, bool oneLevelOnly, 665 int (*compareFunc)(const BListItem* a, const BListItem* b)) 666 { 667 BListItem **items = (BListItem **)tree->Items(); 668 std::sort(items, items + tree->CountItems(), ListItemComparator(compareFunc)); 669 670 if (oneLevelOnly) 671 return; 672 673 for (int32 index = tree->CountItems(); index-- > 0;) { 674 BListItem* item = (BListItem*)tree->ItemAt(index); 675 676 if (item->HasSubitems()) 677 _SortTree(item->fTemporaryList, false, compareFunc); 678 } 679 } 680 681 682 void 683 BOutlineListView::_DestructTree(BList* tree) 684 { 685 for (int32 index = tree->CountItems(); index-- > 0;) { 686 BListItem* item = (BListItem*)tree->ItemAt(index); 687 688 if (item->HasSubitems()) 689 _DestructTree(item->fTemporaryList); 690 } 691 692 delete tree; 693 } 694 695 696 BList* 697 BOutlineListView::_BuildTree(BListItem* underItem, int32& fullIndex) 698 { 699 int32 fullCount = FullListCountItems(); 700 uint32 level = underItem != NULL ? underItem->OutlineLevel() + 1 : 0; 701 BList* list = new BList; 702 if (underItem != NULL) 703 underItem->fTemporaryList = list; 704 705 while (fullIndex < fullCount) { 706 BListItem *item = FullListItemAt(fullIndex); 707 708 // If we jump out of the subtree, break out 709 if (item->fLevel < level) 710 break; 711 712 // If the level matches, put them into the list 713 // (we handle the case of a missing sublevel gracefully) 714 list->AddItem(item); 715 fullIndex++; 716 717 if (item->HasSubitems()) { 718 // we're going deeper 719 _BuildTree(item, fullIndex); 720 } 721 } 722 723 return list; 724 } 725 726 727 int32 728 BOutlineListView::CountItemsUnder(BListItem* underItem, 729 bool oneLevelOnly) const 730 { 731 int32 i = FullListIndexOf(underItem); 732 if (i == -1) 733 return 0; 734 735 ++i; 736 int32 count = 0; 737 uint32 baseLevel = underItem->OutlineLevel(); 738 739 for (; i < FullListCountItems(); i++) { 740 BListItem *item = FullListItemAt(i); 741 742 // If we jump out of the subtree, return count 743 if (item->fLevel <= baseLevel) 744 return count; 745 746 // If the level matches, increase count 747 if (!oneLevelOnly || item->fLevel == baseLevel + 1) 748 count++; 749 } 750 751 return count; 752 } 753 754 755 BListItem * 756 BOutlineListView::EachItemUnder(BListItem *underItem, bool oneLevelOnly, 757 BListItem *(*eachFunc)(BListItem* item, void* arg), void* arg) 758 { 759 int32 i = IndexOf(underItem); 760 if (i == -1) 761 return NULL; 762 763 while (i < FullListCountItems()) { 764 BListItem *item = FullListItemAt(i); 765 766 // If we jump out of the subtree, return NULL 767 if (item->fLevel < underItem->OutlineLevel()) 768 return NULL; 769 770 // If the level matches, check the index 771 if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1) { 772 item = eachFunc(item, arg); 773 if (item != NULL) 774 return item; 775 } 776 777 i++; 778 } 779 780 return NULL; 781 } 782 783 784 BListItem * 785 BOutlineListView::ItemUnderAt(BListItem* underItem, 786 bool oneLevelOnly, int32 index) const 787 { 788 int32 i = IndexOf(underItem); 789 if (i == -1) 790 return NULL; 791 792 while (i < FullListCountItems()) { 793 BListItem *item = FullListItemAt(i); 794 795 // If we jump out of the subtree, return NULL 796 if (item->fLevel < underItem->OutlineLevel()) 797 return NULL; 798 799 // If the level matches, check the index 800 if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1) { 801 if (index == 0) 802 return item; 803 804 index--; 805 } 806 807 i++; 808 } 809 810 return NULL; 811 } 812 813 static void _GetSubItems(BList &sourceList, BList &destList, 814 BListItem *parent, int32 start) 815 { 816 for (int32 i = start; i < sourceList.CountItems(); i++) { 817 BListItem *item = (BListItem *)sourceList.ItemAt(i); 818 if (item->OutlineLevel() <= parent->OutlineLevel()) 819 break; 820 destList.AddItem(item); 821 } 822 } 823 824 static void 825 _DoSwap(BList &list, int32 firstIndex, int32 secondIndex, BList *firstItems, 826 BList *secondItems) 827 { 828 BListItem *item = (BListItem *)list.ItemAt(firstIndex); 829 list.SwapItems(firstIndex, secondIndex); 830 list.RemoveItems(secondIndex + 1, secondItems->CountItems()); 831 list.RemoveItems(firstIndex + 1, firstItems->CountItems()); 832 list.AddList(secondItems, firstIndex + 1); 833 int32 newIndex = list.IndexOf(item); 834 if (newIndex + 1 < list.CountItems()) 835 list.AddList(firstItems, newIndex + 1); 836 else 837 list.AddList(firstItems); 838 } 839 840 void 841 BOutlineListView::_CullInvisibleItems(BList &list) 842 { 843 int32 index = 0; 844 while (index < list.CountItems()) { 845 if (reinterpret_cast<BListItem *>(list.ItemAt(index))->IsItemVisible()) 846 ++index; 847 else 848 list.RemoveItem(index); 849 } 850 } 851 852 bool 853 BOutlineListView::_SwapItems(int32 first, int32 second) 854 { 855 // same item, do nothing 856 if (first == second) 857 return true; 858 859 // fail, first item out of bounds 860 if ((first < 0) || (first >= CountItems())) 861 return false; 862 863 // fail, second item out of bounds 864 if ((second < 0) || (second >= CountItems())) 865 return false; 866 867 int32 firstIndex = min_c(first, second); 868 int32 secondIndex = max_c(first, second); 869 BListItem *firstItem = ItemAt(firstIndex); 870 BListItem *secondItem = ItemAt(secondIndex); 871 BList firstSubItems, secondSubItems; 872 873 if (Superitem(firstItem) != Superitem(secondItem)) 874 return false; 875 if (!firstItem->IsItemVisible() || !secondItem->IsItemVisible()) 876 return false; 877 878 int32 fullFirstIndex = FullListIndex(firstIndex); 879 int32 fullSecondIndex = FullListIndex(secondIndex); 880 _GetSubItems(fFullList, firstSubItems, firstItem, fullFirstIndex + 1); 881 _GetSubItems(fFullList, secondSubItems, secondItem, fullSecondIndex + 1); 882 _DoSwap(fFullList, fullFirstIndex, fullSecondIndex, &firstSubItems, 883 &secondSubItems); 884 885 _CullInvisibleItems(firstSubItems); 886 _CullInvisibleItems(secondSubItems); 887 _DoSwap(fList, firstIndex, secondIndex, &firstSubItems, 888 &secondSubItems); 889 890 _RecalcItemTops(firstIndex); 891 _RescanSelection(firstIndex, secondIndex + secondSubItems.CountItems()); 892 Invalidate(Bounds()); 893 return true; 894 } 895 896 bool 897 BOutlineListView::DoMiscellaneous(MiscCode code, MiscData* data) 898 { 899 if (code == B_SWAP_OP) 900 return _SwapItems(data->swap.a, data->swap.b); 901 902 return BListView::DoMiscellaneous(code, data); 903 } 904 905 906 void 907 BOutlineListView::MessageReceived(BMessage* msg) 908 { 909 BListView::MessageReceived(msg); 910 } 911 912 913 void BOutlineListView::_ReservedOutlineListView1() {} 914 void BOutlineListView::_ReservedOutlineListView2() {} 915 void BOutlineListView::_ReservedOutlineListView3() {} 916 void BOutlineListView::_ReservedOutlineListView4() {} 917 918 919 int32 920 BOutlineListView::FullListIndex(int32 index) const 921 { 922 BListItem *item = ItemAt(index); 923 924 if (item == NULL) 925 return -1; 926 927 return FullListIndexOf(item); 928 } 929 930 931 int32 932 BOutlineListView::ListViewIndex(int32 index) const 933 { 934 BListItem *item = ItemAt(index); 935 936 if (item == NULL) 937 return -1; 938 939 return BListView::IndexOf(item); 940 } 941 942 943 void 944 BOutlineListView::ExpandOrCollapse(BListItem *underItem, bool expand) 945 { 946 } 947 948 949 BRect 950 BOutlineListView::LatchRect(BRect itemRect, int32 level) const 951 { 952 return BRect(itemRect.left, itemRect.top, itemRect.left 953 + (level * 10.0f + 15.0f), itemRect.bottom); 954 } 955 956 957 void 958 BOutlineListView::DrawLatch(BRect itemRect, int32 level, bool collapsed, 959 bool highlighted, bool misTracked) 960 { 961 float left = level * 10.0f; 962 963 if (collapsed) { 964 SetHighColor(192, 192, 192); 965 966 FillTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f), 967 itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f), 968 itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f)); 969 970 SetHighColor(0, 0, 0); 971 972 StrokeTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f), 973 itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f), 974 itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f)); 975 } else { 976 SetHighColor(192, 192, 192); 977 978 FillTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f), 979 itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f), 980 itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f)); 981 982 SetHighColor(0, 0, 0); 983 984 StrokeTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f), 985 itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f), 986 itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f)); 987 } 988 } 989 990 991 void 992 BOutlineListView::DrawItem(BListItem* item, BRect itemRect, bool complete) 993 { 994 if (item->fHasSubitems) 995 DrawLatch(itemRect, item->fLevel, !item->IsExpanded(), false, false); 996 997 itemRect.left += (item->fLevel) * 10.0f + 15.0f; 998 item->DrawItem(this, itemRect, complete); 999 } 1000 1001 1002 /*! 1003 \brief Removes a single item from the list and all of its children. 1004 1005 Unlike the BeOS version, this one will actually delete the children, too, 1006 as there should be no reference left to them. This may cause problems for 1007 applications that actually take the misbehaviour of the Be classes into 1008 account. 1009 */ 1010 BListItem * 1011 BOutlineListView::_RemoveItem(BListItem* item, int32 fullIndex) 1012 { 1013 if (item == NULL || fullIndex < 0 || fullIndex >= FullListCountItems()) 1014 return NULL; 1015 1016 uint32 level = item->OutlineLevel(); 1017 int32 superIndex; 1018 BListItem* super = _SuperitemForIndex(fullIndex, level, &superIndex); 1019 1020 if (item->IsItemVisible()) { 1021 // remove children, too 1022 while (fullIndex + 1 < CountItems()) { 1023 BListItem *subItem = ItemAt(fullIndex + 1); 1024 1025 if (subItem->OutlineLevel() <= level) 1026 break; 1027 1028 if (subItem->IsItemVisible()) 1029 BListView::RemoveItem(subItem); 1030 1031 fFullList.RemoveItem(fullIndex + 1); 1032 delete subItem; 1033 } 1034 BListView::RemoveItem(item); 1035 } 1036 1037 fFullList.RemoveItem(fullIndex); 1038 1039 if (super != NULL) { 1040 // we might need to change the fHasSubitems field of the parent 1041 BListItem* child = FullListItemAt(superIndex + 1); 1042 if (child == NULL || child->OutlineLevel() <= super->OutlineLevel()) 1043 super->fHasSubitems = false; 1044 } 1045 return item; 1046 } 1047 1048 1049 BListItem * 1050 BOutlineListView::RemoveOne(int32 fullListIndex) 1051 { 1052 return NULL; 1053 } 1054 1055 1056 void 1057 BOutlineListView::TrackInLatchItem(void *) 1058 { 1059 } 1060 1061 1062 void 1063 BOutlineListView::TrackOutLatchItem(void *) 1064 { 1065 } 1066 1067 1068 bool 1069 BOutlineListView::OutlineSwapItems(int32 a, int32 b) 1070 { 1071 return false; 1072 } 1073 1074 1075 bool 1076 BOutlineListView::OutlineMoveItem(int32 from, int32 to) 1077 { 1078 return false; 1079 } 1080 1081 1082 bool 1083 BOutlineListView::OutlineReplaceItem(int32 index, BListItem *item) 1084 { 1085 return false; 1086 } 1087 1088 1089 void 1090 BOutlineListView::CommonMoveItems(int32 from, int32 count, int32 to) 1091 { 1092 } 1093 1094 1095 /*! 1096 Returns the super item before the item specified by \a fullListIndex 1097 and \a level. 1098 */ 1099 BListItem * 1100 BOutlineListView::_SuperitemForIndex(int32 fullListIndex, int32 level, 1101 int32* _superIndex) 1102 { 1103 BListItem *item; 1104 fullListIndex--; 1105 1106 while (fullListIndex >= 0) { 1107 if ((item = FullListItemAt(fullListIndex))->OutlineLevel() 1108 < (uint32)level) { 1109 if (_superIndex != NULL) 1110 *_superIndex = fullListIndex; 1111 return item; 1112 } 1113 1114 fullListIndex--; 1115 } 1116 1117 return NULL; 1118 } 1119 1120 1121 int32 1122 BOutlineListView::_FindPreviousVisibleIndex(int32 fullListIndex) 1123 { 1124 fullListIndex--; 1125 1126 while (fullListIndex >= 0) { 1127 if (FullListItemAt(fullListIndex)->fVisible) 1128 return fullListIndex; 1129 1130 fullListIndex--; 1131 } 1132 1133 return -1; 1134 } 1135 1136