1 //////////////////////////////////////////////////////////////////////////////// 2 // 3 // File: ListView.cpp 4 // 5 // Description: BListView represents a one-dimensional list view. 6 // 7 // Copyright 2001, Ulrich Wimboeck 8 // 9 //////////////////////////////////////////////////////////////////////////////// 10 11 #include "ListView.h" 12 #include <Region.h> 13 #include <Window.h> 14 #include <ClassInfo.h> 15 #include <iostream> 16 #include <ScrollView.h> 17 18 19 20 struct track_data 21 { 22 } ; 23 24 /** 25 * Initializes the new BListView. The frame, name, resizingMode, and flags 26 * arguments are identical to those declared for the BView class and are 27 * passed unchanged to the BView constructor. 28 * 29 * The list type can be either: 30 * B_SINGLE_SELECTION_LIST 31 * The user can select only one item in the list at a time. This is the 32 * default setting. 33 * B_MULTIPLE_SELECTION_LIST 34 * The user can select any number of items by holding down an Option 35 * key (for discontinuous selections) or a Shift key (for contiguous 36 * selections). 37 */ 38 BListView::BListView(BRect frame, const char *name, 39 list_view_type type /* = B_SINGLE_SELECTION_LIST */, 40 uint32 resizeMask /* = B_FOLLOW_LEFT | B_FOLLOW_TOP */, 41 uint32 flags /* = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE */) 42 : BView(frame, name, resizeMask, flags) 43 { 44 InitObject(type) ; 45 } 46 47 48 BListView::BListView(BMessage* data) 49 : BView(data) 50 { 51 // if the pointer is null the object should be initialized anyway 52 if (data == NULL) { 53 InitObject(B_SINGLE_SELECTION_LIST) ; 54 return ; 55 } 56 57 { 58 if (data->FindInt32("_lv_type", fListType) != B_OK) 59 fList = B_SINGLE_SELECTION_LIST ; 60 61 // first check if a selection message is included in the messgage 62 // if there is one allocate memory for it and get it. 63 type_code code ; 64 65 if (data->GetInfo("_2nd_msg", &code) == B_OK ) 66 { 67 if (code == B_MESSAGE_TYPE) 68 { 69 fSelectMessage = new BMessage() ; 70 data->FindMessage("_msg", fSelectMessage) ; 71 BInvoker::SetMessage(fSelectMessage) ; 72 73 // set the fSelectMessage back to NULL 74 fSelectMessage = NULL ; 75 } 76 } 77 78 // check if there is an invokation message stored 79 // in the archiv and set it 80 if (data->GetInfo("_msg", &code) == B_OK ) 81 { 82 if (code == B_MESSAGE_TYPE) 83 { 84 fSelectMessage = new BMessage() ; 85 data->FindMessage("_msg", fSelectMessage) ; 86 } 87 else { 88 fSelectMessage = NULL ; 89 } 90 } 91 92 int32 count ; 93 94 // add the item archivied in the message if it was a deep copy 95 if (data->GetInfo("_l_items", &code, &count) == B_OK) 96 { 97 if (code == B_MESSAGE_TYPE) 98 { 99 BMessage msg ; 100 BArchivable* unarchived ; 101 BListItem* item ; 102 103 for (int32 i = 0 ; i < count ; ++i) 104 { 105 data->FindMessage("_l_items", i, &msg) ; 106 unarchived = instantiate_object(data) ; 107 item = dynamic_cast<BListItem*>(unarchived) ; 108 109 if (item != NULL) { 110 AddItem(item) ; 111 } 112 } 113 } 114 } 115 } 116 } 117 118 /** 119 * Frees the selection and invocation messages, if any, and any memory 120 * allocated to hold the list of items, but not the items themselves. 121 */ 122 BListView::~BListView() 123 { 124 // Remove all pointers from the list 125 fList.MakeEmpty() ; 126 127 // delete the select message if there is one 128 // the invocation message is deleted by the destructor of BInvoker 129 if (fSelectMessage != NULL) 130 { 131 delete fSelectMessage ; 132 fSelectMessage = NULL ; 133 } 134 135 if (fTrack != NULL) 136 { 137 delete fTrack ; 138 fTrack = NULL ; 139 } 140 } 141 142 /** 143 * Returns a new BListView object, allocated by new and created 144 * with the version of the constructor that takes a BMessage 145 * archive. However, if the archive message doesn't contain 146 * data for a BListView object, this function returns NULL. 147 * 148 * @param data Pointer to a BMessge object containing the data 149 * of an archieved BListView object. 150 */ 151 BArchivable* 152 BListView::Instantiate(BMessage* data) 153 { 154 if (validate_instantiation(data, "BListView")) 155 return new BListView(data) ; 156 157 return NULL ; 158 } 159 160 status_t 161 BListView::Archive(BMessage* data, bool deep /* = true */) const 162 { 163 if (!data) 164 return B_ERROR ; 165 166 BView::Archive(data, deep) ; 167 168 data->AddString("class", "BListView") ; 169 data->AddInt32("_lv_type", fListType) ; 170 171 if (fSelectMessage) { 172 data->AddMessage("_msg", fSelectMessage) ; 173 } 174 175 if (BInvoker::Message()) { 176 data->AddMessage("_2nd_msg", BInvoker::Message()) ; 177 } 178 179 if (deep) 180 { 181 BMessage* msg ; 182 183 for (int i = 0 ; i < fList.CountItems() ; ++i) 184 { 185 msg = new BMessage() ; 186 ItemAt(i)->Archive(msg, deep) ; 187 data->AddMessage("_l_items", msg) ; 188 } 189 } 190 191 return B_OK ; 192 } 193 194 /** 195 * Calls upon every item in the updateRect area of the view to draw itself. 196 * Draw() is called for you whenever the list view is to be updated or 197 * redisplayed; you don't need to call it yourself. You also don't need to 198 * reimplement it; to change the way items are drawn, define a new version 199 * of DrawItem() in a class derived from BListItem. 200 */ 201 void 202 BListView::Draw(BRect updateRect) 203 { 204 SetHighColor(0, 0 ,0) ; 205 206 if (!fList.IsEmpty()) 207 { 208 // Find the first and the last item which has to be updated 209 float tmp = 0 ; 210 int32 firstIndex = 0 ; 211 212 for ( ; firstIndex < fList.CountItems() ; ++firstIndex) 213 { 214 float help = (ItemAt(firstIndex))->Height() ; 215 tmp += help ; 216 if (tmp >= updateRect.top) 217 break ; 218 } 219 220 int32 lastIndex = firstIndex ; 221 222 for (; lastIndex < fList.CountItems() ; ++lastIndex) 223 { 224 //Draw the item 225 BRect rect(Bounds()); 226 rect.top = tmp - ItemAt(lastIndex)->Height() ; 227 rect.bottom = tmp ; 228 rect.right = 2000 ; 229 230 // BRegion region ; 231 // region.Include(rect) ; 232 233 // ConstrainClippingRegion(®ion) ; 234 ItemAt(lastIndex)->DrawItem(this, rect);//, true) ; 235 236 if (tmp >= updateRect.bottom) 237 break ; 238 239 tmp += ItemAt(lastIndex)->Height() ; 240 } 241 } 242 } 243 244 void 245 BListView::MessageReceived(BMessage *msg) 246 { 247 switch (msg->what) 248 { 249 // scripting stuff 250 case B_GET_SUPPORTED_SUITES: 251 { 252 BMessage reply(B_REPLY) ; 253 reply.AddString("suites", "suite/vnd.Be-list-view") ; 254 msg->SendReply(&reply) ; 255 break ; 256 } 257 case B_COUNT_PROPERTIES: 258 { 259 BMessage reply(B_REPLY) ; 260 reply.AddInt32("result", fList.CountItems()) ; 261 msg->SendReply(&reply) ; 262 break ; 263 } 264 case B_EXECUTE_PROPERTY: 265 break ; 266 case B_GET_PROPERTY: 267 break ; 268 case B_SET_PROPERTY: 269 break ; 270 case B_MOUSE_WHEEL_CHANGED: 271 break ; 272 } 273 274 BView::MessageReceived(msg) ; 275 } 276 277 /** 278 * Responds to B_MOUSE_DOWN messages by selecting items, invoking them 279 * (if the mouse-down event is the second of a double-click), and 280 * autoscrolling the list (when the user drags with a mouse button down). 281 * This function also calls InitiateDrag() to give derived classes the 282 * opportunity to drag items. You can implement that function; you shouldn't 283 * override (or call) this one. 284 */ 285 // The docu says shift key and option key like Windows but it does not. 286 void 287 BListView::MouseDown(BPoint where) 288 { 289 int32 index = IndexOf(where) ; 290 291 if (index >= 0) 292 { 293 bool extend = false ; 294 295 if ((fListType == B_MULTIPLE_SELECTION_LIST) && (modifiers() & B_SHIFT_KEY)) 296 extend = true ; 297 298 if (!ItemAt(index)->IsSelected()) 299 Select(index, extend) ; 300 else { 301 ItemAt(index)->Deselect() ; 302 InvalidateItem(index) ; 303 } 304 } 305 306 MakeFocus() ; 307 } 308 309 /** 310 * Permits the user to operate the list using the following keys: 311 * 312 * Up Arrow and Down Arrow 313 * Select the items that are immediately before and immediately after 314 * the currently selected item. 315 * 316 * Page Up and Page Down 317 * Select the items that are one viewful above and below the currently 318 * selected item—or the first and last items if there's no item a viewful away. 319 * 320 * Home and End 321 * Select the first and last items in the list. 322 * 323 * Enter and the space bar 324 * Invoke the current selection. 325 * 326 * This function also incorporates the inherited BView version so that the 327 * Tab key can navigate to another view. KeyDown() is called to report 328 * B_KEY_DOWN messages when the BListView is the focus view of the active 329 * window; you shouldn't call it yourself. 330 */ 331 void 332 BListView::KeyDown(const char *bytes, int32 numBytes) 333 { 334 switch (*bytes) 335 { 336 // pressing the home key the first item in the list is selected 337 // and made visible 338 case B_HOME: 339 { 340 if (CountItems() > 0) 341 { 342 Select(0) ; 343 ScrollToSelection() ; 344 } 345 break; 346 } 347 // if the end key is pressed the last entry is selected and 348 // made visible 349 case B_END: 350 { 351 if (CountItems() > 0) 352 { 353 Select(CountItems() - 1) ; 354 ScrollToSelection() ; 355 } 356 break ; 357 } 358 // select the item above the first selected one 359 case B_UP_ARROW: 360 if (fFirstSelected > 0) 361 { 362 Select(fFirstSelected - 1) ; 363 ScrollToSelection() ; 364 } 365 break ; 366 // select the item below the last one 367 case B_DOWN_ARROW: 368 if ((fFirstSelected >= 0) && (fLastSelected < (fList.CountItems() - 1))) 369 { 370 Select(fLastSelected + 1) ; 371 ScrollToSelection() ; 372 } 373 break ; 374 case B_PAGE_UP: 375 { 376 BRect bounds(Bounds()) ; 377 BPoint top(bounds.left, bounds.top) ; 378 int32 index = IndexOf(top) ; 379 380 if (index >= 0) 381 { 382 BRect frame(ItemFrame(index)) ; 383 384 if ((frame.bottom - (bounds.bottom - bounds.top)) >= 0) 385 ScrollTo(bounds.left, frame.bottom - (bounds.bottom - bounds.top)) ; 386 else 387 ScrollTo(bounds.left, 0) ; 388 } 389 390 break ; 391 } 392 case B_PAGE_DOWN: 393 { 394 BRect bounds(Bounds()) ; 395 BPoint bottom(bounds.left, bounds.bottom) ; 396 int32 index = IndexOf(bottom) ; 397 398 if (index >= 0) 399 { 400 BRect frame(ItemFrame(index)) ; 401 float height ; 402 GetPreferredSize(NULL, &height) ; 403 404 if ((bounds.bottom - bounds.top + frame.top) <= height) 405 { 406 ScrollTo(bounds.left, frame.top) ; 407 } 408 else { 409 ScrollTo(bounds.left, height - (bounds.bottom - bounds.top)) ; 410 } 411 } 412 413 break; 414 } 415 // both, the space bar and the return key invoke the current selection 416 case B_SPACE: 417 case B_RETURN: 418 { 419 if (fFirstSelected >= 0) 420 { 421 Invoke() ; 422 } 423 break ; 424 } 425 } 426 BView::KeyDown(bytes, numBytes) ; 427 } 428 429 /** 430 * Overrides the BView version of MakeFocus() to draw an indication that 431 * the BListView has become the focus for keyboard events when the 432 * focused flag is true, and to remove that indication when the flag is false. 433 */ 434 void 435 BListView::MakeFocus(bool state /* = true */) 436 { 437 if (state != IsFocus()) 438 { 439 BView::MakeFocus(state) ; 440 } 441 442 // if the list view is target of a scroll view 443 // the border of the scroll view needs to be set to the 444 // correct state 445 if (fScrollView != NULL) 446 { 447 fScrollView->SetBorderHighlighted(state) ; 448 } 449 } 450 451 /** 452 * Updates the on-screen display in response to a notification that 453 * the BListView's frame rectangle has been resized. In particular, 454 * this function looks for a vertical scroll bar that's a sibling of 455 * the BListView. It adjusts this scroll bar to reflect the way the 456 * list view was resized, under the assumption that it must have the 457 * BListView as its target. 458 */ 459 void 460 BListView::FrameResized(float newWidth, float newHeight) 461 { 462 // set the range of the scroll bars of the scroll view 463 FixupScrollBar() ; 464 } 465 466 void 467 BListView::TargetedByScrollView(BScrollView *scroller) 468 { 469 // if (fScrollView != NULL) 470 { 471 // delete fScrollView ; 472 } 473 474 fScrollView = scroller ; 475 } 476 477 void 478 BListView::ScrollTo(BPoint where) 479 { 480 BView::ScrollTo(where) ; 481 } 482 483 /** 484 * Adds an item to the BListView at the end of the list. If necessary, 485 * additional memory is allocated to accommodate the new item. 486 * Adding an item never removes an item already in the list. 487 * If the supplied pointer is NULL the function returns false. Otherwise, 488 * it returns true. 489 */ 490 bool 491 BListView::AddItem(BListItem *item) 492 { 493 // check if the supplied pointer is NULL 494 bool bReturn = (item != NULL) ; 495 496 if (bReturn) 497 { 498 if (bReturn = fList.AddItem(static_cast<void*>(item))) 499 { 500 // If the view is already attached to a view the item needs to be updated 501 if (Parent() != NULL) 502 { 503 BFont font ; 504 GetFont(&font) ; 505 item->Update(this, &font) ; 506 } 507 } 508 509 // The new item needs to be drawn if visible 510 InvalidateItem(fList.CountItems() - 1) ; 511 } 512 513 return bReturn ; 514 } 515 516 /** 517 * Adds an item to the BListView at index. If necessary, additional memory is 518 * allocated to accommodate the new item. 519 * Adding an item never removes an item already in the list. If the item is 520 * added at an index that's already occupied, items currently in the list are 521 * bumped down one slot to make room. 522 * If index is out of range (greater than the current item count, or less than zero), this function fails and returns false. Otherwise, it returns 523 * true 524 */ 525 bool 526 BListView::AddItem(BListItem *item, int32 atIndex) 527 { 528 bool bReturn(item != NULL) ; 529 530 // If successful invalidate all items starting at atIndex 531 if (bReturn) 532 { 533 if (bReturn = fList.AddItem(static_cast<void*>(item), atIndex)) 534 { 535 if (Parent() != NULL) 536 { 537 BFont font ; 538 GetFont(&font) ; 539 item->Update(this, &font) ; 540 } 541 } 542 543 // The new item needs to drawn if visible 544 InvalidateFrom(atIndex) ; 545 } 546 547 return bReturn ; 548 } 549 550 /** 551 * Adds the contents of another list to this BListView. The items from 552 * the BList are appended to the end of the list. 553 * If the supplied pointer or one of the pointers within the list is NULL, 554 * the function fails and returns false. If successful, it returns true. 555 * The BListView doesn't check to be sure that all the items it adds from the 556 * list are pointers to BListItem objects. It assumes that they are; if the 557 * assumption is false, the program will crash. 558 */ 559 bool 560 BListView::AddList(BList* newItems) 561 { 562 bool bReturn(newItems != NULL) ; 563 564 if (bReturn) 565 { 566 for (int32 index = 0 ; index < newItems->CountItems() ; ++index) 567 { 568 if (newItems->ItemAt(index) == NULL) 569 { 570 bReturn = false ; 571 break ; 572 } 573 } 574 575 if (bReturn) 576 { 577 int32 index = CountItems() ; 578 fList.AddList(newItems) ; 579 InvalidateFrom(index) ; 580 581 if (Parent() != NULL) 582 { 583 BFont font ; 584 GetFont(&font) ; 585 586 for (int32 index = CountItems() - fList.CountItems() ; 587 index < CountItems() ; ++index) 588 { 589 ItemAt(index)->Update(this, &font) ; 590 } 591 } 592 } 593 } 594 595 return bReturn ; 596 } 597 598 /** 599 * Adds the contents of another list to this BListView. The items from the 600 * BList are inserted at index. 601 * If the supplied pointer or one of the pointers within the list is NULL or 602 * the index is out of range, the function, fails and returns false. 603 * If successful, it returns true. 604 * The BListView doesn't check to be sure that all the items it adds from 605 * the list are pointers to BListItem objects. It assumes that they are; if the 606 * assumption is false, the program will crash. 607 */ 608 bool 609 BListView::AddList(BList *newItems, int32 atIndex) 610 { 611 bool bReturn(newItems != NULL) ; 612 613 if (bReturn) 614 { 615 for (int32 index = 0 ; index < newItems->CountItems() ; ++index) 616 { 617 if (newItems->ItemAt(index) == NULL) 618 { 619 bReturn = false ; 620 break ; 621 } 622 } 623 624 if (bReturn) 625 { 626 fList.AddList(newItems, atIndex) ; 627 InvalidateFrom(atIndex) ; 628 629 if (Parent() != NULL) 630 { 631 BFont font ; 632 GetFont(&font) ; 633 634 for (int32 index = CountItems() - fList.CountItems() ; 635 index < CountItems() ; ++index) 636 { 637 ItemAt(index)->Update(this, &font) ; 638 } 639 } 640 } 641 } 642 643 return bReturn ; 644 } 645 646 /** 647 * Removes a single item from the BListView. If passed an index, 648 * it removes the item at that index and returns it. If there's no 649 * item at the index, it returns NULL. If passed an item, this 650 * function looks for that particular item in the list, removes it, 651 * and returns true. If it can't find the item, it returns false. 652 * If the item is in the list more than once, this function removes 653 * only its first occurrence. 654 */ 655 bool 656 BListView::RemoveItem(BListItem* item) 657 { 658 bool bReturn = false ; 659 // Remember the position of the item 660 int32 index = IndexOf(item) ; 661 bReturn = fList.RemoveItem(static_cast<void*>(item)) ; 662 663 // update all items with a higher index than the removed item 664 if (bReturn) 665 { 666 for ( ; index < fList.CountItems() ; ++index) 667 { 668 InvalidateItem(index) ; 669 } 670 } 671 672 return bReturn ; 673 } 674 675 BListItem* 676 BListView::RemoveItem(int32 index) 677 { 678 BListItem* item = static_cast<BListItem*>(fList.RemoveItem(index)) ; 679 680 // update all items with a higher index than the removed item 681 if (item != NULL) 682 { 683 for ( ; index < fList.CountItems() ; ++index) 684 { 685 InvalidateItem(index) ; 686 } 687 } 688 689 return item ; 690 } 691 692 bool 693 BListView::RemoveItems(int32 index, int32 count) 694 { 695 bool bReturn = fList.RemoveItems(index, count) ; 696 697 // update all items with a higher index than the last removed item 698 if (bReturn) 699 { 700 for ( ; index < fList.CountItems() ; ++index) 701 { 702 InvalidateItem(index) ; 703 } 704 } 705 706 return bReturn ; 707 } 708 709 710 /** 711 * These functions set, and return information about, the message that 712 * a BListView sends whenever a new item is selected. They're exact 713 * counterparts to the functions described above under SetInvocationMessage(), 714 * except that the selection message is sent whenever an item in the list 715 * is selected, rather than when invoked. It's more common to take action 716 * (to initiate a message) when invoking an item than when selecting one. 717 */ 718 void 719 BListView::SetSelectionMessage(BMessage *message) 720 { 721 if (fSelectMessage != NULL) 722 { 723 delete fSelectMessage ; 724 } 725 726 fSelectMessage = message ; 727 } 728 729 BMessage* 730 BListView::SelectionMessage() const 731 { 732 return fSelectMessage ; 733 } 734 735 uint32 736 BListView::SelectionCommand() const 737 { 738 if (fSelectMessage) 739 return fSelectMessage->what ; 740 else 741 return 0 ; 742 } 743 744 void 745 BListView::SetInvocationMessage(BMessage *message) 746 { 747 BInvoker::SetMessage(message) ; 748 } 749 750 /** 751 * These functions set and return information about the BMessage 752 * that the BListView sends when currently selected items are invoked. 753 * SetInvocationMessage() assigns message to the BListView, freeing 754 * any message previously assigned. The message becomes the responsibility 755 * of the BListView object and will be freed only when it's replaced by 756 * another message or the BListView is freed; you shouldn't free it 757 * yourself. 758 * Passing a NULL pointer to this function deletes the current message 759 * without replacing it. 760 * When sending the message, the Invoke() function makes a copy of it 761 * and adds two pieces of relevant information—"when" the message is 762 * sent and the "source" BListView. These names should not be used for 763 * any data that you add to the invocation message. 764 * InvocationMessage() returns a pointer to the BMessage and 765 * InvocationCommand() returns its what data member. The message 766 * belongs to the BListView; it can be altered by adding or removing 767 * data, but it shouldn't be deleted. To get rid of the current message, 768 * pass a NULL pointer to SetInvocationMessage(). 769 */ 770 BMessage* 771 BListView::InvocationMessage() const 772 { 773 return BInvoker::Message() ; 774 } 775 776 777 uint32 778 BListView::InvocationCommand() const 779 { 780 return BInvoker::Command() ; 781 } 782 783 /** 784 * These functions set and return the list type—whether or not 785 * it permits multiple selections. The list_view_type must be either 786 * B_SINGLE_SELECTION_LIST or B_MULTIPLE_SELECTION_LIST. The type 787 * is first set when the BListView is constructed. 788 */ 789 void 790 BListView::SetListType(list_view_type type) 791 { 792 if (type != fListType) 793 { 794 // When the type is changed from B_MULTIPLE_SELECTION_LIST to 795 // B_SINGLE_SELECTION_LIST all selected items will be deselcted. 796 if (fListType == B_MULTIPLE_SELECTION_LIST) 797 { 798 for (int32 item = fFirstSelected ; item <= fLastSelected ; ++item) 799 { 800 if (ItemAt(item)->IsSelected()) 801 { 802 ItemAt(item)->Deselect() ; 803 InvalidateItem(item) ; 804 } 805 } 806 } 807 808 fListType = type ; 809 } 810 } 811 812 list_view_type 813 BListView::ListType() const 814 { 815 return fListType ; 816 } 817 818 /** 819 * This function returns the BListItem at index in the list, or NULL if the 820 * list is empty. 821 * The function does not alter the contents of the list—they don't remove the returned 822 * item. 823 */ 824 BListItem* 825 BListView::ItemAt(int32 index) const 826 { 827 return static_cast<BListItem*>(fList.ItemAt(index)) ; 828 } 829 830 /** 831 * Returns the index where a particular item whose display rectangle 832 * includes a particular point—is located in the list. 833 * To determine whether an item lies at the specified point, only 834 * the y-coordinate value of the point is considered. 835 * If the item isn't in the list or the y-coordinate of the point 836 * doesn't intersect with the data rectangle of the BListView, the return 837 * value will be a negative number. 838 */ 839 int32 840 BListView::IndexOf(BPoint point) const 841 { 842 float tmp = 0 ; 843 844 for (int32 index = 0 ; index < fList.CountItems() ; ++index) 845 { 846 tmp += ItemAt(index)->Height() ; 847 848 if (point.y < tmp) 849 { 850 return index ; 851 } 852 } 853 854 return -1 ; 855 } 856 857 /** 858 * Returns the index where a particular item is located in the list. 859 * If the item is in the list more than once, the index returned will 860 * be the position of its first occurrence. 861 * If the item isn't in the list of the BListView, the return value 862 * will be a negative number. 863 */ 864 int32 865 BListView::IndexOf(BListItem *item) const 866 { 867 return fList.IndexOf(static_cast<void*>(item)) ; 868 } 869 870 871 /** 872 * This function returns the very first in the list, or NULL if the list is empty. 873 * The function does not alter the contents of the list—they don't remove the returned 874 * item. 875 */ 876 BListItem* 877 BListView::FirstItem() const 878 { 879 return static_cast<BListItem*>(fList.FirstItem()) ; 880 } 881 882 /** 883 * This function returns the very last in the list, or NULL if the list is empty. 884 * The function does not alter the contents of the list—they don't remove the returned 885 * item. 886 */ 887 888 BListItem* 889 BListView::LastItem() const 890 { 891 return static_cast<BListItem*>(fList.FirstItem()) ; 892 } 893 894 /** 895 * Returns true if item is in the list, and false if not. 896 */ 897 bool 898 BListView::HasItem(BListItem *item) const 899 { 900 return fList.HasItem(static_cast<void*>(item)) ; 901 } 902 903 /** 904 * Returns the number of BListItems currently in the list. 905 */ 906 int32 907 BListView::CountItems() const 908 { 909 return fList.CountItems() ; 910 } 911 912 /** 913 * MakeEmpty() empties the BListView of all its items, without freeing 914 * the BListItem objects. 915 */ 916 void 917 BListView::MakeEmpty() 918 { 919 // Remove all items from the list 920 fList.MakeEmpty() ; 921 fFirstSelected = -1 ; 922 923 // update the list view 924 Invalidate() ; 925 } 926 927 /** 928 * IsEmpty() returns true if the list is empty (if it contains no items), 929 * and false otherwise. 930 */ 931 bool 932 BListView::IsEmpty() const 933 { 934 return fList.IsEmpty() ; 935 } 936 937 /** 938 * Calls the func function once for each item in the BListView. 939 * BListItems are visited in order, beginning with the first one in 940 * the list (index 0) and ending with the last. 941 * If a call to func returns true, the iteration is stopped, even 942 * if some items have not yet been visited. 943 */ 944 void 945 BListView::DoForEach(bool (*func)(BListItem *)) 946 { 947 fList.DoForEach(reinterpret_cast<bool (*)(void*)>(func)) ; 948 } 949 950 /** 951 * Calls the func function once for each item in the BListView. 952 * BListItems are visited in order, beginning with the first one in 953 * the list (index 0) and ending with the last. 954 * If a call to func returns true, the iteration is stopped, even 955 * if some items have not yet been visited. 956 */ 957 void 958 BListView::DoForEach(bool (*func)(BListItem *, void *), void* parameter) 959 { 960 fList.DoForEach(reinterpret_cast<bool (*)(void*, void*)>(func), parameter) ; 961 } 962 963 /** 964 * Returns a pointer to the BListView's list of BListItems. You can index 965 * directly into the list of items if you're certain that the index is in range: 966 * BListItem *item = Items()[index] ; 967 * 968 * Although the practice is discouraged, you can also step through the list 969 * of items by incrementing the list pointer that Items() returns. Be aware 970 * that the list isn't null-terminated—you have to detect the end of the 971 * list by some other means. The simplest method is to count items: 972 * 973 * BListItem **ptr = myListView->Items() ; 974 * 975 * for ( long i = myListView->CountItems(); i > 0; i-- ) 976 * { 977 * . . . 978 * *ptr++ ; 979 * } 980 * 981 * You should never use the items pointer to alter the contents of the list. 982 */ 983 const BListItem** 984 BListView::Items() const 985 { 986 return reinterpret_cast<const BListItem**>(fList.Items()) ; 987 } 988 989 /** 990 * Invalidates the item at index so that an update message will 991 * be sent forcing the BListView to redraw it. 992 */ 993 void 994 BListView::InvalidateItem(int32 index) 995 { 996 if (index <= fList.CountItems()) 997 { 998 // Invalidate the rectangle of the list item 999 Invalidate(Bounds() | ItemFrame(index)) ; 1000 } 1001 } 1002 1003 void 1004 BListView::ScrollToSelection() 1005 { 1006 if (fFirstSelected != -1) 1007 { 1008 float tmp = 0 ; 1009 int32 index = 0 ; 1010 1011 for ( ; index < fFirstSelected ; ++index) 1012 { 1013 tmp += ItemAt(index)->Height() ; 1014 } 1015 1016 BRect rect (Bounds()) ; 1017 1018 if (tmp < rect.top) 1019 { 1020 ScrollTo(0, tmp) ; 1021 } 1022 else if ((tmp + ItemAt(index)->Height()) > rect.bottom) 1023 { 1024 ScrollTo(0, tmp + ItemAt(index)->Height() - (rect.bottom - rect.top)) ; 1025 } 1026 } 1027 } 1028 1029 /** 1030 * Selects the item at the index 1031 * if extend is false all former selections will be removed 1032 * if extend is true the 1033 */ 1034 void 1035 BListView::Select(int32 index, bool extend /* = false */) 1036 { 1037 // The index must be in range 1038 if (index < fList.CountItems() && (index >= 0)) 1039 { 1040 // Deselect all selected items 1041 if ((fFirstSelected != -1) && (!extend)) 1042 { 1043 for (int32 item = fFirstSelected ; item <= fLastSelected ; ++item) 1044 { 1045 if ((ItemAt(item)->IsSelected()) && (item != index)) 1046 { 1047 ItemAt(item)->Deselect() ; 1048 InvalidateItem(item) ; 1049 } 1050 } 1051 1052 // No item is selected any more 1053 fFirstSelected = -1 ; 1054 } 1055 1056 if (fFirstSelected == -1) 1057 { 1058 fFirstSelected = index ; 1059 fLastSelected = index ; 1060 } 1061 else if (index < fFirstSelected) 1062 fFirstSelected = index ; 1063 else if (index > fLastSelected) 1064 fLastSelected = index ; 1065 1066 if (!ItemAt(index)->IsSelected()) 1067 { 1068 ItemAt(index)->Select() ; 1069 InvalidateItem(index) ; 1070 } 1071 1072 SelectionChanged() ; 1073 } 1074 } 1075 1076 void 1077 BListView::Select(int32 from, int32 to, bool extend /* = false */) 1078 { 1079 if ((from <= to) && (to < fList.CountItems())) 1080 { 1081 // Deselect all current selected items 1082 if (!extend) 1083 { 1084 } 1085 1086 for (int32 index = from ; index <= to ; ++index) 1087 { 1088 ItemAt(index)->Select() ; 1089 } 1090 } 1091 } 1092 1093 /** 1094 * Returns true if the item at index is currently selected, and false if it's not. 1095 * It also returns false if the index is not in the range an therefoe does not 1096 * exist. 1097 */ 1098 bool 1099 BListView::IsItemSelected(int32 index) const 1100 { 1101 if ((fFirstSelected >= 0) && (index >= fFirstSelected) && (index <= fLastSelected)) 1102 return ItemAt(index)->IsSelected() ; 1103 1104 return false ; 1105 } 1106 1107 /** 1108 * Returns the index of a currently selected item in the list, or 1109 * a negative number if no item is selected. 1110 * The domain of the index passed as an argument is the current set 1111 * of selected items; the first selected item is at index 0, the second 1112 * at index 1, and so on, even if the selection is not contiguous. The 1113 * domain of the returned index is the set of all items in the list. 1114 * 1115 * To get all currently selected items, increment the passed index until 1116 * the function returns a negative number. 1117 */ 1118 int32 1119 BListView::CurrentSelection(int32 index /* = 0 */) const 1120 { 1121 if ((index >= 0) && (fFirstSelected >= 0)) 1122 { 1123 for (int32 count = (fFirstSelected > index ? fFirstSelected : index) ; 1124 count <= fLastSelected ; ++count) 1125 { 1126 if (ItemAt(count)->IsSelected()) 1127 { 1128 --index ; 1129 1130 if (index == 0) 1131 { 1132 return count ; 1133 } 1134 } 1135 } 1136 } 1137 1138 return -1 ; 1139 } 1140 1141 /** 1142 * Augments the BInvoker version of Invoke() to add three pieces of information 1143 * to each message the BListView sends: 1144 * 1145 * "when" - B_INT64_TYPE 1146 * When the message is sent, as measured by the number of microseconds 1147 * since 12:00:00 AM 1970. 1148 * 1149 * "source" - B_POINTER_TYPE 1150 * A pointer to the BListView object. 1151 * 1152 * "index" - B_INT32_TYPE 1153 * An array containing the index of every selected item. 1154 * 1155 * This function is called to send both the selection message and the 1156 * invocation message. It can also be called from application code. The default 1157 * target of the message (established by AttachedToWindow()) is the BWindow 1158 * where the BListView is located. 1159 * What it means to "invoke" selected items depends entirely on the 1160 * invocation BMessage and the receiver's response to it. This function does 1161 * nothing but send the message. 1162 */ 1163 1164 // check if it is done in the right way 1165 status_t 1166 BListView::Invoke(BMessage* msg /* = NULL */) 1167 { 1168 return BInvoker::Invoke(msg) ; 1169 } 1170 1171 /** 1172 * This function deselects all the items. 1173 */ 1174 void 1175 BListView::DeselectAll() 1176 { 1177 if (fFirstSelected != -1) 1178 { 1179 for (int32 index = fFirstSelected ; index <= fLastSelected ; ++index) 1180 { 1181 if (!ItemAt(index)->IsSelected()) 1182 { 1183 ItemAt(index)->Deselect() ; 1184 InvalidateItem(index) ; 1185 } 1186 } 1187 } 1188 } 1189 1190 /** 1191 * This function deselects all the items except those from index start through 1192 * index finish. 1193 */ 1194 void 1195 BListView::DeselectExcept(int32 except_from, int32 except_to) 1196 { 1197 // if no item is selected -> nothing to do 1198 if ((fFirstSelected != -1) && (except_from <= except_to)) 1199 { 1200 for (int32 index = fFirstSelected ; index < except_from ; ++index) 1201 { 1202 if (!ItemAt(index)->IsSelected()) 1203 { 1204 ItemAt(index)->Deselect() ; 1205 InvalidateItem(index) ; 1206 } 1207 } 1208 for (int32 index = except_to + 1 ; index <= fLastSelected ; ++index) 1209 { 1210 if (!ItemAt(index)->IsSelected()) 1211 { 1212 ItemAt(index)->Deselect() ; 1213 InvalidateItem(index) ; 1214 } 1215 } 1216 1217 SelectionChanged() ; 1218 } 1219 } 1220 1221 /** 1222 * These functions deselect the item at index. 1223 */ 1224 void 1225 BListView::Deselect(int32 index) 1226 { 1227 if (fFirstSelected != -1) 1228 { 1229 if ((index < fLastSelected) && (index >= fFirstSelected)) 1230 { 1231 if (!ItemAt(index)->IsSelected()) 1232 { 1233 ItemAt(index)->Deselect() ; 1234 InvalidateItem(index) ; 1235 SelectionChanged() ; 1236 } 1237 } 1238 } 1239 } 1240 1241 // Implemented by derived class 1242 void 1243 BListView::SelectionChanged() 1244 { 1245 } 1246 1247 void 1248 BListView::SortItems(int (*cmp)(const void *, const void *)) 1249 { 1250 fList.SortItems(cmp) ; 1251 Invalidate() ; 1252 } 1253 1254 1255 /* These functions bottleneck through DoMiscellaneous() */ 1256 bool 1257 BListView::SwapItems(int32 a, int32 b) 1258 { 1259 bool success = true ; 1260 1261 if ((a >= 0) && (a < fList.CountItems()) && (b >= 0) && (b < fList.CountItems())) 1262 { 1263 // just swap if a is not b 1264 if (a != b) 1265 { 1266 // ToDo chack if it is done correct 1267 BListItem* item = RemoveItem(a) ; 1268 AddItem(item, b - 1) ; 1269 item = RemoveItem(b) ; 1270 AddItem(item, a) ; 1271 } 1272 } 1273 else { 1274 success = false ; 1275 } 1276 1277 return success ; 1278 } 1279 1280 /** 1281 * MoveItem() moves the item located at the index from to the index 1282 * specified by the to argument. 1283 * This function returns true if the requested operation is 1284 * completed successfully, or false if the operation failed (for example, 1285 * if a specified index is out of range). 1286 */ 1287 1288 // Is redraw neccessary???? 1289 bool 1290 BListView::MoveItem(int32 from, int32 to) 1291 { 1292 bool success = true ; 1293 1294 if ((to >= 0) && (to < fList.CountItems())) 1295 { 1296 // no move required if from would be to 1297 if (from != to) 1298 { 1299 BListItem* item = RemoveItem(from) ; 1300 1301 if (item != NULL) 1302 AddItem(item, to - 1) ; 1303 else 1304 success = false ; 1305 } 1306 } 1307 else { 1308 success = false ; 1309 } 1310 1311 return success ; 1312 } 1313 1314 bool 1315 BListView::ReplaceItem(int32 index, BListItem* item) 1316 { 1317 bool success = true ; 1318 1319 // otherwise the "new" item would be deleted 1320 if (ItemAt(index) != item) 1321 { 1322 BListItem* old = RemoveItem(index) ; 1323 1324 if (old != NULL) 1325 { 1326 AddItem(item, index) ; 1327 delete old ; 1328 } 1329 // In this case the index is not in the range 1330 else { 1331 success = false ; 1332 } 1333 } 1334 1335 return success ; 1336 } 1337 1338 /** 1339 * Sets up the BListView and makes the BWindow to which it has become 1340 * attached the target for the messages it sends when items are selected 1341 * or invoked—provided another target hasn't already been set. In addition, 1342 * this function calls Update() for each item in the list to give it a 1343 * chance to adjust its layout. The BListView's vertical scroll bar is 1344 * also adjusted. 1345 * This function is called for you when the BListView becomes part of a 1346 * window's view hierarchy. 1347 */ 1348 void 1349 BListView::AttachedToWindow() 1350 { 1351 // If no target is set the window to which the ListView is attached will 1352 // become the target 1353 if (BInvoker::Target() == NULL) 1354 { 1355 BInvoker::SetTarget(BView::Window()) ; 1356 } 1357 1358 // call the update function of all items in the list 1359 for (int32 index = 0 ; index < fList.CountItems() ; ++index) 1360 { 1361 BFont font ; 1362 BView::GetFont(&font) ; 1363 ItemAt(index)->Update(this, &font) ; 1364 1365 if (fWidth < ItemAt(index)->Width()) 1366 fWidth = ItemAt(index)->Width() ; 1367 } 1368 1369 // Adjust the ScrollBars. 1370 FixupScrollBar() ; 1371 } 1372 1373 void 1374 BListView::FrameMoved(BPoint new_position) 1375 { 1376 } 1377 1378 /** 1379 * Returns the frame rectangle of the BListItem at index. The rectangle is stated 1380 * in the coordinate system of the BListView and defines the area where the item 1381 * is drawn. Items can differ in height, (but all have the same width ??). 1382 */ 1383 BRect 1384 BListView::ItemFrame(int32 index) 1385 { 1386 BRect rect ; 1387 1388 if ((index < fList.CountItems()) && (index >= 0)) 1389 { 1390 rect.left = 0 ; 1391 1392 for (int32 item = 0 ; item < index ; ++item) 1393 { 1394 rect.top += ItemAt(item)->Height() ; 1395 } 1396 1397 rect.bottom = rect.top + ItemAt(index)->Height() ; 1398 rect.right = Bounds().right ; 1399 } 1400 1401 return rect ; 1402 } 1403 1404 BHandler* 1405 BListView::ResolveSpecifier(BMessage *msg, int32 index, 1406 BMessage *specifier, int32 form, 1407 const char *property) 1408 { 1409 return NULL ; 1410 } 1411 1412 status_t 1413 BListView::GetSupportedSuites(BMessage *data) 1414 { 1415 return B_OK ; 1416 } 1417 1418 status_t 1419 BListView::Perform(perform_code d, void *arg) 1420 { 1421 return B_OK ; 1422 } 1423 1424 void 1425 BListView::WindowActivated(bool state) 1426 { 1427 } 1428 1429 void 1430 BListView::MouseUp(BPoint pt) 1431 { 1432 int32 index = IndexOf(pt) ; 1433 1434 if ((index > 0) && (fListType == B_MULTIPLE_SELECTION_LIST) && ((modifiers() & B_SHIFT_KEY))) 1435 { 1436 if (!ItemAt(index)->IsSelected()) 1437 Deselect(index) ; 1438 } 1439 } 1440 1441 void 1442 BListView::MouseMoved(BPoint pt, uint32 code, const BMessage *msg) 1443 { 1444 } 1445 1446 void 1447 BListView::DetachedFromWindow() 1448 { 1449 } 1450 1451 /** 1452 * Implemented by derived classes to permit users to drag items. 1453 * This function is called from the BListView's MouseDown() function; 1454 * it should initiate the drag-and-drop operation and return true, 1455 * or refuse to do so and return false. By default, it always 1456 * returns false. 1457 * The point that's passed to InitiateDrag() is the same as the 1458 * point passed to MouseDown(); it's where the cursor was located 1459 * when the user pressed the mouse button. The index of the item 1460 * under the cursor (the item that would be dragged) is passed as the 1461 * second argument, and the wasSelected flag indicates whether or 1462 * not the item was selected before the mouse button went down. 1463 * 1464 * A BListView allows users to autoscroll the list by holding the 1465 * mouse button down and dragging outside its frame rectangle. If a 1466 * derived class implements InitiateDrag() to drag an item each time 1467 * the user moves the mouse with a button down, it will hide this 1468 * autoscrolling behavior. Therefore, derived classes typically 1469 * permit users to drag items only if they're already selected (if 1470 * wasSelected is true). In other words, it takes two mouse-down events 1471 * to drag an item—one to select it and one to begin dragging it. 1472 */ 1473 bool 1474 BListView::InitiateDrag(BPoint pt, int32 itemIndex, bool initialySelected) 1475 { 1476 return false ; 1477 } 1478 1479 void 1480 BListView::ResizeToPreferred() 1481 { 1482 } 1483 1484 void 1485 BListView::GetPreferredSize(float* width, float* height) 1486 { 1487 if (height != NULL) 1488 { 1489 *height = 0 ; 1490 1491 for (int32 index = 0 ; index < CountItems() ; ++index) 1492 { 1493 *height += ItemAt(index)->Height() ; 1494 } 1495 } 1496 1497 if (width != NULL) 1498 *width = 0 ; 1499 } 1500 1501 void 1502 BListView::AllAttached() 1503 { 1504 } 1505 1506 void 1507 BListView::AllDetached() 1508 { 1509 } 1510 1511 //--------------------------------------------------------------------------- 1512 // protected! 1513 1514 bool 1515 BListView::DoMiscellaneous(MiscCode code, MiscData* data) 1516 { 1517 switch(code) 1518 { 1519 case B_NO_OP: 1520 break; 1521 1522 case B_REPLACE_OP: 1523 data->replace.index ; 1524 data->replace.item ; 1525 break ; 1526 1527 case B_MOVE_OP: 1528 data->move.from ; 1529 data->move.to ; 1530 break ; 1531 1532 case B_SWAP_OP: 1533 data->swap.a ; 1534 data->swap.b ; 1535 break ; 1536 } 1537 1538 return true ; 1539 } 1540 1541 /*----- Private or reserved -----------------------------------------*/ 1542 1543 void 1544 BListView::InitObject(list_view_type type) 1545 { 1546 fList.MakeEmpty() ; 1547 fListType = type ; 1548 fFirstSelected = -1 ; 1549 /* 1550 int32 fLastSelected; 1551 int32 fAnchorIndex; 1552 */ 1553 fWidth = 0 ; 1554 fSelectMessage = NULL ; 1555 fScrollView = NULL ; 1556 fTrack = NULL ; 1557 } 1558 1559 /** 1560 * Updates the scroll bars if the list view is targetted by a scroll view 1561 */ 1562 void 1563 BListView::FixupScrollBar() 1564 { 1565 // set the range of the scroll bars of the scroll view 1566 if (fScrollView != NULL) 1567 { 1568 BScrollBar* bar ; 1569 float height ; 1570 float width ; 1571 BRect bounds(Bounds()) ; 1572 1573 GetPreferredSize(&width, &height) ; 1574 height -= (bounds.bottom - bounds.top) ; 1575 width -= (bounds.right - bounds.left) ; 1576 1577 if (height < 0) 1578 height = 0 ; 1579 1580 if (width < 0) 1581 width = 0 ; 1582 1583 if ((bar = fScrollView->ScrollBar(B_HORIZONTAL)) != NULL) 1584 { 1585 bar->SetRange(0, width) ; 1586 bar->SetSteps(ItemAt(0)->Height(), bounds.bottom - bounds.top) ; 1587 } 1588 1589 if ((bar = fScrollView->ScrollBar(B_VERTICAL)) != NULL) 1590 { 1591 bar->SetRange(0, height) ; 1592 bar->SetSteps(ItemAt(0)->Height(), bounds.bottom - bounds.top) ; 1593 } 1594 } 1595 } 1596 1597 void 1598 BListView::InvalidateFrom(int32 index) 1599 { 1600 if ((index >= 0) && (index < CountItems())) 1601 { 1602 BRect frame(ItemFrame(index)) ; 1603 1604 for (++index ; index < fList.CountItems() ; ++index) 1605 frame.bottom += ItemAt(index)->Height() ; 1606 1607 // build the rect which needs an update 1608 BRect bounds(Bounds()) ; 1609 frame = frame & bounds ; 1610 frame = frame | bounds ; 1611 Invalidate(frame) ; 1612 } 1613 } 1614 1615 inline status_t 1616 BListView::PostMsg(BMessage* msg) 1617 { 1618 return BInvoker::Invoke(msg) ; 1619 } 1620 1621 void 1622 BListView::FontChanged() 1623 { 1624 // all items need to be updated 1625 for (int32 i = 0 ; i < fList.CountItems() ; ++i) 1626 { 1627 ItemAt(i)->Update(this, NULL) ; 1628 } 1629 } 1630 1631 int32 1632 BListView::RangeCheck(int32 index) 1633 { 1634 return 0 ; 1635 } 1636 1637 bool 1638 BListView::_Select(int32 index, bool extend) 1639 { 1640 return true ; 1641 } 1642 1643 bool 1644 BListView::_Select(int32 from, int32 to, bool extend) 1645 { 1646 return true ; 1647 } 1648 1649 bool 1650 BListView::_Deselect(int32 index) 1651 { 1652 return true ; 1653 } 1654 1655 void 1656 BListView::Deselect(int32 from, int32 to) 1657 { 1658 for ( ; (from <= to) && (from < fList.CountItems()) ; ++from) 1659 { 1660 ItemAt(from)->Deselect() ; 1661 } 1662 } 1663 1664 bool 1665 BListView::_DeselectAll(int32 except_from, int32 except_to) 1666 { 1667 for (int32 i = 0 ; i < CountItems() ; ++i) 1668 { 1669 if (i == except_from) 1670 { 1671 i = except_to ; 1672 if (i >= CountItems()) 1673 break ; 1674 } 1675 1676 ItemAt(i)->Deselect() ; 1677 } 1678 1679 return true ; 1680 } 1681 1682 void 1683 BListView::PerformDelayedSelect() 1684 { 1685 } 1686 1687 bool 1688 BListView::TryInitiateDrag(BPoint where) 1689 { 1690 return true ; 1691 } 1692 1693 int32 1694 BListView::CalcFirstSelected(int32 after) 1695 { 1696 return 0 ; 1697 } 1698 1699 int32 1700 BListView::CalcLastSelected(int32 before) 1701 { 1702 return 0 ; 1703 } 1704 1705 void 1706 BListView::DrawItem(BListItem* item, BRect itemRect, 1707 bool complete /* = false */) 1708 { 1709 } 1710 1711 bool 1712 BListView::DoSwapItems(int32 a, int32 b) 1713 { 1714 return true ; 1715 } 1716 1717 bool 1718 BListView::DoMoveItem(int32 from, int32 to) 1719 { 1720 return true ; 1721 } 1722 1723 bool 1724 BListView::DoReplaceItem(int32 index, BListItem* item) 1725 { 1726 return true ; 1727 } 1728 1729 void 1730 BListView::RescanSelection(int32 from, int32 to) 1731 { 1732 } 1733 1734 void 1735 BListView::DoMouseUp(BPoint where) 1736 { 1737 } 1738 1739 void 1740 BListView::DoMouseMoved(BPoint where) 1741 { 1742 } 1743