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