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