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 37static const rgb_color kDropIndicatorColor = make_color(255, 65, 54, 255); 38static const rgb_color kDragFrameColor = make_color(17, 17, 17, 255); 39 40 41// #pragma mark - SimpleItem 42 43 44SimpleItem::SimpleItem(const char *name) 45 : 46 BStringItem(name) 47{ 48} 49 50 51SimpleItem::~SimpleItem() 52{ 53} 54 55 56void 57SimpleItem::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 86void 87SimpleItem::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 114DragSortableListView::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 134DragSortableListView::~DragSortableListView() 135{ 136 delete fMouseWheelFilter; 137 138 SetSelection(NULL); 139} 140 141 142void 143DragSortableListView::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 157void 158DragSortableListView::DetachedFromWindow() 159{ 160 Window()->RemoveCommonFilter(fMouseWheelFilter); 161} 162 163 164void 165DragSortableListView::FrameResized(float width, float height) 166{ 167 BListView::FrameResized(width, height); 168} 169 170 171void 172DragSortableListView::TargetedByScrollView(BScrollView* scrollView) 173{ 174 fScrollView = scrollView; 175 BListView::TargetedByScrollView(scrollView); 176} 177 178 179void 180DragSortableListView::WindowActivated(bool active) 181{ 182 // work-around for buggy focus indicator on BScrollView 183 if (BView* view = Parent()) 184 view->Invalidate(); 185} 186 187 188void 189DragSortableListView::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 218void 219DragSortableListView::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 231void 232DragSortableListView::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 267void 268DragSortableListView::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 309void 310DragSortableListView::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 324bool 325DragSortableListView::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 340void 341DragSortableListView::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 385void 386DragSortableListView::SetDragCommand(uint32 command) 387{ 388 fDragCommand = command; 389} 390 391 392void 393DragSortableListView::ModifiersChanged() 394{ 395 SetDropTargetRect(&fDragMessageCopy, fLastMousePos); 396} 397 398 399void 400DragSortableListView::SetItemFocused(int32 index) 401{ 402 InvalidateItem(fFocusedIndex); 403 InvalidateItem(index); 404 fFocusedIndex = index; 405} 406 407 408bool 409DragSortableListView::AcceptDragMessage(const BMessage* message) const 410{ 411 return message->what == fDragCommand; 412} 413 414 415void 416DragSortableListView::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 445bool 446DragSortableListView::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 473bool 474DragSortableListView::DoesAutoScrolling() const 475{ 476 return true; 477} 478 479 480void 481DragSortableListView::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 511void 512DragSortableListView::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 539void 540DragSortableListView::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 551void 552DragSortableListView::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 567void 568DragSortableListView::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 583int32 584DragSortableListView::IndexOfSelectable(Selectable* selectable) const 585{ 586 return -1; 587} 588 589 590Selectable* 591DragSortableListView::SelectableFor(BListItem* item) const 592{ 593 return NULL; 594} 595 596 597void 598DragSortableListView::SelectAll() 599{ 600 Select(0, CountItems() - 1); 601} 602 603 604int32 605DragSortableListView::CountSelectedItems() const 606{ 607 int32 count = 0; 608 while (CurrentSelection(count) >= 0) 609 count++; 610 return count; 611} 612 613 614void 615DragSortableListView::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 653bool 654DragSortableListView::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 665void 666DragSortableListView::SetDropRect(BRect rect) 667{ 668 fDropRect = rect; 669} 670 671 672void 673DragSortableListView::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 702void 703DragSortableListView::InvalidateDropRect() 704{ 705 fDropRect = BRect(0, 0, -1, -1); 706// SetDropIndex(-1); 707} 708 709 710void 711DragSortableListView::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 723SimpleListView::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 733SimpleListView::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 743SimpleListView::~SimpleListView() 744{ 745 delete fSelectionChangeMessage; 746} 747 748 749void 750SimpleListView::DetachedFromWindow() 751{ 752 DragSortableListView::DetachedFromWindow(); 753 _MakeEmpty(); 754} 755 756 757void 758SimpleListView::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 801bool 802SimpleListView::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 903void 904SimpleListView::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 945BListItem* 946SimpleListView::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 955void 956SimpleListView::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 972bool 973SimpleListView::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 987bool 988SimpleListView::HandlePaste(const BMessage* archive) 989{ 990 return InstantiateSelection(archive, CountItems()); 991} 992 993 994void 995SimpleListView::_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