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