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