1 /* 2 * Copyright 2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "ListViews.h" 10 11 #include <malloc.h> 12 #include <stdio.h> 13 #include <typeinfo> 14 15 #include <Bitmap.h> 16 #include <Cursor.h> 17 #include <Entry.h> 18 #include <MessageRunner.h> 19 #include <Messenger.h> 20 #include <ScrollBar.h> 21 #include <ScrollView.h> 22 #include <String.h> 23 #include <Window.h> 24 25 #include "cursors.h" 26 27 #include "Selection.h" 28 29 #define MAX_DRAG_HEIGHT 200.0 30 #define ALPHA 170 31 #define TEXT_OFFSET 5.0 32 33 enum { 34 MSG_TICK = 'tick', 35 }; 36 37 // SimpleItem class 38 SimpleItem::SimpleItem(const char *name) 39 : BStringItem(name) 40 { 41 } 42 43 SimpleItem::~SimpleItem() 44 { 45 } 46 47 // SimpleItem::DrawItem 48 void 49 SimpleItem::Draw(BView *owner, BRect frame, uint32 flags) 50 { 51 DrawBackground(owner, frame, flags); 52 // label 53 owner->SetHighColor(0, 0, 0, 255); 54 font_height fh; 55 owner->GetFontHeight(&fh); 56 const char* text = Text(); 57 BString truncatedString(text); 58 owner->TruncateString(&truncatedString, B_TRUNCATE_MIDDLE, 59 frame.Width() - TEXT_OFFSET - 4.0); 60 float height = frame.Height(); 61 float textHeight = fh.ascent + fh.descent; 62 BPoint textPoint; 63 textPoint.x = frame.left + TEXT_OFFSET; 64 textPoint.y = frame.top 65 + ceilf(height / 2.0 - textHeight / 2.0 66 + fh.ascent); 67 owner->DrawString(truncatedString.String(), textPoint); 68 } 69 70 // SimpleItem::DrawBackground 71 void 72 SimpleItem::DrawBackground(BView *owner, BRect frame, uint32 flags) 73 { 74 // stroke a blue frame around the item if it's focused 75 if (flags & FLAGS_FOCUSED) { 76 owner->SetLowColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 77 owner->StrokeRect(frame, B_SOLID_LOW); 78 frame.InsetBy(1.0, 1.0); 79 } 80 // figure out bg-color 81 rgb_color color = (rgb_color){ 255, 255, 255, 255 }; 82 if (flags & FLAGS_TINTED_LINE) 83 color = tint_color(color, 1.06); 84 // background 85 if (IsSelected()) 86 color = tint_color(color, (B_DARKEN_1_TINT + B_DARKEN_2_TINT) / 2.0); 87 owner->SetLowColor(color); 88 owner->FillRect(frame, B_SOLID_LOW); 89 } 90 91 // DragSortableListView class 92 DragSortableListView::DragSortableListView(BRect frame, const char* name, 93 list_view_type type, uint32 resizingMode, 94 uint32 flags) 95 : BListView(frame, name, type, resizingMode, flags), 96 fDropRect(0.0, 0.0, -1.0, -1.0), 97 fMouseWheelFilter(NULL), 98 fScrollPulse(NULL), 99 fDropIndex(-1), 100 fLastClickedItem(NULL), 101 fScrollView(NULL), 102 fDragCommand(B_SIMPLE_DATA), 103 fFocusedIndex(-1), 104 105 fSelection(NULL), 106 fSyncingToSelection(false), 107 fModifyingSelection(false) 108 { 109 SetViewColor(B_TRANSPARENT_32_BIT); 110 } 111 112 DragSortableListView::~DragSortableListView() 113 { 114 delete fMouseWheelFilter; 115 delete fScrollPulse; 116 117 SetSelection(NULL); 118 } 119 120 // AttachedToWindow 121 void 122 DragSortableListView::AttachedToWindow() 123 { 124 if (!fMouseWheelFilter) 125 fMouseWheelFilter = new MouseWheelFilter(this); 126 Window()->AddCommonFilter(fMouseWheelFilter); 127 128 BListView::AttachedToWindow(); 129 130 // work arround a bug in BListView 131 BRect bounds = Bounds(); 132 BListView::FrameResized(bounds.Width(), bounds.Height()); 133 } 134 135 // DetachedFromWindow 136 void 137 DragSortableListView::DetachedFromWindow() 138 { 139 Window()->RemoveCommonFilter(fMouseWheelFilter); 140 } 141 142 // FrameResized 143 void 144 DragSortableListView::FrameResized(float width, float height) 145 { 146 BListView::FrameResized(width, height); 147 } 148 149 /* 150 // MakeFocus 151 void 152 DragSortableListView::MakeFocus(bool focused) 153 { 154 if (focused != IsFocus()) { 155 Invalidate(); 156 BListView::MakeFocus(focused); 157 } 158 } 159 */ 160 // Draw 161 void 162 DragSortableListView::Draw(BRect updateRect) 163 { 164 int32 firstIndex = IndexOf(updateRect.LeftTop()); 165 int32 lastIndex = IndexOf(updateRect.RightBottom()); 166 if (firstIndex >= 0) { 167 if (lastIndex < firstIndex) 168 lastIndex = CountItems() - 1; 169 // update rect contains items 170 BRect r = updateRect; 171 for (int32 i = firstIndex; i <= lastIndex; i++) { 172 r = ItemFrame(i); 173 DrawListItem(this, i, r); 174 } 175 updateRect.top = r.bottom + 1.0; 176 if (updateRect.IsValid()) { 177 SetLowColor(255, 255, 255, 255); 178 FillRect(updateRect, B_SOLID_LOW); 179 } 180 } else { 181 SetLowColor(255, 255, 255, 255); 182 FillRect(updateRect, B_SOLID_LOW); 183 } 184 // drop anticipation indication 185 if (fDropRect.IsValid()) { 186 SetHighColor(255, 0, 0, 255); 187 StrokeRect(fDropRect); 188 } 189 /* // focus indication 190 if (IsFocus()) { 191 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 192 StrokeRect(Bounds()); 193 }*/ 194 } 195 196 // ScrollTo 197 void 198 DragSortableListView::ScrollTo(BPoint where) 199 { 200 uint32 buttons; 201 BPoint point; 202 GetMouse(&point, &buttons, false); 203 uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; 204 MouseMoved(point, transit, &fDragMessageCopy); 205 BListView::ScrollTo(where); 206 } 207 208 // TargetedByScrollView 209 void 210 DragSortableListView::TargetedByScrollView(BScrollView* scrollView) 211 { 212 fScrollView = scrollView; 213 BListView::TargetedByScrollView(scrollView); 214 } 215 216 // InitiateDrag 217 bool 218 DragSortableListView::InitiateDrag(BPoint point, int32 index, bool) 219 { 220 // supress drag&drop while an item is focused 221 if (fFocusedIndex >= 0) 222 return false; 223 224 bool success = false; 225 BListItem* item = ItemAt(CurrentSelection(0)); 226 if (!item) { 227 // workarround a timing problem 228 Select(index); 229 item = ItemAt(index); 230 } 231 if (item) { 232 // create drag message 233 BMessage msg(fDragCommand); 234 MakeDragMessage(&msg); 235 // figure out drag rect 236 float width = Bounds().Width(); 237 BRect dragRect(0.0, 0.0, width, -1.0); 238 // figure out, how many items fit into our bitmap 239 int32 numItems; 240 bool fade = false; 241 for (numItems = 0; BListItem* item = ItemAt(CurrentSelection(numItems)); numItems++) { 242 dragRect.bottom += ceilf(item->Height()) + 1.0; 243 if (dragRect.Height() > MAX_DRAG_HEIGHT) { 244 fade = true; 245 dragRect.bottom = MAX_DRAG_HEIGHT; 246 numItems++; 247 break; 248 } 249 } 250 BBitmap* dragBitmap = new BBitmap(dragRect, B_RGB32, true); 251 if (dragBitmap && dragBitmap->IsValid()) { 252 if (BView *v = new BView(dragBitmap->Bounds(), "helper", 253 B_FOLLOW_NONE, B_WILL_DRAW)) { 254 dragBitmap->AddChild(v); 255 dragBitmap->Lock(); 256 BRect itemBounds(dragRect) ; 257 itemBounds.bottom = 0.0; 258 // let all selected items, that fit into our drag_bitmap, draw 259 for (int32 i = 0; i < numItems; i++) { 260 int32 index = CurrentSelection(i); 261 BListItem* item = ItemAt(index); 262 itemBounds.bottom = itemBounds.top + ceilf(item->Height()); 263 if (itemBounds.bottom > dragRect.bottom) 264 itemBounds.bottom = dragRect.bottom; 265 DrawListItem(v, index, itemBounds); 266 itemBounds.top = itemBounds.bottom + 1.0; 267 } 268 // make a black frame arround the edge 269 v->SetHighColor(0, 0, 0, 255); 270 v->StrokeRect(v->Bounds()); 271 v->Sync(); 272 273 uint8 *bits = (uint8 *)dragBitmap->Bits(); 274 int32 height = (int32)dragBitmap->Bounds().Height() + 1; 275 int32 width = (int32)dragBitmap->Bounds().Width() + 1; 276 int32 bpr = dragBitmap->BytesPerRow(); 277 278 if (fade) { 279 for (int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr) { 280 uint8 *line = bits + 3; 281 for (uint8 *end = line + 4 * width; line < end; line += 4) 282 *line = ALPHA; 283 } 284 for (int32 y = height - ALPHA / 2; y < height; y++, bits += bpr) { 285 uint8 *line = bits + 3; 286 for (uint8 *end = line + 4 * width; line < end; line += 4) 287 *line = (height - y) << 1; 288 } 289 } else { 290 for (int32 y = 0; y < height; y++, bits += bpr) { 291 uint8 *line = bits + 3; 292 for (uint8 *end = line + 4 * width; line < end; line += 4) 293 *line = ALPHA; 294 } 295 } 296 dragBitmap->Unlock(); 297 } 298 } else { 299 delete dragBitmap; 300 dragBitmap = NULL; 301 } 302 if (dragBitmap) 303 DragMessage(&msg, dragBitmap, B_OP_ALPHA, BPoint(0.0, 0.0)); 304 else 305 DragMessage(&msg, dragRect.OffsetToCopy(point), this); 306 307 _SetDragMessage(&msg); 308 success = true; 309 } 310 return success; 311 } 312 313 // WindowActivated 314 void 315 DragSortableListView::WindowActivated(bool active) 316 { 317 // workarround for buggy focus indication of BScrollView 318 if (BView* view = Parent()) 319 view->Invalidate(); 320 } 321 322 // MessageReceived 323 void 324 DragSortableListView::MessageReceived(BMessage* message) 325 { 326 if (message->what == fDragCommand) { 327 int32 count = CountItems(); 328 if (fDropIndex < 0 || fDropIndex > count) 329 fDropIndex = count; 330 HandleDropMessage(message, fDropIndex); 331 fDropIndex = -1; 332 } else { 333 switch (message->what) { 334 case MSG_TICK: { 335 float scrollV = 0.0; 336 BRect rect(Bounds()); 337 BPoint point; 338 uint32 buttons; 339 GetMouse(&point, &buttons, false); 340 if (rect.Contains(point)) { 341 // calculate the vertical scrolling offset 342 float hotDist = rect.Height() * SCROLL_AREA; 343 if (point.y > rect.bottom - hotDist) 344 scrollV = hotDist - (rect.bottom - point.y); 345 else if (point.y < rect.top + hotDist) 346 scrollV = (point.y - rect.top) - hotDist; 347 } 348 // scroll 349 if (scrollV != 0.0 && fScrollView) { 350 if (BScrollBar* scrollBar = fScrollView->ScrollBar(B_VERTICAL)) { 351 float value = scrollBar->Value(); 352 scrollBar->SetValue(scrollBar->Value() + scrollV); 353 if (scrollBar->Value() != value) { 354 // update mouse position 355 uint32 buttons; 356 BPoint point; 357 GetMouse(&point, &buttons, false); 358 uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; 359 MouseMoved(point, transit, &fDragMessageCopy); 360 } 361 } 362 } 363 break; 364 } 365 // case B_MODIFIERS_CHANGED: 366 // ModifiersChanged(); 367 // break; 368 case B_MOUSE_WHEEL_CHANGED: { 369 BListView::MessageReceived(message); 370 BPoint point; 371 uint32 buttons; 372 GetMouse(&point, &buttons, false); 373 uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; 374 MouseMoved(point, transit, &fDragMessageCopy); 375 break; 376 } 377 default: 378 BListView::MessageReceived(message); 379 break; 380 } 381 } 382 } 383 384 // KeyDown 385 void 386 DragSortableListView::KeyDown(const char* bytes, int32 numBytes) 387 { 388 if (numBytes < 1) 389 return; 390 391 if ((bytes[0] == B_BACKSPACE) || (bytes[0] == B_DELETE)) 392 RemoveSelected(); 393 394 BListView::KeyDown(bytes, numBytes); 395 } 396 397 // MouseDown 398 void 399 DragSortableListView::MouseDown(BPoint where) 400 { 401 int32 clicks = 1; 402 uint32 buttons = 0; 403 Window()->CurrentMessage()->FindInt32("clicks", &clicks); 404 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 405 int32 clickedIndex = -1; 406 for (int32 i = 0; BListItem* item = ItemAt(i); i++) { 407 if (ItemFrame(i).Contains(where)) { 408 if (clicks == 2) { 409 // only do something if user clicked the same item twice 410 if (fLastClickedItem == item) 411 DoubleClicked(i); 412 } else { 413 // remember last clicked item 414 fLastClickedItem = item; 415 } 416 clickedIndex = i; 417 break; 418 } 419 } 420 if (clickedIndex == -1) 421 fLastClickedItem = NULL; 422 423 BListItem* item = ItemAt(clickedIndex); 424 if (ListType() == B_MULTIPLE_SELECTION_LIST 425 && item && (buttons & B_SECONDARY_MOUSE_BUTTON)) { 426 if (item->IsSelected()) 427 Deselect(clickedIndex); 428 else 429 Select(clickedIndex, true); 430 } else { 431 BListView::MouseDown(where); 432 } 433 } 434 435 // MouseMoved 436 void 437 DragSortableListView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg) 438 { 439 if (msg && AcceptDragMessage(msg)) { 440 switch (transit) { 441 case B_ENTERED_VIEW: 442 case B_INSIDE_VIEW: { 443 // remember drag message 444 // this is needed to react on modifier changes 445 _SetDragMessage(msg); 446 // set drop target through virtual function 447 SetDropTargetRect(msg, where); 448 // go into autoscrolling mode 449 BRect r = Bounds(); 450 r.InsetBy(0.0, r.Height() * SCROLL_AREA); 451 SetAutoScrolling(!r.Contains(where)); 452 break; 453 } 454 case B_EXITED_VIEW: 455 // forget drag message 456 _SetDragMessage(NULL); 457 SetAutoScrolling(false); 458 // fall through 459 case B_OUTSIDE_VIEW: 460 _RemoveDropAnticipationRect(); 461 break; 462 } 463 } else { 464 _RemoveDropAnticipationRect(); 465 BListView::MouseMoved(where, transit, msg); 466 _SetDragMessage(NULL); 467 SetAutoScrolling(false); 468 469 BCursor cursor(B_HAND_CURSOR); 470 SetViewCursor(&cursor, true); 471 } 472 fLastMousePos = where; 473 } 474 475 // MouseUp 476 void 477 DragSortableListView::MouseUp(BPoint where) 478 { 479 // remove drop mark 480 _SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0)); 481 SetAutoScrolling(false); 482 // be sure to forget drag message 483 _SetDragMessage(NULL); 484 BListView::MouseUp(where); 485 486 BCursor cursor(B_HAND_CURSOR); 487 SetViewCursor(&cursor, true); 488 } 489 490 // DrawItem 491 void 492 DragSortableListView::DrawItem(BListItem *item, BRect itemFrame, bool complete) 493 { 494 DrawListItem(this, IndexOf(item), itemFrame); 495 /* if (IsFocus()) { 496 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 497 StrokeRect(Bounds()); 498 }*/ 499 } 500 501 // #pragma mark - 502 503 // MouseWheelChanged 504 bool 505 DragSortableListView::MouseWheelChanged(float x, float y) 506 { 507 BPoint where; 508 uint32 buttons; 509 GetMouse(&where, &buttons, false); 510 if (Bounds().Contains(where)) 511 return true; 512 else 513 return false; 514 } 515 516 // #pragma mark - 517 518 // ObjectChanged 519 void 520 DragSortableListView::ObjectChanged(const Observable* object) 521 { 522 if (object != fSelection || fModifyingSelection || fSyncingToSelection) 523 return; 524 525 //printf("%s - syncing start\n", Name()); 526 fSyncingToSelection = true; 527 528 // try to sync to Selection 529 BList selectedItems; 530 531 int32 count = fSelection->CountSelected(); 532 for (int32 i = 0; i < count; i++) { 533 int32 index = IndexOfSelectable(fSelection->SelectableAtFast(i)); 534 if (index >= 0) { 535 BListItem* item = ItemAt(index); 536 if (item && !selectedItems.HasItem((void*)item)) 537 selectedItems.AddItem((void*)item); 538 } 539 } 540 541 count = selectedItems.CountItems(); 542 if (count == 0) { 543 if (CurrentSelection(0) >= 0) 544 DeselectAll(); 545 } else { 546 count = CountItems(); 547 for (int32 i = 0; i < count; i++) { 548 BListItem* item = ItemAt(i); 549 bool selected = selectedItems.RemoveItem((void*)item); 550 if (item->IsSelected() != selected) { 551 Select(i, true); 552 } 553 } 554 } 555 556 fSyncingToSelection = false; 557 //printf("%s - done\n", Name()); 558 } 559 560 // #pragma mark - 561 562 // SetDragCommand 563 void 564 DragSortableListView::SetDragCommand(uint32 command) 565 { 566 fDragCommand = command; 567 } 568 569 // ModifiersChaned 570 void 571 DragSortableListView::ModifiersChanged() 572 { 573 SetDropTargetRect(&fDragMessageCopy, fLastMousePos); 574 } 575 576 // SetItemFocused 577 void 578 DragSortableListView::SetItemFocused(int32 index) 579 { 580 InvalidateItem(fFocusedIndex); 581 InvalidateItem(index); 582 fFocusedIndex = index; 583 } 584 585 // AcceptDragMessage 586 bool 587 DragSortableListView::AcceptDragMessage(const BMessage* message) const 588 { 589 return message->what == fDragCommand; 590 } 591 592 // SetDropTargetRect 593 void 594 DragSortableListView::SetDropTargetRect(const BMessage* message, BPoint where) 595 596 { 597 if (AcceptDragMessage(message)) { 598 bool copy = modifiers() & B_SHIFT_KEY; 599 bool replaceAll = !message->HasPointer("list") && !copy; 600 BRect r = Bounds(); 601 if (replaceAll) { 602 r.bottom--; // compensate for scrollbar offset 603 _SetDropAnticipationRect(r); 604 fDropIndex = -1; 605 } else { 606 // offset where by half of item height 607 r = ItemFrame(0); 608 where.y += r.Height() / 2.0; 609 610 int32 index = IndexOf(where); 611 if (index < 0) 612 index = CountItems(); 613 _SetDropIndex(index); 614 615 const uchar* cursorData = copy ? kCopyCursor : B_HAND_CURSOR; 616 BCursor cursor(cursorData); 617 SetViewCursor(&cursor, true); 618 } 619 } 620 } 621 622 623 bool 624 DragSortableListView::HandleDropMessage(const BMessage* message, 625 int32 dropIndex) 626 { 627 DragSortableListView *list = NULL; 628 if (message->FindPointer("list", (void **)&list) != B_OK || list != this) 629 return false; 630 631 BList items; 632 int32 index; 633 for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK; i++) { 634 BListItem* item = ItemAt(index); 635 if (item != NULL) 636 items.AddItem((void*)item); 637 } 638 639 if (items.CountItems() == 0) 640 return false; 641 642 if ((modifiers() & B_SHIFT_KEY) != 0) 643 CopyItems(items, dropIndex); 644 else 645 MoveItems(items, dropIndex); 646 647 return true; 648 } 649 650 // SetAutoScrolling 651 void 652 DragSortableListView::SetAutoScrolling(bool enable) 653 { 654 if (fScrollPulse && enable) 655 return; 656 if (enable) { 657 BMessenger messenger(this, Window()); 658 BMessage message(MSG_TICK); 659 fScrollPulse = new BMessageRunner(messenger, &message, 40000LL); 660 } else { 661 delete fScrollPulse; 662 fScrollPulse = NULL; 663 } 664 } 665 666 // DoesAutoScrolling 667 bool 668 DragSortableListView::DoesAutoScrolling() const 669 { 670 return fScrollPulse; 671 } 672 673 // ScrollTo 674 void 675 DragSortableListView::ScrollTo(int32 index) 676 { 677 if (index < 0) 678 index = 0; 679 if (index >= CountItems()) 680 index = CountItems() - 1; 681 682 if (ItemAt(index)) { 683 BRect itemFrame = ItemFrame(index); 684 BRect bounds = Bounds(); 685 if (itemFrame.top < bounds.top) { 686 ScrollTo(itemFrame.LeftTop()); 687 } else if (itemFrame.bottom > bounds.bottom) { 688 ScrollTo(BPoint(0.0, itemFrame.bottom - bounds.Height())); 689 } 690 } 691 } 692 693 // MoveItems 694 void 695 DragSortableListView::MoveItems(BList& items, int32 index) 696 { 697 DeselectAll(); 698 // we remove the items while we look at them, the insertion index is decreased 699 // when the items index is lower, so that we insert at the right spot after 700 // removal 701 BList removedItems; 702 int32 count = items.CountItems(); 703 for (int32 i = 0; i < count; i++) { 704 BListItem* item = (BListItem*)items.ItemAt(i); 705 int32 removeIndex = IndexOf(item); 706 if (RemoveItem(item) && removedItems.AddItem((void*)item)) { 707 if (removeIndex < index) 708 index--; 709 } 710 // else ??? -> blow up 711 } 712 for (int32 i = 0; 713 BListItem* item = (BListItem*)removedItems.ItemAt(i); i++) { 714 if (AddItem(item, index)) { 715 // after we're done, the newly inserted items will be selected 716 Select(index, true); 717 // next items will be inserted after this one 718 index++; 719 } else 720 delete item; 721 } 722 } 723 724 // CopyItems 725 void 726 DragSortableListView::CopyItems(BList& items, int32 index) 727 { 728 DeselectAll(); 729 // by inserting the items after we copied all items first, we avoid 730 // cloning an item we already inserted and messing everything up 731 // in other words, don't touch the list before we know which items 732 // need to be cloned 733 BList clonedItems; 734 int32 count = items.CountItems(); 735 for (int32 i = 0; i < count; i++) { 736 BListItem* item = CloneItem(IndexOf((BListItem*)items.ItemAt(i))); 737 if (item && !clonedItems.AddItem((void*)item)) 738 delete item; 739 } 740 for (int32 i = 0; 741 BListItem* item = (BListItem*)clonedItems.ItemAt(i); i++) { 742 if (AddItem(item, index)) { 743 // after we're done, the newly inserted items will be selected 744 Select(index, true); 745 // next items will be inserted after this one 746 index++; 747 } else 748 delete item; 749 } 750 } 751 752 // RemoveItemList 753 void 754 DragSortableListView::RemoveItemList(BList& items) 755 { 756 int32 count = items.CountItems(); 757 for (int32 i = 0; i < count; i++) { 758 BListItem* item = (BListItem*)items.ItemAt(i); 759 if (RemoveItem(item)) 760 delete item; 761 } 762 } 763 764 // RemoveSelected 765 void 766 DragSortableListView::RemoveSelected() 767 { 768 // if (fFocusedIndex >= 0) 769 // return; 770 771 BList items; 772 for (int32 i = 0; BListItem* item = ItemAt(CurrentSelection(i)); i++) 773 items.AddItem((void*)item); 774 RemoveItemList(items); 775 } 776 777 // #pragma mark - 778 779 // SetSelection 780 void 781 DragSortableListView::SetSelection(Selection* selection) 782 { 783 if (fSelection == selection) 784 return; 785 786 if (fSelection) 787 fSelection->RemoveObserver(this); 788 789 fSelection = selection; 790 791 if (fSelection) 792 fSelection->AddObserver(this); 793 } 794 795 // IndexOfSelectable 796 int32 797 DragSortableListView::IndexOfSelectable(Selectable* selectable) const 798 { 799 return -1; 800 } 801 802 // SelectableFor 803 Selectable* 804 DragSortableListView::SelectableFor(BListItem* item) const 805 { 806 return NULL; 807 } 808 809 // SelectAll 810 void 811 DragSortableListView::SelectAll() 812 { 813 Select(0, CountItems() - 1); 814 } 815 816 // CountSelectedItems 817 int32 818 DragSortableListView::CountSelectedItems() const 819 { 820 int32 count = 0; 821 while (CurrentSelection(count) >= 0) 822 count++; 823 return count; 824 } 825 826 // SelectionChanged 827 void 828 DragSortableListView::SelectionChanged() 829 { 830 //printf("%s::SelectionChanged()", typeid(*this).name()); 831 // modify global Selection 832 if (!fSelection || fSyncingToSelection) 833 return; 834 835 fModifyingSelection = true; 836 837 BList selectables; 838 for (int32 i = 0; BListItem* item = ItemAt(CurrentSelection(i)); i++) { 839 Selectable* selectable = SelectableFor(item); 840 if (selectable) 841 selectables.AddItem((void*)selectable); 842 } 843 844 AutoNotificationSuspender _(fSelection); 845 846 int32 count = selectables.CountItems(); 847 if (count == 0) { 848 //printf(" deselecting all\n"); 849 if (!fSyncingToSelection) 850 fSelection->DeselectAll(); 851 } else { 852 //printf(" selecting %ld items\n", count); 853 for (int32 i = 0; i < count; i++) { 854 Selectable* selectable = (Selectable*)selectables.ItemAtFast(i); 855 fSelection->Select(selectable, i > 0); 856 } 857 } 858 859 fModifyingSelection = false; 860 } 861 862 // #pragma mark - 863 864 // DeleteItem 865 bool 866 DragSortableListView::DeleteItem(int32 index) 867 { 868 BListItem* item = ItemAt(index); 869 if (item && RemoveItem(item)) { 870 delete item; 871 return true; 872 } 873 return false; 874 } 875 876 // _SetDropAnticipationRect 877 void 878 DragSortableListView::_SetDropAnticipationRect(BRect r) 879 { 880 if (fDropRect != r) { 881 if (fDropRect.IsValid()) 882 Invalidate(fDropRect); 883 fDropRect = r; 884 if (fDropRect.IsValid()) 885 Invalidate(fDropRect); 886 } 887 } 888 889 // _SetDropIndex 890 void 891 DragSortableListView::_SetDropIndex(int32 index) 892 { 893 if (fDropIndex != index) { 894 fDropIndex = index; 895 if (fDropIndex >= 0) { 896 int32 count = CountItems(); 897 if (fDropIndex == count) { 898 BRect r; 899 if (ItemAt(count - 1)) { 900 r = ItemFrame(count - 1); 901 r.top = r.bottom; 902 r.bottom = r.top + 1.0; 903 } else { 904 r = Bounds(); 905 r.bottom--; // compensate for scrollbars moved slightly out of window 906 } 907 _SetDropAnticipationRect(r); 908 } else { 909 BRect r = ItemFrame(fDropIndex); 910 r.top--; 911 r.bottom = r.top + 1.0; 912 _SetDropAnticipationRect(r); 913 } 914 } 915 } 916 } 917 918 // _RemoveDropAnticipationRect 919 void 920 DragSortableListView::_RemoveDropAnticipationRect() 921 { 922 _SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0)); 923 // _SetDropIndex(-1); 924 } 925 926 // _SetDragMessage 927 void 928 DragSortableListView::_SetDragMessage(const BMessage* message) 929 { 930 if (message) 931 fDragMessageCopy = *message; 932 else 933 fDragMessageCopy.what = 0; 934 } 935 936 // #pragma mark - SimpleListView 937 938 // SimpleListView class 939 SimpleListView::SimpleListView(BRect frame, BMessage* selectionChangeMessage) 940 : DragSortableListView(frame, "playlist listview", 941 B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL, 942 B_WILL_DRAW | B_NAVIGABLE 943 | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE), 944 fSelectionChangeMessage(selectionChangeMessage) 945 { 946 } 947 948 // SimpleListView class 949 SimpleListView::SimpleListView(BRect frame, const char* name, 950 BMessage* selectionChangeMessage, 951 list_view_type type, 952 uint32 resizingMode, uint32 flags) 953 : DragSortableListView(frame, name, type, resizingMode, flags), 954 fSelectionChangeMessage(selectionChangeMessage) 955 { 956 } 957 958 // destructor 959 SimpleListView::~SimpleListView() 960 { 961 delete fSelectionChangeMessage; 962 } 963 964 #ifdef LIB_LAYOUT 965 // layoutprefs 966 minimax 967 SimpleListView::layoutprefs() 968 { 969 mpm.mini.x = 30.0; 970 mpm.maxi.x = 10000.0; 971 mpm.mini.y = 50.0; 972 mpm.maxi.y = 10000.0; 973 mpm.weight = 1.0; 974 return mpm; 975 } 976 977 // layout 978 BRect 979 SimpleListView::layout(BRect frame) 980 { 981 MoveTo(frame.LeftTop()); 982 ResizeTo(frame.Width(), frame.Height()); 983 return Frame(); 984 } 985 #endif // LIB_LAYOUT 986 987 // DetachedFromWindow 988 void 989 SimpleListView::DetachedFromWindow() 990 { 991 DragSortableListView::DetachedFromWindow(); 992 _MakeEmpty(); 993 } 994 995 // MessageReceived 996 void 997 SimpleListView::MessageReceived(BMessage* message) 998 { 999 switch (message->what) { 1000 default: 1001 DragSortableListView::MessageReceived(message); 1002 break; 1003 } 1004 } 1005 1006 // CloneItem 1007 BListItem* 1008 SimpleListView::CloneItem(int32 atIndex) const 1009 { 1010 BListItem* clone = NULL; 1011 if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(atIndex))) 1012 clone = new SimpleItem(item->Text()); 1013 return clone; 1014 } 1015 1016 // DrawListItem 1017 void 1018 SimpleListView::DrawListItem(BView* owner, int32 index, BRect frame) const 1019 { 1020 if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(index))) { 1021 uint32 flags = FLAGS_NONE; 1022 if (index == fFocusedIndex) 1023 flags |= FLAGS_FOCUSED; 1024 if (index % 2) 1025 flags |= FLAGS_TINTED_LINE; 1026 item->Draw(owner, frame, flags); 1027 } 1028 } 1029 1030 // MakeDragMessage 1031 void 1032 SimpleListView::MakeDragMessage(BMessage* message) const 1033 { 1034 if (message) { 1035 message->AddPointer("list", 1036 (void*)dynamic_cast<const DragSortableListView*>(this)); 1037 int32 index; 1038 for (int32 i = 0; (index = CurrentSelection(i)) >= 0; i++) 1039 message->AddInt32("index", index); 1040 } 1041 } 1042 1043 // _MakeEmpty 1044 void 1045 SimpleListView::_MakeEmpty() 1046 { 1047 // NOTE: BListView::MakeEmpty() uses ScrollTo() 1048 // for which the object needs to be attached to 1049 // a BWindow.... :-( 1050 int32 count = CountItems(); 1051 for (int32 i = count - 1; i >= 0; i--) 1052 delete RemoveItem(i); 1053 } 1054 1055