1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2005, Haiku, Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: ListView.cpp 23 // Author: Ulrich Wimboeck 24 // Marc Flerackers (mflerackers@androme.be) 25 // Description: BListView represents a one-dimensional list view. 26 //------------------------------------------------------------------------------ 27 28 // Standard Includes ----------------------------------------------------------- 29 30 // System Includes ------------------------------------------------------------- 31 #include <stdio.h> 32 33 #include <ListView.h> 34 #include <ScrollBar.h> 35 #include <ScrollView.h> 36 #include <support/Errors.h> 37 #include <PropertyInfo.h> 38 #include <Window.h> 39 40 // Project Includes ------------------------------------------------------------ 41 42 // Local Includes -------------------------------------------------------------- 43 44 // Local Defines --------------------------------------------------------------- 45 46 // Globals --------------------------------------------------------------------- 47 static property_info prop_list[] = 48 { 49 { "Item", { B_COUNT_PROPERTIES, 0 }, { B_DIRECT_SPECIFIER, 0 }, 50 "Returns the number of BListItems currently in the list." }, 51 { "Item", { B_EXECUTE_PROPERTY, 0 }, { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 52 B_RANGE_SPECIFIER, B_REVERSE_RANGE_SPECIFIER, 0 }, 53 "Select and invoke the specified items, first removing any existing selection." }, 54 { "Selection", { B_COUNT_PROPERTIES, 0 }, { B_DIRECT_SPECIFIER, 0 }, 55 "Returns int32 count of items in the selection." }, 56 { "Selection", { B_EXECUTE_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, 57 "Invoke items in selection." }, 58 { "Selection", { B_GET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, 59 "Returns int32 indices of all items in the selection." }, 60 { "Selection", { B_SET_PROPERTY, 0 }, { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 61 B_RANGE_SPECIFIER, B_REVERSE_RANGE_SPECIFIER, 0 }, 62 "Extends current selection or deselects specified items. Boolean field \"data\" " 63 "chooses selection or deselection." }, 64 { "Selection", { B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, 65 "Select or deselect all items in the selection. Boolean field \"data\" chooses " 66 "selection or deselection." }, 67 }; 68 69 //------------------------------------------------------------------------------ 70 BListView::BListView(BRect frame, const char *name, list_view_type type, 71 uint32 resizingMode, uint32 flags) 72 : BView(frame, name, resizingMode, flags) 73 { 74 _InitObject(type); 75 } 76 //------------------------------------------------------------------------------ 77 BListView::BListView(BMessage *archive) 78 : BView(archive) 79 { 80 int32 listType; 81 82 archive->FindInt32("_lv_type", &listType); 83 fListType = (list_view_type)listType; 84 85 fFirstSelected = -1; 86 fLastSelected = -1; 87 fAnchorIndex = -1; 88 89 fSelectMessage = NULL; 90 fScrollView = NULL; 91 fTrack = NULL; 92 93 fWidth = Bounds().Width(); 94 95 int32 i = 0; 96 BMessage subData; 97 98 while (archive->FindMessage("_l_items", i++, &subData)) 99 { 100 BArchivable *object = instantiate_object(&subData); 101 102 if (!object) 103 continue; 104 105 BListItem *item = dynamic_cast<BListItem*>(object); 106 107 if (!item) 108 continue; 109 110 AddItem(item); 111 } 112 113 if (archive->HasMessage("_msg")) 114 { 115 BMessage *invokationMessage = new BMessage; 116 117 archive->FindMessage("_msg", invokationMessage); 118 SetInvocationMessage(invokationMessage); 119 } 120 121 if (archive->HasMessage("_2nd_msg")) 122 { 123 BMessage *selectionMessage = new BMessage; 124 125 archive->FindMessage("_2nd_msg", selectionMessage); 126 SetSelectionMessage(selectionMessage); 127 } 128 } 129 //------------------------------------------------------------------------------ 130 BListView::~BListView() 131 { 132 SetSelectionMessage(NULL); 133 134 if (fSelectMessage) 135 delete fSelectMessage; 136 } 137 //------------------------------------------------------------------------------ 138 BArchivable *BListView::Instantiate(BMessage *archive) 139 { 140 if (validate_instantiation(archive, "BListView")) 141 return new BListView(archive); 142 else 143 return NULL; 144 } 145 //------------------------------------------------------------------------------ 146 status_t BListView::Archive(BMessage *archive, bool deep) const 147 { 148 BView::Archive ( archive, deep ); 149 150 archive->AddInt32("_lv_type", fListType); 151 152 if (deep) 153 { 154 int32 i = 0; 155 BListItem *item; 156 157 while ((item = ItemAt(i++))) 158 { 159 BMessage subData; 160 161 if (item->Archive(&subData, true) != B_OK) 162 continue; 163 164 archive->AddMessage("_l_items", &subData); 165 } 166 } 167 168 if (InvocationMessage()) 169 archive->AddMessage("_msg", InvocationMessage()); 170 171 if (fSelectMessage) 172 archive->AddMessage("_2nd_msg", fSelectMessage); 173 174 return B_OK; 175 } 176 177 // Draw 178 void 179 BListView::Draw(BRect updateRect) 180 { 181 for (int i = 0; i < CountItems(); i++) { 182 BRect item_frame = ItemFrame(i); 183 184 if (item_frame.Intersects(updateRect)) 185 DrawItem(((BListItem*)ItemAt(i)), item_frame); 186 } 187 } 188 189 // MessageReceived 190 void 191 BListView::MessageReceived(BMessage* msg) 192 { 193 switch (msg->what) { 194 case B_COUNT_PROPERTIES: 195 case B_EXECUTE_PROPERTY: 196 case B_GET_PROPERTY: 197 case B_SET_PROPERTY: 198 { 199 BPropertyInfo propInfo ( prop_list ); 200 BMessage specifier; 201 const char *property; 202 203 if ( msg->GetCurrentSpecifier ( NULL, &specifier ) != B_OK || 204 specifier.FindString ( "property", &property ) != B_OK ) 205 return; 206 207 switch ( propInfo.FindMatch ( msg, 0, &specifier, msg->what, property ) ) 208 { 209 case B_ERROR: 210 BView::MessageReceived ( msg ); 211 break; 212 213 case 0: 214 { 215 BMessage reply ( B_REPLY ); 216 217 reply.AddInt32 ( "result", CountItems () ); 218 reply.AddInt32 ( "error", B_OK ); 219 220 msg->SendReply ( &reply ); 221 break; 222 } 223 case 1: 224 break; 225 case 2: 226 { 227 BMessage reply ( B_REPLY ); 228 229 int32 count = 0; 230 231 for ( int32 i = 0; i < CountItems (); i++ ) 232 if ( ItemAt ( i )->IsSelected () ) 233 count++; 234 235 reply.AddInt32 ( "result", count ); 236 reply.AddInt32 ( "error", B_OK ); 237 238 msg->SendReply ( &reply ); 239 break; 240 } 241 case 3: 242 break; 243 case 4: 244 { 245 BMessage reply ( B_REPLY ); 246 247 for ( int32 i = 0; i < CountItems (); i++ ) 248 if ( ItemAt ( i )->IsSelected () ) 249 reply.AddInt32 ( "result", i ); 250 251 reply.AddInt32 ( "error", B_OK ); 252 253 msg->SendReply ( &reply ); 254 break; 255 } 256 case 5: 257 break; 258 case 6: 259 { 260 BMessage reply ( B_REPLY ); 261 262 bool select; 263 264 msg->FindBool ( "data", &select ); 265 266 if ( select ) 267 Select ( 0, CountItems () - 1, false ); 268 else 269 DeselectAll (); 270 271 reply.AddInt32 ( "error", B_OK ); 272 273 msg->SendReply ( &reply ); 274 break; 275 } 276 } 277 278 break; 279 } 280 case B_SELECT_ALL: 281 { 282 Select(0, CountItems() - 1, false); 283 break; 284 } 285 default: 286 BView::MessageReceived(msg); 287 } 288 } 289 290 // MouseDown 291 void 292 BListView::MouseDown(BPoint point) 293 { 294 if (!IsFocus()) { 295 MakeFocus(); 296 Sync(); 297 Window()->UpdateIfNeeded(); 298 } 299 300 BMessage *message = Looper()->CurrentMessage(); 301 int32 clicks; 302 int32 modifiers; 303 304 message->FindInt32("clicks", &clicks); 305 message->FindInt32("modifiers", &modifiers); 306 307 int index = IndexOf(point); 308 if (index > -1) { 309 if (fListType == B_MULTIPLE_SELECTION_LIST) { 310 if (modifiers & B_CONTROL_KEY) { 311 // select entire block 312 // TODO: maybe review if we want it like in Tracker (anchor item) 313 Select(min_c(index, fFirstSelected), max_c(index, fLastSelected)); 314 } else { 315 if (modifiers & B_SHIFT_KEY) { 316 // toggle selection state of clicked item (like in Tracker) 317 // toggle selection state of clicked item 318 if (clicks == 1) { 319 if (ItemAt(index)->IsSelected()) 320 Deselect(index); 321 else 322 Select(index, true); 323 } 324 } else { 325 Select(index); 326 } 327 } 328 } else { 329 // toggle selection state of clicked item 330 if ((modifiers & B_SHIFT_KEY) && ItemAt(index)->IsSelected() && clicks == 1) 331 Deselect(index); 332 else 333 Select(index); 334 } 335 } else { 336 if (!(modifiers & B_SHIFT_KEY)) 337 DeselectAll(); 338 } 339 } 340 341 // KeyDown 342 void 343 BListView::KeyDown(const char *bytes, int32 numBytes) 344 { 345 switch (bytes[0]) { 346 case B_UP_ARROW: 347 { 348 if (fFirstSelected == -1) 349 break; 350 351 bool extend = false; 352 353 if (fListType == B_MULTIPLE_SELECTION_LIST && (modifiers() & B_SHIFT_KEY)) 354 extend = true; 355 356 Select (fFirstSelected - 1, extend); 357 358 ScrollToSelection (); 359 360 break; 361 } 362 case B_DOWN_ARROW: 363 { 364 if (fFirstSelected == -1) 365 break; 366 367 bool extend = false; 368 369 if (fListType == B_MULTIPLE_SELECTION_LIST && (modifiers() & B_SHIFT_KEY)) 370 extend = true; 371 372 Select (fLastSelected + 1, extend); 373 374 ScrollToSelection (); 375 376 break; 377 } 378 case B_HOME: 379 { 380 Select ( 0, fListType == B_MULTIPLE_SELECTION_LIST ); 381 382 ScrollToSelection (); 383 384 break; 385 } 386 case B_END: 387 { 388 Select ( CountItems () - 1, fListType == B_MULTIPLE_SELECTION_LIST ); 389 390 ScrollToSelection (); 391 392 break; 393 } 394 case B_RETURN: 395 case B_SPACE: 396 { 397 Invoke (); 398 399 break; 400 } 401 default: 402 BView::KeyDown ( bytes, numBytes ); 403 } 404 } 405 406 // MakeFocus 407 void 408 BListView::MakeFocus(bool focused) 409 { 410 if (IsFocus() == focused) 411 return; 412 413 BView::MakeFocus(focused); 414 415 if (fScrollView) 416 fScrollView->SetBorderHighlighted(focused); 417 } 418 419 // FrameResized 420 void 421 BListView::FrameResized(float width, float height) 422 { 423 // TODO 424 _FixupScrollBar(); 425 } 426 427 // TargetedByScrollView 428 void 429 BListView::TargetedByScrollView(BScrollView *view) 430 { 431 fScrollView = view; 432 } 433 434 // ScrollTo 435 void 436 BListView::ScrollTo(BPoint point) 437 { 438 BView::ScrollTo(point); 439 fWidth = Bounds().right; 440 } 441 442 // AddItem 443 bool 444 BListView::AddItem(BListItem *item, int32 index) 445 { 446 if (!fList.AddItem(item, index)) 447 return false; 448 449 if (fFirstSelected != -1 && index < fFirstSelected) 450 fFirstSelected++; 451 452 if (fLastSelected != -1 && index < fLastSelected) 453 fLastSelected++; 454 455 if (Window()) { 456 BFont font; 457 GetFont(&font); 458 459 item->Update(this, &font); 460 461 _FixupScrollBar(); 462 _InvalidateFrom(index); 463 } 464 465 return true; 466 } 467 468 // AddItem 469 bool 470 BListView::AddItem(BListItem* item) 471 { 472 if (!fList.AddItem(item)) 473 return false; 474 475 if (Window()) { 476 BFont font; 477 GetFont(&font); 478 479 item->Update(this, &font); 480 481 _FixupScrollBar(); 482 InvalidateItem(CountItems() - 1); 483 } 484 485 return true; 486 } 487 488 // AddList 489 bool 490 BListView::AddList(BList* list, int32 index) 491 { 492 if (!fList.AddList(list, index)) 493 return false; 494 495 int32 count = fList.CountItems(); 496 497 if (fFirstSelected != -1 && index < fFirstSelected) 498 fFirstSelected += count; 499 500 if (fLastSelected != -1 && index < fLastSelected) 501 fLastSelected += count; 502 503 if (Window()) { 504 BFont font; 505 GetFont(&font); 506 507 int32 i = 0; 508 while(BListItem *item = (BListItem*)list->ItemAt(i)) { 509 item->Update(this, &font); 510 i++; 511 } 512 513 _FixupScrollBar(); 514 Invalidate(); // TODO 515 } 516 517 return true; 518 } 519 520 // AddList 521 bool 522 BListView::AddList(BList* list) 523 { 524 return AddList(list, CountItems()); 525 } 526 527 // RemoveItem 528 BListItem* 529 BListView::RemoveItem(int32 index) 530 { 531 BListItem *item = ItemAt(index); 532 533 if (!item) 534 return NULL; 535 536 if (item->IsSelected()) 537 Deselect(index); 538 539 if (!fList.RemoveItem(item)) 540 return NULL; 541 542 if (fFirstSelected != -1 && index < fFirstSelected) 543 fFirstSelected--; 544 545 if (fLastSelected != -1 && index < fLastSelected) 546 fLastSelected--; 547 548 _InvalidateFrom(index); 549 _FixupScrollBar(); 550 551 return item; 552 } 553 554 // RemoveItem 555 bool 556 BListView::RemoveItem(BListItem *item) 557 { 558 return RemoveItem(IndexOf(item)) != NULL; 559 } 560 561 // RemoveItems 562 bool 563 BListView::RemoveItems(int32 index, int32 count) 564 { 565 if (index >= CountItems()) 566 index = -1; 567 568 if (index < 0) 569 return false; 570 571 // TODO: very bad for performance!! 572 while (count--) 573 RemoveItem(index); 574 575 return true; 576 } 577 578 // SetSelectionMessage 579 void 580 BListView::SetSelectionMessage(BMessage* message) 581 { 582 delete fSelectMessage; 583 fSelectMessage = message; 584 } 585 586 // SetInvocationMessage 587 void 588 BListView::SetInvocationMessage(BMessage* message) 589 { 590 BInvoker::SetMessage(message); 591 } 592 593 // InvocationMessage 594 BMessage* 595 BListView::InvocationMessage() const 596 { 597 return BInvoker::Message(); 598 } 599 600 // InvocationCommand 601 uint32 602 BListView::InvocationCommand() const 603 { 604 return BInvoker::Command(); 605 } 606 607 // SelectionMessage 608 BMessage* 609 BListView::SelectionMessage() const 610 { 611 return fSelectMessage; 612 } 613 614 // SelectionCommand 615 uint32 616 BListView::SelectionCommand() const 617 { 618 if (fSelectMessage) 619 return fSelectMessage->what; 620 else 621 return 0; 622 } 623 624 // SetListType 625 void 626 BListView::SetListType(list_view_type type) 627 { 628 if (fListType == B_MULTIPLE_SELECTION_LIST && 629 type == B_SINGLE_SELECTION_LIST) 630 Select(CurrentSelection(0)); 631 632 fListType = type; 633 } 634 //------------------------------------------------------------------------------ 635 list_view_type BListView::ListType() const 636 { 637 return fListType; 638 } 639 640 // ItemAt 641 BListItem* 642 BListView::ItemAt(int32 index) const 643 { 644 return (BListItem*)fList.ItemAt(index); 645 } 646 647 // IndexOf 648 int32 649 BListView::IndexOf(BListItem *item) const 650 { 651 return fList.IndexOf(item); 652 } 653 654 // IndexOf 655 int32 656 BListView::IndexOf(BPoint point) const 657 { 658 float y = 0.0f; 659 660 // TODO: somehow binary search, but items don't know their frame 661 for (int i = 0; i < fList.CountItems(); i++) { 662 y += ItemAt(i)->Height(); 663 664 if (point.y < y) 665 return i; 666 } 667 668 return -1; 669 } 670 671 // FirstItem 672 BListItem* 673 BListView::FirstItem() const 674 { 675 return (BListItem*)fList.FirstItem(); 676 } 677 678 // LastItem 679 BListItem* 680 BListView::LastItem() const 681 { 682 return (BListItem*)fList.LastItem(); 683 } 684 685 // HasItem 686 bool 687 BListView::HasItem(BListItem *item) const 688 { 689 return fList.HasItem(item); 690 } 691 692 // CountItems 693 int32 694 BListView::CountItems() const 695 { 696 return fList.CountItems(); 697 } 698 699 // MakeEmpty 700 void 701 BListView::MakeEmpty() 702 { 703 _DeselectAll(-1, -1); 704 fList.MakeEmpty(); 705 706 Invalidate(); 707 } 708 709 // IsEmpty 710 bool 711 BListView::IsEmpty() const 712 { 713 return fList.IsEmpty(); 714 } 715 716 // DoForEach 717 void 718 BListView::DoForEach(bool (*func)(BListItem*)) 719 { 720 fList.DoForEach(reinterpret_cast<bool (*)(void*)>(func)); 721 } 722 723 // DoForEach 724 void 725 BListView::DoForEach(bool (*func)(BListItem*, void*), void* arg ) 726 { 727 fList.DoForEach(reinterpret_cast<bool (*)(void*, void*)>(func), arg); 728 } 729 730 // Items 731 const BListItem** 732 BListView::Items() const 733 { 734 return (const BListItem**)fList.Items(); 735 } 736 737 // InvalidateItem 738 void 739 BListView::InvalidateItem(int32 index) 740 { 741 Invalidate(ItemFrame(index)); 742 } 743 744 // ScrollToSelection 745 void 746 BListView::ScrollToSelection() 747 { 748 BRect item_frame = ItemFrame ( CurrentSelection ( 0 ) ); 749 750 if ( Bounds ().Intersects ( item_frame.InsetByCopy ( 0.0f, 2.0f ) ) ) 751 return; 752 753 if ( item_frame.top < Bounds ().top ) 754 ScrollTo ( 0, item_frame.top ); 755 else 756 ScrollTo ( 0, item_frame.bottom - Bounds ().Height () ); 757 } 758 759 // Select 760 void 761 BListView::Select(int32 index, bool extend) 762 { 763 _Select(index, extend); 764 765 SelectionChanged(); 766 InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED); 767 } 768 769 // Select 770 void 771 BListView::Select(int32 start, int32 finish, bool extend) 772 { 773 _Select(start, finish, extend); 774 775 SelectionChanged(); 776 InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED); 777 } 778 779 // IsItemSelected 780 bool 781 BListView::IsItemSelected(int32 index) const 782 { 783 BListItem *item = ItemAt(index); 784 785 if (item) 786 return item->IsSelected(); 787 else 788 return false; 789 } 790 791 // CurrentSelection 792 int32 793 BListView::CurrentSelection(int32 index) const 794 { 795 if (fFirstSelected == -1) 796 return -1; 797 798 if (index == 0) 799 return fFirstSelected; 800 801 for (int32 i = fFirstSelected; i <= fLastSelected; i++) { 802 if (ItemAt(i)->IsSelected()) { 803 if (index == 0) 804 return i; 805 806 index--; 807 } 808 } 809 810 return -1; 811 } 812 813 // Invoke 814 status_t 815 BListView::Invoke(BMessage *message) 816 { 817 bool notify = false; 818 uint32 kind = InvokeKind(¬ify); 819 820 BMessage clone(kind); 821 status_t err = B_BAD_VALUE; 822 823 if (!message && !notify) 824 message = Message(); 825 826 if (!message) { 827 if (!IsWatched()) 828 return err; 829 } else 830 clone = *message; 831 832 clone.AddInt64("when", (int64)system_time()); 833 clone.AddPointer("source", this); 834 clone.AddMessenger("be:sender", BMessenger(this)); 835 836 if (fListType == B_SINGLE_SELECTION_LIST) 837 clone.AddInt32("index", fFirstSelected); 838 else { 839 for (int32 i = fFirstSelected; i <= fLastSelected; i++) { 840 if (ItemAt(i)->IsSelected()) 841 clone.AddInt32("index", i); 842 } 843 } 844 845 if (message) 846 err = BInvoker::Invoke(&clone); 847 848 // TODO: assynchronous messaging 849 // SendNotices(kind, &clone); 850 851 return err; 852 } 853 854 // DeselectAll 855 void 856 BListView::DeselectAll() 857 { 858 if (fFirstSelected == -1) 859 return; 860 861 for (int32 index = fFirstSelected; index <= fLastSelected; index++) { 862 if (ItemAt(index)->IsSelected()) { 863 ItemAt(index)->Deselect(); 864 InvalidateItem(index); 865 } 866 } 867 fFirstSelected = fLastSelected = -1; 868 869 SelectionChanged(); 870 InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED); 871 } 872 873 // DeselectExcept 874 void 875 BListView::DeselectExcept(int32 start, int32 finish) 876 { 877 if (fFirstSelected == -1 || finish < start) 878 return; 879 880 int32 index; 881 882 // TODO: check if the items from start to finish are 883 // supposed to be selected if not already 884 for (index = fFirstSelected; index < start; index++) { 885 if (ItemAt(index)->IsSelected()) { 886 ItemAt(index)->Deselect(); 887 InvalidateItem(index); 888 } 889 } 890 for (index = finish + 1; index <= fLastSelected; index++) { 891 if (ItemAt(index)->IsSelected()) { 892 ItemAt(index)->Deselect(); 893 InvalidateItem(index); 894 } 895 } 896 fFirstSelected = max_c(fFirstSelected, start); 897 fLastSelected = min_c(fLastSelected, finish); 898 899 SelectionChanged(); 900 InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED); 901 } 902 903 // Deselect 904 void 905 BListView::Deselect(int32 index) 906 { 907 if (_Deselect(index)) { 908 SelectionChanged(); 909 InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED); 910 } 911 } 912 913 // SelectionChanged 914 void BListView::SelectionChanged() 915 { 916 } 917 918 // SortItems 919 void 920 BListView::SortItems(int (*cmp)(const void *, const void *)) 921 { 922 if (_DeselectAll(-1, -1)) { 923 SelectionChanged(); 924 InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED); 925 } 926 927 fList.SortItems(cmp); 928 Invalidate(); 929 } 930 931 // SwapItems 932 bool 933 BListView::SwapItems(int32 a, int32 b) 934 { 935 MiscData data; 936 937 data.swap.a = a; 938 data.swap.b = b; 939 940 return DoMiscellaneous(B_SWAP_OP, &data); 941 } 942 943 // MoveItem 944 bool 945 BListView::MoveItem(int32 from, int32 to) 946 { 947 MiscData data; 948 949 data.move.from = from; 950 data.move.to = to; 951 952 return DoMiscellaneous(B_MOVE_OP, &data); 953 } 954 955 // ReplaceItem 956 bool 957 BListView::ReplaceItem(int32 index, BListItem *item) 958 { 959 MiscData data; 960 961 data.replace.index = index; 962 data.replace.item = item; 963 964 return DoMiscellaneous(B_REPLACE_OP, &data); 965 } 966 967 // AttachedToWindow 968 void 969 BListView::AttachedToWindow() 970 { 971 BView::AttachedToWindow(); 972 _FontChanged(); 973 974 if (!Messenger().IsValid()) 975 SetTarget(Window(), NULL); 976 977 _FixupScrollBar(); 978 } 979 980 // FrameMoved 981 void 982 BListView::FrameMoved(BPoint new_position) 983 { 984 BView::FrameMoved(new_position); 985 } 986 987 // ItemFrame 988 BRect 989 BListView::ItemFrame(int32 index) 990 { 991 BRect frame(0, 0, Bounds().Width(), -1); 992 993 if (index < 0 || index >= CountItems()) 994 return frame; 995 996 for (int32 i = 0; i <= index; i++) { 997 frame.top = frame.bottom + 1; 998 frame.bottom += (float)ceil(ItemAt(i)->Height()); 999 } 1000 1001 return frame; 1002 } 1003 1004 // ResolveSpecifier 1005 BHandler* 1006 BListView::ResolveSpecifier(BMessage *msg, int32 index, 1007 BMessage *specifier, int32 form, 1008 const char *property) 1009 { 1010 BPropertyInfo propInfo(prop_list); 1011 1012 if (propInfo.FindMatch(msg, 0, specifier, form, property) < 0) 1013 return BView::ResolveSpecifier(msg, index, specifier, form, property); 1014 1015 // TODO: msg->AddInt32("_match_code_", ); 1016 1017 return this; 1018 } 1019 1020 // GetSupportedSuites 1021 status_t 1022 BListView::GetSupportedSuites(BMessage *data ) 1023 { 1024 BPropertyInfo propertyInfo(prop_list); 1025 1026 data->AddString("suites", "suite/vnd.Be-list-view"); 1027 data->AddFlat("messages", &propertyInfo); 1028 1029 return BView::GetSupportedSuites(data); 1030 } 1031 1032 // Perform 1033 status_t 1034 BListView::Perform(perform_code d, void *arg) 1035 { 1036 return BView::Perform(d, arg); 1037 } 1038 1039 // WindowActivated 1040 void 1041 BListView::WindowActivated(bool state) 1042 { 1043 BView::WindowActivated(state); 1044 1045 if (IsFocus()) { 1046 Invalidate(); 1047 } 1048 } 1049 1050 // MouseUp 1051 void 1052 BListView::MouseUp(BPoint pt) 1053 { 1054 if (fWidth == 0) 1055 return; 1056 1057 DoMouseMoved(pt); 1058 DoMouseUp(pt); 1059 } 1060 1061 // MouseMoved 1062 void 1063 BListView::MouseMoved(BPoint pt, uint32 code, const BMessage *msg) 1064 { 1065 if (fTrack == NULL) 1066 return; 1067 1068 if (_TryInitiateDrag(pt)) 1069 return; 1070 1071 DoMouseMoved(pt); 1072 } 1073 1074 // DetachedFromWindow 1075 void 1076 BListView::DetachedFromWindow() 1077 { 1078 BView::DetachedFromWindow(); 1079 } 1080 1081 // InitiateDrag 1082 bool 1083 BListView::InitiateDrag(BPoint point, int32 index, bool wasSelected) 1084 { 1085 return false; 1086 } 1087 1088 // ResizeToPreferred 1089 void 1090 BListView::ResizeToPreferred() 1091 { 1092 BView::ResizeToPreferred(); 1093 } 1094 1095 // GetPreferredSize 1096 void 1097 BListView::GetPreferredSize(float *width, float *height) 1098 { 1099 BView::GetPreferredSize(width, height); 1100 } 1101 1102 // AllAttached 1103 void 1104 BListView::AllAttached() 1105 { 1106 BView::AllAttached(); 1107 } 1108 1109 // AllDetached 1110 void 1111 BListView::AllDetached() 1112 { 1113 BView::AllDetached(); 1114 } 1115 1116 // DoMiscellaneous 1117 bool 1118 BListView::DoMiscellaneous(MiscCode code, MiscData *data) 1119 { 1120 if (code > B_SWAP_OP) 1121 return false; 1122 1123 switch (code) 1124 { 1125 case B_NO_OP: 1126 { 1127 break; 1128 } 1129 case B_REPLACE_OP: 1130 { 1131 return ReplaceItem(data->replace.index, data->replace.item); 1132 } 1133 case B_MOVE_OP: 1134 { 1135 return MoveItem(data->move.from, data->move.to); 1136 } 1137 case B_SWAP_OP: 1138 { 1139 return SwapItems(data->swap.a, data->swap.b); 1140 } 1141 } 1142 1143 return false; 1144 } 1145 1146 void BListView::_ReservedListView2() {} 1147 void BListView::_ReservedListView3() {} 1148 void BListView::_ReservedListView4() {} 1149 1150 BListView &BListView::operator=(const BListView &) 1151 { 1152 return *this; 1153 } 1154 1155 // _InitObject 1156 void 1157 BListView::_InitObject(list_view_type type) 1158 { 1159 fListType = type; 1160 fFirstSelected = -1; 1161 fLastSelected = -1; 1162 fAnchorIndex = -1; 1163 fWidth = Bounds().Width(); 1164 fSelectMessage = NULL; 1165 fScrollView = NULL; 1166 fTrack = NULL; 1167 } 1168 1169 // _FixupScrollBar 1170 void 1171 BListView::_FixupScrollBar() 1172 { 1173 BScrollBar* vertScroller = ScrollBar(B_VERTICAL); 1174 1175 if (!vertScroller) 1176 return; 1177 1178 BRect bounds = Bounds(); 1179 int32 count = CountItems(); 1180 1181 float itemHeight = 0; 1182 for (int32 i = 0; BListItem* item = ItemAt(i); i++) { 1183 itemHeight += item->Height(); 1184 } 1185 1186 if (bounds.Height() > itemHeight) { 1187 vertScroller->SetRange(0.0, 0.0); 1188 vertScroller->SetValue(0.0); 1189 } else { 1190 // TODO: what about removing items from a scrolled 1191 // list view? Doesn't "value" have to be adjusted? 1192 vertScroller->SetRange(0.0, itemHeight - bounds.Height() - 1.0); 1193 vertScroller->SetProportion(bounds.Height () / itemHeight); 1194 } 1195 1196 if (count != 0) { 1197 vertScroller->SetSteps((float)ceil(FirstItem()->Height()), 1198 bounds.Height()); 1199 } 1200 } 1201 1202 // _InvalidateFrom 1203 void 1204 BListView::_InvalidateFrom(int32 index) 1205 { 1206 // make sure index is behind last valid index 1207 int32 count = CountItems(); 1208 if (index >= count) { 1209 index = count; 1210 } 1211 // take the item before the wanted one, 1212 // because that might already be removed 1213 index--; 1214 BRect dirty = Bounds(); 1215 if (index >= 0) { 1216 dirty.top = ItemFrame(index).bottom + 1; 1217 } 1218 Invalidate(dirty); 1219 } 1220 1221 // _FontChanged 1222 void 1223 BListView::_FontChanged() 1224 { 1225 BFont font; 1226 GetFont(&font); 1227 1228 for (int i = 0; i < CountItems (); i ++) 1229 ItemAt(i)->Update(this, &font); 1230 } 1231 1232 // _Select 1233 bool 1234 BListView::_Select(int32 index, bool extend) 1235 { 1236 if (index < 0 || index >= CountItems()) 1237 return false; 1238 1239 if ((fFirstSelected != -1) && (!extend)) 1240 { 1241 for (int32 item = fFirstSelected; item <= fLastSelected; ++item) 1242 { 1243 if ((ItemAt(item)->IsSelected()) && (item != index)) 1244 { 1245 ItemAt(item)->Deselect(); 1246 InvalidateItem(item); 1247 } 1248 } 1249 1250 fFirstSelected = -1; 1251 } 1252 1253 if (fFirstSelected == -1) 1254 { 1255 fFirstSelected = index; 1256 fLastSelected = index; 1257 } 1258 else if (index < fFirstSelected) 1259 fFirstSelected = index; 1260 else if (index > fLastSelected) 1261 fLastSelected = index; 1262 1263 if (!ItemAt(index)->IsSelected()) 1264 { 1265 ItemAt(index)->Select(); 1266 InvalidateItem(index); 1267 } 1268 1269 return true; 1270 } 1271 1272 // _Select 1273 bool 1274 BListView::_Select(int32 from, int32 to, bool extend) 1275 { 1276 if (to < from) 1277 return false; 1278 1279 if ((fFirstSelected != -1) && (!extend)) 1280 { 1281 for (int32 item = fFirstSelected; item <= fLastSelected; ++item) 1282 { 1283 if ((ItemAt(item)->IsSelected()) && (item < from || item > to)) 1284 { 1285 ItemAt(item)->Deselect(); 1286 InvalidateItem(item); 1287 } 1288 } 1289 1290 fFirstSelected = -1; 1291 } 1292 1293 if (fFirstSelected == -1) 1294 { 1295 fFirstSelected = from; 1296 fLastSelected = to; 1297 } 1298 else if (from < fFirstSelected) 1299 fFirstSelected = from; 1300 else if (to > fLastSelected) 1301 fLastSelected = to; 1302 1303 for (int32 item = from; item <= to; ++item) 1304 { 1305 if (!ItemAt(item)->IsSelected()) 1306 { 1307 ItemAt(item)->Select(); 1308 InvalidateItem(item); 1309 } 1310 } 1311 1312 return true; 1313 } 1314 1315 // _Deselect 1316 bool 1317 BListView::_Deselect(int32 index) 1318 { 1319 if (index < 0 || index >= CountItems()) 1320 return false; 1321 1322 if (!Window()->Lock()) 1323 return false; 1324 1325 BListItem *item = ItemAt(index); 1326 1327 if (item->IsSelected()) 1328 { 1329 BRect frame(ItemFrame(index)); 1330 BRect bounds(Bounds()); 1331 1332 item->Deselect(); 1333 1334 if (fFirstSelected == index && fLastSelected == index) 1335 { 1336 fFirstSelected = -1; 1337 fLastSelected = -1; 1338 } 1339 else 1340 { 1341 if (fFirstSelected == index) 1342 fFirstSelected = _CalcFirstSelected(index); 1343 1344 if (fLastSelected == index) 1345 fLastSelected = _CalcLastSelected(index); 1346 } 1347 1348 if (bounds.Intersects(frame)) 1349 DrawItem(ItemAt(index), frame, true); 1350 } 1351 1352 Window()->Unlock(); 1353 1354 return true; 1355 } 1356 /* 1357 // _Deselect 1358 void 1359 BListView::_Deselect(int32 from, int32 to) 1360 { 1361 if (from < 0 || from >= CountItems() || to < 0 || to >= CountItems()) 1362 return; 1363 1364 bool changed = false; 1365 1366 for (int32 i = from; i <= to; i++) 1367 { 1368 if (_Deselect(i)) 1369 changed = true; 1370 } 1371 1372 if (changed) 1373 { 1374 SelectionChanged(); 1375 InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED); 1376 } 1377 } 1378 */ 1379 // _DeselectAll 1380 bool 1381 BListView::_DeselectAll(int32 except_from, int32 except_to) 1382 { 1383 if (fFirstSelected == -1) 1384 return true; 1385 // TODO... 1386 1387 return true; 1388 } 1389 1390 // _TryInitiateDrag 1391 bool 1392 BListView::_TryInitiateDrag(BPoint where) 1393 { 1394 return false; 1395 } 1396 1397 // _CalcFirstSelected 1398 int32 1399 BListView::_CalcFirstSelected(int32 after) 1400 { 1401 if (after >= CountItems()) 1402 return -1; 1403 1404 for (int32 i = after; i < CountItems(); i++) { 1405 if (ItemAt(i)->IsSelected()) 1406 return i; 1407 } 1408 1409 return -1; 1410 } 1411 //------------------------------------------------------------------------------ 1412 int32 BListView::_CalcLastSelected(int32 before) 1413 { 1414 if (before < 0) 1415 return -1; 1416 1417 for (int32 i = before; i >= 0; i--) 1418 { 1419 if (ItemAt(i)->IsSelected()) 1420 return i; 1421 } 1422 1423 return -1; 1424 } 1425 //------------------------------------------------------------------------------ 1426 void BListView::DrawItem(BListItem *item, BRect itemRect, bool complete) 1427 { 1428 item->DrawItem(this, itemRect, complete); 1429 } 1430 //------------------------------------------------------------------------------ 1431 bool BListView::DoSwapItems(int32 a, int32 b) 1432 { 1433 if (!fList.SwapItems(a, b)) 1434 return false; 1435 1436 Invalidate(ItemFrame(a)); 1437 Invalidate(ItemFrame(b)); 1438 1439 if (fAnchorIndex == a) 1440 fAnchorIndex = b; 1441 else if (fAnchorIndex == b) 1442 fAnchorIndex = a; 1443 1444 RescanSelection(a, b); 1445 1446 return true; 1447 } 1448 //------------------------------------------------------------------------------ 1449 bool BListView::DoMoveItem(int32 from, int32 to) 1450 { 1451 BRect frameFrom = ItemFrame(from); 1452 BRect frameTo = ItemFrame(to); 1453 1454 if (!fList.MoveItem(from, to)) 1455 return false; 1456 1457 RescanSelection(from, to); 1458 1459 BRect frame = frameFrom | frameTo; 1460 1461 if (Bounds().Intersects(frame)) 1462 Invalidate(Bounds() & frame); 1463 1464 return true; 1465 } 1466 //------------------------------------------------------------------------------ 1467 bool BListView::DoReplaceItem(int32 index, BListItem *item) 1468 { 1469 BRect frame = ItemFrame(index); 1470 1471 if (!fList.ReplaceItem(index, item)) 1472 return false; 1473 1474 if (frame != ItemFrame(index)) 1475 _InvalidateFrom(index); 1476 else 1477 Invalidate(frame); 1478 1479 return true; 1480 } 1481 //------------------------------------------------------------------------------ 1482 void BListView::RescanSelection(int32 from, int32 to) 1483 { 1484 if (from > to) 1485 { 1486 int32 tmp = from; 1487 from = to; 1488 to = tmp; 1489 } 1490 1491 if (fAnchorIndex != -1) 1492 { 1493 if (fAnchorIndex == from) 1494 fAnchorIndex = to; 1495 else if (fAnchorIndex == to) 1496 fAnchorIndex = from; 1497 } 1498 1499 /* if (from < fFirstSelected && from < fLastSelected) 1500 return; 1501 1502 if (to > fFirstSelected && to > fLastSelected) 1503 return;*/ 1504 1505 int32 i; 1506 1507 for (i = from; i <= to; i++) 1508 { 1509 if (ItemAt(i)->IsSelected()) 1510 { 1511 fFirstSelected = i; 1512 break; 1513 } 1514 } 1515 1516 for (i = from; i <= to; i++) 1517 if (ItemAt(i)->IsSelected()) 1518 fLastSelected = i; 1519 } 1520 //------------------------------------------------------------------------------ 1521 void BListView::DoMouseUp(BPoint where) 1522 { 1523 } 1524 //------------------------------------------------------------------------------ 1525 void BListView::DoMouseMoved(BPoint where) 1526 { 1527 } 1528 //------------------------------------------------------------------------------ 1529 1530 /* 1531 * $Log $ 1532 * 1533 * $Id $ 1534 * 1535 */ 1536