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