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 DragSortableListView *list = NULL; 328 if (message->FindPointer("list", (void **)&list) == B_OK 329 && list == this) { 330 int32 count = CountItems(); 331 if (fDropIndex < 0 || fDropIndex > count) 332 fDropIndex = count; 333 BList items; 334 int32 index; 335 for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK; i++) 336 if (BListItem* item = ItemAt(index)) 337 items.AddItem((void*)item); 338 if (items.CountItems() > 0) { 339 if (modifiers() & B_SHIFT_KEY) 340 CopyItems(items, fDropIndex); 341 else 342 MoveItems(items, fDropIndex); 343 } 344 fDropIndex = -1; 345 } 346 } else { 347 switch (message->what) { 348 case MSG_TICK: { 349 float scrollV = 0.0; 350 BRect rect(Bounds()); 351 BPoint point; 352 uint32 buttons; 353 GetMouse(&point, &buttons, false); 354 if (rect.Contains(point)) { 355 // calculate the vertical scrolling offset 356 float hotDist = rect.Height() * SCROLL_AREA; 357 if (point.y > rect.bottom - hotDist) 358 scrollV = hotDist - (rect.bottom - point.y); 359 else if (point.y < rect.top + hotDist) 360 scrollV = (point.y - rect.top) - hotDist; 361 } 362 // scroll 363 if (scrollV != 0.0 && fScrollView) { 364 if (BScrollBar* scrollBar = fScrollView->ScrollBar(B_VERTICAL)) { 365 float value = scrollBar->Value(); 366 scrollBar->SetValue(scrollBar->Value() + scrollV); 367 if (scrollBar->Value() != value) { 368 // update mouse position 369 uint32 buttons; 370 BPoint point; 371 GetMouse(&point, &buttons, false); 372 uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; 373 MouseMoved(point, transit, &fDragMessageCopy); 374 } 375 } 376 } 377 break; 378 } 379 // case B_MODIFIERS_CHANGED: 380 // ModifiersChanged(); 381 // break; 382 case B_MOUSE_WHEEL_CHANGED: { 383 BListView::MessageReceived(message); 384 BPoint point; 385 uint32 buttons; 386 GetMouse(&point, &buttons, false); 387 uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; 388 MouseMoved(point, transit, &fDragMessageCopy); 389 break; 390 } 391 default: 392 BListView::MessageReceived(message); 393 break; 394 } 395 } 396 } 397 398 // KeyDown 399 void 400 DragSortableListView::KeyDown(const char* bytes, int32 numBytes) 401 { 402 if (numBytes < 1) 403 return; 404 405 if ((bytes[0] == B_BACKSPACE) || (bytes[0] == B_DELETE)) 406 RemoveSelected(); 407 408 BListView::KeyDown(bytes, numBytes); 409 } 410 411 // MouseDown 412 void 413 DragSortableListView::MouseDown(BPoint where) 414 { 415 int32 clicks = 1; 416 uint32 buttons = 0; 417 Window()->CurrentMessage()->FindInt32("clicks", &clicks); 418 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 419 int32 clickedIndex = -1; 420 for (int32 i = 0; BListItem* item = ItemAt(i); i++) { 421 if (ItemFrame(i).Contains(where)) { 422 if (clicks == 2) { 423 // only do something if user clicked the same item twice 424 if (fLastClickedItem == item) 425 DoubleClicked(i); 426 } else { 427 // remember last clicked item 428 fLastClickedItem = item; 429 } 430 clickedIndex = i; 431 break; 432 } 433 } 434 if (clickedIndex == -1) 435 fLastClickedItem = NULL; 436 437 BListItem* item = ItemAt(clickedIndex); 438 if (ListType() == B_MULTIPLE_SELECTION_LIST 439 && item && (buttons & B_SECONDARY_MOUSE_BUTTON)) { 440 if (item->IsSelected()) 441 Deselect(clickedIndex); 442 else 443 Select(clickedIndex, true); 444 } else { 445 BListView::MouseDown(where); 446 } 447 } 448 449 // MouseMoved 450 void 451 DragSortableListView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg) 452 { 453 if (msg && AcceptDragMessage(msg)) { 454 switch (transit) { 455 case B_ENTERED_VIEW: 456 case B_INSIDE_VIEW: { 457 // remember drag message 458 // this is needed to react on modifier changes 459 _SetDragMessage(msg); 460 // set drop target through virtual function 461 SetDropTargetRect(msg, where); 462 // go into autoscrolling mode 463 BRect r = Bounds(); 464 r.InsetBy(0.0, r.Height() * SCROLL_AREA); 465 SetAutoScrolling(!r.Contains(where)); 466 break; 467 } 468 case B_EXITED_VIEW: 469 // forget drag message 470 _SetDragMessage(NULL); 471 SetAutoScrolling(false); 472 // fall through 473 case B_OUTSIDE_VIEW: 474 _RemoveDropAnticipationRect(); 475 break; 476 } 477 } else { 478 _RemoveDropAnticipationRect(); 479 BListView::MouseMoved(where, transit, msg); 480 _SetDragMessage(NULL); 481 SetAutoScrolling(false); 482 483 BCursor cursor(B_HAND_CURSOR); 484 SetViewCursor(&cursor, true); 485 } 486 fLastMousePos = where; 487 } 488 489 // MouseUp 490 void 491 DragSortableListView::MouseUp(BPoint where) 492 { 493 // remove drop mark 494 _SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0)); 495 SetAutoScrolling(false); 496 // be sure to forget drag message 497 _SetDragMessage(NULL); 498 BListView::MouseUp(where); 499 500 BCursor cursor(B_HAND_CURSOR); 501 SetViewCursor(&cursor, true); 502 } 503 504 // DrawItem 505 void 506 DragSortableListView::DrawItem(BListItem *item, BRect itemFrame, bool complete) 507 { 508 DrawListItem(this, IndexOf(item), itemFrame); 509 /* if (IsFocus()) { 510 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 511 StrokeRect(Bounds()); 512 }*/ 513 } 514 515 // #pragma mark - 516 517 // MouseWheelChanged 518 bool 519 DragSortableListView::MouseWheelChanged(float x, float y) 520 { 521 BPoint where; 522 uint32 buttons; 523 GetMouse(&where, &buttons, false); 524 if (Bounds().Contains(where)) 525 return true; 526 else 527 return false; 528 } 529 530 // #pragma mark - 531 532 // ObjectChanged 533 void 534 DragSortableListView::ObjectChanged(const Observable* object) 535 { 536 if (object != fSelection || fModifyingSelection || fSyncingToSelection) 537 return; 538 539 //printf("%s - syncing start\n", Name()); 540 fSyncingToSelection = true; 541 542 // try to sync to Selection 543 BList selectedItems; 544 545 int32 count = fSelection->CountSelected(); 546 for (int32 i = 0; i < count; i++) { 547 int32 index = IndexOfSelectable(fSelection->SelectableAtFast(i)); 548 if (index >= 0) { 549 BListItem* item = ItemAt(index); 550 if (item && !selectedItems.HasItem((void*)item)) 551 selectedItems.AddItem((void*)item); 552 } 553 } 554 555 count = selectedItems.CountItems(); 556 if (count == 0) { 557 if (CurrentSelection(0) >= 0) 558 DeselectAll(); 559 } else { 560 count = CountItems(); 561 for (int32 i = 0; i < count; i++) { 562 BListItem* item = ItemAt(i); 563 bool selected = selectedItems.RemoveItem((void*)item); 564 if (item->IsSelected() != selected) { 565 Select(i, true); 566 } 567 } 568 } 569 570 fSyncingToSelection = false; 571 //printf("%s - done\n", Name()); 572 } 573 574 // #pragma mark - 575 576 // SetDragCommand 577 void 578 DragSortableListView::SetDragCommand(uint32 command) 579 { 580 fDragCommand = command; 581 } 582 583 // ModifiersChaned 584 void 585 DragSortableListView::ModifiersChanged() 586 { 587 SetDropTargetRect(&fDragMessageCopy, fLastMousePos); 588 } 589 590 // SetItemFocused 591 void 592 DragSortableListView::SetItemFocused(int32 index) 593 { 594 InvalidateItem(fFocusedIndex); 595 InvalidateItem(index); 596 fFocusedIndex = index; 597 } 598 599 // AcceptDragMessage 600 bool 601 DragSortableListView::AcceptDragMessage(const BMessage* message) const 602 { 603 return message->what == fDragCommand; 604 } 605 606 // SetDropTargetRect 607 void 608 DragSortableListView::SetDropTargetRect(const BMessage* message, BPoint where) 609 610 { 611 if (AcceptDragMessage(message)) { 612 bool copy = modifiers() & B_SHIFT_KEY; 613 bool replaceAll = !message->HasPointer("list") && !copy; 614 BRect r = Bounds(); 615 if (replaceAll) { 616 r.bottom--; // compensate for scrollbar offset 617 _SetDropAnticipationRect(r); 618 fDropIndex = -1; 619 } else { 620 // offset where by half of item height 621 r = ItemFrame(0); 622 where.y += r.Height() / 2.0; 623 624 int32 index = IndexOf(where); 625 if (index < 0) 626 index = CountItems(); 627 _SetDropIndex(index); 628 629 const uchar* cursorData = copy ? kCopyCursor : B_HAND_CURSOR; 630 BCursor cursor(cursorData); 631 SetViewCursor(&cursor, true); 632 } 633 } 634 } 635 636 // SetAutoScrolling 637 void 638 DragSortableListView::SetAutoScrolling(bool enable) 639 { 640 if (fScrollPulse && enable) 641 return; 642 if (enable) { 643 BMessenger messenger(this, Window()); 644 BMessage message(MSG_TICK); 645 fScrollPulse = new BMessageRunner(messenger, &message, 40000LL); 646 } else { 647 delete fScrollPulse; 648 fScrollPulse = NULL; 649 } 650 } 651 652 // DoesAutoScrolling 653 bool 654 DragSortableListView::DoesAutoScrolling() const 655 { 656 return fScrollPulse; 657 } 658 659 // ScrollTo 660 void 661 DragSortableListView::ScrollTo(int32 index) 662 { 663 if (index < 0) 664 index = 0; 665 if (index >= CountItems()) 666 index = CountItems() - 1; 667 668 if (ItemAt(index)) { 669 BRect itemFrame = ItemFrame(index); 670 BRect bounds = Bounds(); 671 if (itemFrame.top < bounds.top) { 672 ScrollTo(itemFrame.LeftTop()); 673 } else if (itemFrame.bottom > bounds.bottom) { 674 ScrollTo(BPoint(0.0, itemFrame.bottom - bounds.Height())); 675 } 676 } 677 } 678 679 // MoveItems 680 void 681 DragSortableListView::MoveItems(BList& items, int32 index) 682 { 683 DeselectAll(); 684 // we remove the items while we look at them, the insertion index is decreased 685 // when the items index is lower, so that we insert at the right spot after 686 // removal 687 BList removedItems; 688 int32 count = items.CountItems(); 689 for (int32 i = 0; i < count; i++) { 690 BListItem* item = (BListItem*)items.ItemAt(i); 691 int32 removeIndex = IndexOf(item); 692 if (RemoveItem(item) && removedItems.AddItem((void*)item)) { 693 if (removeIndex < index) 694 index--; 695 } 696 // else ??? -> blow up 697 } 698 for (int32 i = 0; 699 BListItem* item = (BListItem*)removedItems.ItemAt(i); i++) { 700 if (AddItem(item, index)) { 701 // after we're done, the newly inserted items will be selected 702 Select(index, true); 703 // next items will be inserted after this one 704 index++; 705 } else 706 delete item; 707 } 708 } 709 710 // CopyItems 711 void 712 DragSortableListView::CopyItems(BList& items, int32 index) 713 { 714 DeselectAll(); 715 // by inserting the items after we copied all items first, we avoid 716 // cloning an item we already inserted and messing everything up 717 // in other words, don't touch the list before we know which items 718 // need to be cloned 719 BList clonedItems; 720 int32 count = items.CountItems(); 721 for (int32 i = 0; i < count; i++) { 722 BListItem* item = CloneItem(IndexOf((BListItem*)items.ItemAt(i))); 723 if (item && !clonedItems.AddItem((void*)item)) 724 delete item; 725 } 726 for (int32 i = 0; 727 BListItem* item = (BListItem*)clonedItems.ItemAt(i); i++) { 728 if (AddItem(item, index)) { 729 // after we're done, the newly inserted items will be selected 730 Select(index, true); 731 // next items will be inserted after this one 732 index++; 733 } else 734 delete item; 735 } 736 } 737 738 // RemoveItemList 739 void 740 DragSortableListView::RemoveItemList(BList& items) 741 { 742 int32 count = items.CountItems(); 743 for (int32 i = 0; i < count; i++) { 744 BListItem* item = (BListItem*)items.ItemAt(i); 745 if (RemoveItem(item)) 746 delete item; 747 } 748 } 749 750 // RemoveSelected 751 void 752 DragSortableListView::RemoveSelected() 753 { 754 // if (fFocusedIndex >= 0) 755 // return; 756 757 BList items; 758 for (int32 i = 0; BListItem* item = ItemAt(CurrentSelection(i)); i++) 759 items.AddItem((void*)item); 760 RemoveItemList(items); 761 } 762 763 // #pragma mark - 764 765 // SetSelection 766 void 767 DragSortableListView::SetSelection(Selection* selection) 768 { 769 if (fSelection == selection) 770 return; 771 772 if (fSelection) 773 fSelection->RemoveObserver(this); 774 775 fSelection = selection; 776 777 if (fSelection) 778 fSelection->AddObserver(this); 779 } 780 781 // IndexOfSelectable 782 int32 783 DragSortableListView::IndexOfSelectable(Selectable* selectable) const 784 { 785 return -1; 786 } 787 788 // SelectableFor 789 Selectable* 790 DragSortableListView::SelectableFor(BListItem* item) const 791 { 792 return NULL; 793 } 794 795 // SelectAll 796 void 797 DragSortableListView::SelectAll() 798 { 799 Select(0, CountItems() - 1); 800 } 801 802 // CountSelectedItems 803 int32 804 DragSortableListView::CountSelectedItems() const 805 { 806 int32 count = 0; 807 while (CurrentSelection(count) >= 0) 808 count++; 809 return count; 810 } 811 812 // SelectionChanged 813 void 814 DragSortableListView::SelectionChanged() 815 { 816 //printf("%s::SelectionChanged()", typeid(*this).name()); 817 // modify global Selection 818 if (!fSelection || fSyncingToSelection) 819 return; 820 821 fModifyingSelection = true; 822 823 BList selectables; 824 for (int32 i = 0; BListItem* item = ItemAt(CurrentSelection(i)); i++) { 825 Selectable* selectable = SelectableFor(item); 826 if (selectable) 827 selectables.AddItem((void*)selectable); 828 } 829 830 AutoNotificationSuspender _(fSelection); 831 832 int32 count = selectables.CountItems(); 833 if (count == 0) { 834 //printf(" deselecting all\n"); 835 if (!fSyncingToSelection) 836 fSelection->DeselectAll(); 837 } else { 838 //printf(" selecting %ld items\n", count); 839 for (int32 i = 0; i < count; i++) { 840 Selectable* selectable = (Selectable*)selectables.ItemAtFast(i); 841 fSelection->Select(selectable, i > 0); 842 } 843 } 844 845 fModifyingSelection = false; 846 } 847 848 // #pragma mark - 849 850 // DeleteItem 851 bool 852 DragSortableListView::DeleteItem(int32 index) 853 { 854 BListItem* item = ItemAt(index); 855 if (item && RemoveItem(item)) { 856 delete item; 857 return true; 858 } 859 return false; 860 } 861 862 // _SetDropAnticipationRect 863 void 864 DragSortableListView::_SetDropAnticipationRect(BRect r) 865 { 866 if (fDropRect != r) { 867 if (fDropRect.IsValid()) 868 Invalidate(fDropRect); 869 fDropRect = r; 870 if (fDropRect.IsValid()) 871 Invalidate(fDropRect); 872 } 873 } 874 875 // _SetDropIndex 876 void 877 DragSortableListView::_SetDropIndex(int32 index) 878 { 879 if (fDropIndex != index) { 880 fDropIndex = index; 881 if (fDropIndex >= 0) { 882 int32 count = CountItems(); 883 if (fDropIndex == count) { 884 BRect r; 885 if (ItemAt(count - 1)) { 886 r = ItemFrame(count - 1); 887 r.top = r.bottom; 888 r.bottom = r.top + 1.0; 889 } else { 890 r = Bounds(); 891 r.bottom--; // compensate for scrollbars moved slightly out of window 892 } 893 _SetDropAnticipationRect(r); 894 } else { 895 BRect r = ItemFrame(fDropIndex); 896 r.top--; 897 r.bottom = r.top + 1.0; 898 _SetDropAnticipationRect(r); 899 } 900 } 901 } 902 } 903 904 // _RemoveDropAnticipationRect 905 void 906 DragSortableListView::_RemoveDropAnticipationRect() 907 { 908 _SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0)); 909 // _SetDropIndex(-1); 910 } 911 912 // _SetDragMessage 913 void 914 DragSortableListView::_SetDragMessage(const BMessage* message) 915 { 916 if (message) 917 fDragMessageCopy = *message; 918 else 919 fDragMessageCopy.what = 0; 920 } 921 922 // #pragma mark - SimpleListView 923 924 // SimpleListView class 925 SimpleListView::SimpleListView(BRect frame, BMessage* selectionChangeMessage) 926 : DragSortableListView(frame, "playlist listview", 927 B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL, 928 B_WILL_DRAW | B_NAVIGABLE 929 | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE), 930 fSelectionChangeMessage(selectionChangeMessage) 931 { 932 } 933 934 // SimpleListView class 935 SimpleListView::SimpleListView(BRect frame, const char* name, 936 BMessage* selectionChangeMessage, 937 list_view_type type, 938 uint32 resizingMode, uint32 flags) 939 : DragSortableListView(frame, name, type, resizingMode, flags), 940 fSelectionChangeMessage(selectionChangeMessage) 941 { 942 } 943 944 // destructor 945 SimpleListView::~SimpleListView() 946 { 947 delete fSelectionChangeMessage; 948 } 949 950 #ifdef LIB_LAYOUT 951 // layoutprefs 952 minimax 953 SimpleListView::layoutprefs() 954 { 955 mpm.mini.x = 30.0; 956 mpm.maxi.x = 10000.0; 957 mpm.mini.y = 50.0; 958 mpm.maxi.y = 10000.0; 959 mpm.weight = 1.0; 960 return mpm; 961 } 962 963 // layout 964 BRect 965 SimpleListView::layout(BRect frame) 966 { 967 MoveTo(frame.LeftTop()); 968 ResizeTo(frame.Width(), frame.Height()); 969 return Frame(); 970 } 971 #endif // LIB_LAYOUT 972 973 // DetachedFromWindow 974 void 975 SimpleListView::DetachedFromWindow() 976 { 977 DragSortableListView::DetachedFromWindow(); 978 _MakeEmpty(); 979 } 980 981 // MessageReceived 982 void 983 SimpleListView::MessageReceived(BMessage* message) 984 { 985 switch (message->what) { 986 default: 987 DragSortableListView::MessageReceived(message); 988 break; 989 } 990 } 991 992 // CloneItem 993 BListItem* 994 SimpleListView::CloneItem(int32 atIndex) const 995 { 996 BListItem* clone = NULL; 997 if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(atIndex))) 998 clone = new SimpleItem(item->Text()); 999 return clone; 1000 } 1001 1002 // DrawListItem 1003 void 1004 SimpleListView::DrawListItem(BView* owner, int32 index, BRect frame) const 1005 { 1006 if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(index))) { 1007 uint32 flags = FLAGS_NONE; 1008 if (index == fFocusedIndex) 1009 flags |= FLAGS_FOCUSED; 1010 if (index % 2) 1011 flags |= FLAGS_TINTED_LINE; 1012 item->Draw(owner, frame, flags); 1013 } 1014 } 1015 1016 // MakeDragMessage 1017 void 1018 SimpleListView::MakeDragMessage(BMessage* message) const 1019 { 1020 if (message) { 1021 message->AddPointer("list", 1022 (void*)dynamic_cast<const DragSortableListView*>(this)); 1023 int32 index; 1024 for (int32 i = 0; (index = CurrentSelection(i)) >= 0; i++) 1025 message->AddInt32("index", index); 1026 } 1027 } 1028 1029 // _MakeEmpty 1030 void 1031 SimpleListView::_MakeEmpty() 1032 { 1033 // NOTE: BListView::MakeEmpty() uses ScrollTo() 1034 // for which the object needs to be attached to 1035 // a BWindow.... :-( 1036 int32 count = CountItems(); 1037 for (int32 i = count - 1; i >= 0; i--) 1038 delete RemoveItem(i); 1039 } 1040 1041