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