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 Draw(Bounds()); 1047 } 1048 1049 // MouseUp 1050 void 1051 BListView::MouseUp(BPoint pt) 1052 { 1053 if (fWidth == 0) 1054 return; 1055 1056 DoMouseMoved(pt); 1057 DoMouseUp(pt); 1058 } 1059 1060 // MouseMoved 1061 void 1062 BListView::MouseMoved(BPoint pt, uint32 code, const BMessage *msg) 1063 { 1064 if (fTrack == NULL) 1065 return; 1066 1067 if (_TryInitiateDrag(pt)) 1068 return; 1069 1070 DoMouseMoved(pt); 1071 } 1072 1073 // DetachedFromWindow 1074 void 1075 BListView::DetachedFromWindow() 1076 { 1077 BView::DetachedFromWindow(); 1078 } 1079 1080 // InitiateDrag 1081 bool 1082 BListView::InitiateDrag(BPoint point, int32 index, bool wasSelected) 1083 { 1084 return false; 1085 } 1086 1087 // ResizeToPreferred 1088 void 1089 BListView::ResizeToPreferred() 1090 { 1091 BView::ResizeToPreferred(); 1092 } 1093 1094 // GetPreferredSize 1095 void 1096 BListView::GetPreferredSize(float *width, float *height) 1097 { 1098 BView::GetPreferredSize(width, height); 1099 } 1100 1101 // AllAttached 1102 void 1103 BListView::AllAttached() 1104 { 1105 BView::AllAttached(); 1106 } 1107 1108 // AllDetached 1109 void 1110 BListView::AllDetached() 1111 { 1112 BView::AllDetached(); 1113 } 1114 1115 // DoMiscellaneous 1116 bool 1117 BListView::DoMiscellaneous(MiscCode code, MiscData *data) 1118 { 1119 if (code > B_SWAP_OP) 1120 return false; 1121 1122 switch (code) 1123 { 1124 case B_NO_OP: 1125 { 1126 break; 1127 } 1128 case B_REPLACE_OP: 1129 { 1130 return ReplaceItem(data->replace.index, data->replace.item); 1131 } 1132 case B_MOVE_OP: 1133 { 1134 return MoveItem(data->move.from, data->move.to); 1135 } 1136 case B_SWAP_OP: 1137 { 1138 return SwapItems(data->swap.a, data->swap.b); 1139 } 1140 } 1141 1142 return false; 1143 } 1144 1145 void BListView::_ReservedListView2() {} 1146 void BListView::_ReservedListView3() {} 1147 void BListView::_ReservedListView4() {} 1148 1149 BListView &BListView::operator=(const BListView &) 1150 { 1151 return *this; 1152 } 1153 1154 // _InitObject 1155 void 1156 BListView::_InitObject(list_view_type type) 1157 { 1158 fListType = type; 1159 fFirstSelected = -1; 1160 fLastSelected = -1; 1161 fAnchorIndex = -1; 1162 fWidth = Bounds().Width(); 1163 fSelectMessage = NULL; 1164 fScrollView = NULL; 1165 fTrack = NULL; 1166 } 1167 1168 // _FixupScrollBar 1169 void 1170 BListView::_FixupScrollBar() 1171 { 1172 BScrollBar* vertScroller = ScrollBar(B_VERTICAL); 1173 1174 if (!vertScroller) 1175 return; 1176 1177 BRect bounds = Bounds(); 1178 int32 count = CountItems(); 1179 1180 float itemHeight = 0; 1181 for (int32 i = 0; BListItem* item = ItemAt(i); i++) { 1182 itemHeight += item->Height(); 1183 } 1184 1185 if (bounds.Height() > itemHeight) { 1186 vertScroller->SetRange(0.0, 0.0); 1187 vertScroller->SetValue(0.0); 1188 } else { 1189 // TODO: what about removing items from a scrolled 1190 // list view? Doesn't "value" have to be adjusted? 1191 vertScroller->SetRange(0.0, itemHeight - bounds.Height() - 1.0); 1192 vertScroller->SetProportion(bounds.Height () / itemHeight); 1193 } 1194 1195 if (count != 0) { 1196 vertScroller->SetSteps((float)ceil(FirstItem()->Height()), 1197 bounds.Height()); 1198 } 1199 } 1200 1201 // _InvalidateFrom 1202 void 1203 BListView::_InvalidateFrom(int32 index) 1204 { 1205 // make sure index is behind last valid index 1206 int32 count = CountItems(); 1207 if (index >= count) { 1208 index = count; 1209 } 1210 // take the item before the wanted one, 1211 // because that might already be removed 1212 index--; 1213 BRect dirty = Bounds(); 1214 if (index >= 0) { 1215 dirty.top = ItemFrame(index).bottom + 1; 1216 } 1217 Invalidate(dirty); 1218 } 1219 1220 // _FontChanged 1221 void 1222 BListView::_FontChanged() 1223 { 1224 BFont font; 1225 GetFont(&font); 1226 1227 for (int i = 0; i < CountItems (); i ++) 1228 ItemAt(i)->Update(this, &font); 1229 } 1230 1231 // _Select 1232 bool 1233 BListView::_Select(int32 index, bool extend) 1234 { 1235 if (index < 0 || index >= CountItems()) 1236 return false; 1237 1238 if ((fFirstSelected != -1) && (!extend)) 1239 { 1240 for (int32 item = fFirstSelected; item <= fLastSelected; ++item) 1241 { 1242 if ((ItemAt(item)->IsSelected()) && (item != index)) 1243 { 1244 ItemAt(item)->Deselect(); 1245 InvalidateItem(item); 1246 } 1247 } 1248 1249 fFirstSelected = -1; 1250 } 1251 1252 if (fFirstSelected == -1) 1253 { 1254 fFirstSelected = index; 1255 fLastSelected = index; 1256 } 1257 else if (index < fFirstSelected) 1258 fFirstSelected = index; 1259 else if (index > fLastSelected) 1260 fLastSelected = index; 1261 1262 if (!ItemAt(index)->IsSelected()) 1263 { 1264 ItemAt(index)->Select(); 1265 InvalidateItem(index); 1266 } 1267 1268 return true; 1269 } 1270 1271 // _Select 1272 bool 1273 BListView::_Select(int32 from, int32 to, bool extend) 1274 { 1275 if (to < from) 1276 return false; 1277 1278 if ((fFirstSelected != -1) && (!extend)) 1279 { 1280 for (int32 item = fFirstSelected; item <= fLastSelected; ++item) 1281 { 1282 if ((ItemAt(item)->IsSelected()) && (item < from || item > to)) 1283 { 1284 ItemAt(item)->Deselect(); 1285 InvalidateItem(item); 1286 } 1287 } 1288 1289 fFirstSelected = -1; 1290 } 1291 1292 if (fFirstSelected == -1) 1293 { 1294 fFirstSelected = from; 1295 fLastSelected = to; 1296 } 1297 else if (from < fFirstSelected) 1298 fFirstSelected = from; 1299 else if (to > fLastSelected) 1300 fLastSelected = to; 1301 1302 for (int32 item = from; item <= to; ++item) 1303 { 1304 if (!ItemAt(item)->IsSelected()) 1305 { 1306 ItemAt(item)->Select(); 1307 InvalidateItem(item); 1308 } 1309 } 1310 1311 return true; 1312 } 1313 1314 // _Deselect 1315 bool 1316 BListView::_Deselect(int32 index) 1317 { 1318 if (index < 0 || index >= CountItems()) 1319 return false; 1320 1321 if (!Window()->Lock()) 1322 return false; 1323 1324 BListItem *item = ItemAt(index); 1325 1326 if (item->IsSelected()) 1327 { 1328 BRect frame(ItemFrame(index)); 1329 BRect bounds(Bounds()); 1330 1331 item->Deselect(); 1332 1333 if (fFirstSelected == index && fLastSelected == index) 1334 { 1335 fFirstSelected = -1; 1336 fLastSelected = -1; 1337 } 1338 else 1339 { 1340 if (fFirstSelected == index) 1341 fFirstSelected = _CalcFirstSelected(index); 1342 1343 if (fLastSelected == index) 1344 fLastSelected = _CalcLastSelected(index); 1345 } 1346 1347 if (bounds.Intersects(frame)) 1348 DrawItem(ItemAt(index), frame, true); 1349 } 1350 1351 Window()->Unlock(); 1352 1353 return true; 1354 } 1355 /* 1356 // _Deselect 1357 void 1358 BListView::_Deselect(int32 from, int32 to) 1359 { 1360 if (from < 0 || from >= CountItems() || to < 0 || to >= CountItems()) 1361 return; 1362 1363 bool changed = false; 1364 1365 for (int32 i = from; i <= to; i++) 1366 { 1367 if (_Deselect(i)) 1368 changed = true; 1369 } 1370 1371 if (changed) 1372 { 1373 SelectionChanged(); 1374 InvokeNotify(fSelectMessage, B_CONTROL_MODIFIED); 1375 } 1376 } 1377 */ 1378 // _DeselectAll 1379 bool 1380 BListView::_DeselectAll(int32 except_from, int32 except_to) 1381 { 1382 if (fFirstSelected == -1) 1383 return true; 1384 // TODO... 1385 1386 return true; 1387 } 1388 1389 // _TryInitiateDrag 1390 bool 1391 BListView::_TryInitiateDrag(BPoint where) 1392 { 1393 return false; 1394 } 1395 1396 // _CalcFirstSelected 1397 int32 1398 BListView::_CalcFirstSelected(int32 after) 1399 { 1400 if (after >= CountItems()) 1401 return -1; 1402 1403 for (int32 i = after; i < CountItems(); i++) { 1404 if (ItemAt(i)->IsSelected()) 1405 return i; 1406 } 1407 1408 return -1; 1409 } 1410 //------------------------------------------------------------------------------ 1411 int32 BListView::_CalcLastSelected(int32 before) 1412 { 1413 if (before < 0) 1414 return -1; 1415 1416 for (int32 i = before; i >= 0; i--) 1417 { 1418 if (ItemAt(i)->IsSelected()) 1419 return i; 1420 } 1421 1422 return -1; 1423 } 1424 //------------------------------------------------------------------------------ 1425 void BListView::DrawItem(BListItem *item, BRect itemRect, bool complete) 1426 { 1427 item->DrawItem(this, itemRect, complete); 1428 } 1429 //------------------------------------------------------------------------------ 1430 bool BListView::DoSwapItems(int32 a, int32 b) 1431 { 1432 if (!fList.SwapItems(a, b)) 1433 return false; 1434 1435 Invalidate(ItemFrame(a)); 1436 Invalidate(ItemFrame(b)); 1437 1438 if (fAnchorIndex == a) 1439 fAnchorIndex = b; 1440 else if (fAnchorIndex == b) 1441 fAnchorIndex = a; 1442 1443 RescanSelection(a, b); 1444 1445 return true; 1446 } 1447 //------------------------------------------------------------------------------ 1448 bool BListView::DoMoveItem(int32 from, int32 to) 1449 { 1450 BRect frameFrom = ItemFrame(from); 1451 BRect frameTo = ItemFrame(to); 1452 1453 if (!fList.MoveItem(from, to)) 1454 return false; 1455 1456 RescanSelection(from, to); 1457 1458 BRect frame = frameFrom | frameTo; 1459 1460 if (Bounds().Intersects(frame)) 1461 Invalidate(Bounds() & frame); 1462 1463 return true; 1464 } 1465 //------------------------------------------------------------------------------ 1466 bool BListView::DoReplaceItem(int32 index, BListItem *item) 1467 { 1468 BRect frame = ItemFrame(index); 1469 1470 if (!fList.ReplaceItem(index, item)) 1471 return false; 1472 1473 if (frame != ItemFrame(index)) 1474 _InvalidateFrom(index); 1475 else 1476 Invalidate(frame); 1477 1478 return true; 1479 } 1480 //------------------------------------------------------------------------------ 1481 void BListView::RescanSelection(int32 from, int32 to) 1482 { 1483 if (from > to) 1484 { 1485 int32 tmp = from; 1486 from = to; 1487 to = tmp; 1488 } 1489 1490 if (fAnchorIndex != -1) 1491 { 1492 if (fAnchorIndex == from) 1493 fAnchorIndex = to; 1494 else if (fAnchorIndex == to) 1495 fAnchorIndex = from; 1496 } 1497 1498 /* if (from < fFirstSelected && from < fLastSelected) 1499 return; 1500 1501 if (to > fFirstSelected && to > fLastSelected) 1502 return;*/ 1503 1504 int32 i; 1505 1506 for (i = from; i <= to; i++) 1507 { 1508 if (ItemAt(i)->IsSelected()) 1509 { 1510 fFirstSelected = i; 1511 break; 1512 } 1513 } 1514 1515 for (i = from; i <= to; i++) 1516 if (ItemAt(i)->IsSelected()) 1517 fLastSelected = i; 1518 } 1519 //------------------------------------------------------------------------------ 1520 void BListView::DoMouseUp(BPoint where) 1521 { 1522 } 1523 //------------------------------------------------------------------------------ 1524 void BListView::DoMouseMoved(BPoint where) 1525 { 1526 } 1527 //------------------------------------------------------------------------------ 1528 1529 /* 1530 * $Log $ 1531 * 1532 * $Id $ 1533 * 1534 */ 1535