1 /* 2 * Copyright 2006-2007, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 #include <stdio.h> 9 #include <malloc.h> 10 #include <new> 11 12 #include <Bitmap.h> 13 #include <Cursor.h> 14 #include <Entry.h> 15 #include <MessageRunner.h> 16 #include <Messenger.h> 17 #include <ScrollBar.h> 18 #include <ScrollView.h> 19 #include <String.h> 20 #include <Window.h> 21 22 #include "ListViews.h" 23 24 const unsigned char kCopyCursor[] = { 16, 1, 1, 1, 25 0x00, 0x00, 0x70, 0x00, 0x48, 0x00, 0x48, 0x00, 26 0x27, 0xc0, 0x24, 0xb8, 0x12, 0x54, 0x10, 0x02, 27 0x79, 0xe2, 0x99, 0x22, 0x85, 0x7a, 0x61, 0x4a, 28 0x19, 0xca, 0x04, 0x4a, 0x02, 0x78, 0x00, 0x00, 29 30 0x00, 0x00, 0x70, 0x00, 0x78, 0x00, 0x78, 0x00, 31 0x3f, 0xc0, 0x3f, 0xf8, 0x1f, 0xfc, 0x1f, 0xfe, 32 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfe, 33 0x1f, 0xfe, 0x07, 0xfe, 0x03, 0xf8, 0x00, 0x00 }; 34 35 #define MAX_DRAG_HEIGHT 200.0 36 #define ALPHA 170 37 #define TEXT_OFFSET 5.0 38 39 enum { 40 MSG_TICK = 'tick', 41 }; 42 43 using std::nothrow; 44 45 // SimpleItem class 46 SimpleItem::SimpleItem( const char *name ) 47 : BStringItem( name ) 48 { 49 } 50 51 SimpleItem::~SimpleItem() 52 { 53 } 54 55 // SimpleItem::DrawItem 56 void 57 SimpleItem::Draw(BView *owner, BRect frame, uint32 flags) 58 { 59 DrawBackground(owner, frame, flags); 60 // label 61 if (IsSelected()) 62 owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR)); 63 else 64 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR)); 65 font_height fh; 66 owner->GetFontHeight( &fh ); 67 const char* text = Text(); 68 BString truncatedString( text ); 69 owner->TruncateString( &truncatedString, B_TRUNCATE_MIDDLE, 70 frame.Width() - TEXT_OFFSET - 4.0 ); 71 float height = frame.Height(); 72 float textHeight = fh.ascent + fh.descent; 73 BPoint textPoint; 74 textPoint.x = frame.left + TEXT_OFFSET; 75 textPoint.y = frame.top 76 + ceilf(height / 2.0 - textHeight / 2.0 77 + fh.ascent); 78 owner->DrawString(truncatedString.String(), textPoint); 79 } 80 81 // SimpleItem::DrawBackground 82 void 83 SimpleItem::DrawBackground(BView *owner, BRect frame, uint32 flags) 84 { 85 // stroke a blue frame around the item if it's focused 86 if (flags & FLAGS_FOCUSED) { 87 owner->SetLowColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 88 owner->StrokeRect(frame, B_SOLID_LOW); 89 frame.InsetBy(1.0, 1.0); 90 } 91 // figure out bg-color 92 rgb_color color = ui_color(B_LIST_BACKGROUND_COLOR); 93 94 if (IsSelected()) 95 color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR); 96 97 if ( flags & FLAGS_TINTED_LINE ) 98 color = tint_color(color, 1.06); 99 // background 100 owner->SetLowColor(color); 101 owner->FillRect(frame, B_SOLID_LOW); 102 } 103 104 // DragSortableListView class 105 DragSortableListView::DragSortableListView(BRect frame, const char* name, 106 list_view_type type, uint32 resizingMode, 107 uint32 flags) 108 : BListView(frame, name, type, resizingMode, flags), 109 fDropRect(0.0, 0.0, -1.0, -1.0), 110 fScrollPulse(NULL), 111 fDropIndex(-1), 112 fLastClickedItem(NULL), 113 fScrollView(NULL), 114 fDragCommand(B_SIMPLE_DATA), 115 fFocusedIndex(-1) 116 { 117 SetViewColor(B_TRANSPARENT_32_BIT); 118 } 119 120 DragSortableListView::~DragSortableListView() 121 { 122 delete fScrollPulse; 123 } 124 125 // AttachedToWindow 126 void 127 DragSortableListView::AttachedToWindow() 128 { 129 BListView::AttachedToWindow(); 130 131 // work arround a bug in BListView 132 BRect bounds = Bounds(); 133 BListView::FrameResized(bounds.Width(), bounds.Height()); 134 } 135 136 // DetachedFromWindow 137 void 138 DragSortableListView::DetachedFromWindow() 139 { 140 } 141 142 // FrameResized 143 void 144 DragSortableListView::FrameResized(float width, float height) 145 { 146 BListView::FrameResized(width, height); 147 Invalidate(); 148 } 149 150 /* 151 // MakeFocus 152 void 153 DragSortableListView::MakeFocus(bool focused) 154 { 155 if (focused != IsFocus()) { 156 Invalidate(); 157 BListView::MakeFocus(focused); 158 } 159 } 160 */ 161 // Draw 162 void 163 DragSortableListView::Draw( BRect updateRect ) 164 { 165 int32 firstIndex = IndexOf(updateRect.LeftTop()); 166 int32 lastIndex = IndexOf(updateRect.RightBottom()); 167 if (firstIndex >= 0) { 168 if (lastIndex < firstIndex) 169 lastIndex = CountItems() - 1; 170 // update rect contains items 171 BRect r = updateRect; 172 for (int32 i = firstIndex; i <= lastIndex; i++) { 173 r = ItemFrame(i); 174 DrawListItem(this, i, r); 175 } 176 updateRect.top = r.bottom + 1.0; 177 if (updateRect.IsValid()) { 178 SetLowColor(ui_color(B_LIST_BACKGROUND_COLOR)); 179 FillRect(updateRect, B_SOLID_LOW); 180 } 181 } else { 182 SetLowColor(ui_color(B_LIST_BACKGROUND_COLOR)); 183 FillRect(updateRect, B_SOLID_LOW); 184 } 185 // drop anticipation indication 186 if (fDropRect.IsValid()) { 187 SetHighColor(255, 0, 0, 255); 188 StrokeRect(fDropRect); 189 } 190 /* // focus indication 191 if (IsFocus()) { 192 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 193 StrokeRect(Bounds()); 194 }*/ 195 } 196 197 // ScrollTo 198 void 199 DragSortableListView::ScrollTo(BPoint where) 200 { 201 uint32 buttons; 202 BPoint point; 203 GetMouse(&point, &buttons, false); 204 uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; 205 MouseMoved(point, transit, &fDragMessageCopy); 206 BListView::ScrollTo(where); 207 } 208 209 // TargetedByScrollView 210 void 211 DragSortableListView::TargetedByScrollView(BScrollView* scrollView) 212 { 213 fScrollView = scrollView; 214 BListView::TargetedByScrollView(scrollView); 215 } 216 217 // InitiateDrag 218 bool 219 DragSortableListView::InitiateDrag( BPoint point, int32 index, bool ) 220 { 221 // supress drag&drop while an item is focused 222 if (fFocusedIndex >= 0) 223 return false; 224 225 bool success = false; 226 BListItem* item = ItemAt( CurrentSelection( 0 ) ); 227 if ( !item ) { 228 // workarround a timing problem 229 Select( index ); 230 item = ItemAt( index ); 231 } 232 if ( item ) { 233 // create drag message 234 BMessage msg( fDragCommand ); 235 MakeDragMessage( &msg ); 236 // figure out drag rect 237 float width = Bounds().Width(); 238 BRect dragRect(0.0, 0.0, width, -1.0); 239 // figure out, how many items fit into our bitmap 240 int32 numItems; 241 bool fade = false; 242 for (numItems = 0; BListItem* item = ItemAt( CurrentSelection( numItems ) ); numItems++) { 243 dragRect.bottom += ceilf( item->Height() ) + 1.0; 244 if ( dragRect.Height() > MAX_DRAG_HEIGHT ) { 245 fade = true; 246 dragRect.bottom = MAX_DRAG_HEIGHT; 247 numItems++; 248 break; 249 } 250 } 251 BBitmap* dragBitmap = new BBitmap( dragRect, B_RGB32, true ); 252 if ( dragBitmap && dragBitmap->IsValid() ) { 253 if ( BView *v = new BView( dragBitmap->Bounds(), "helper", 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 (AcceptDragMessage(message)) { 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 indices; 334 int32 index; 335 for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK; i++) 336 indices.AddItem((void*)(addr_t)index); 337 if (indices.CountItems() > 0) { 338 if (modifiers() & B_SHIFT_KEY) 339 CopyItems(indices, fDropIndex); 340 else 341 MoveItems(indices, fDropIndex); 342 } 343 fDropIndex = -1; 344 } 345 } else { 346 switch (message->what) { 347 case MSG_TICK: { 348 float scrollV = 0.0; 349 BRect rect(Bounds()); 350 BPoint point; 351 uint32 buttons; 352 GetMouse(&point, &buttons, false); 353 if (rect.Contains(point)) { 354 // calculate the vertical scrolling offset 355 float hotDist = rect.Height() * SCROLL_AREA; 356 if (point.y > rect.bottom - hotDist) 357 scrollV = hotDist - (rect.bottom - point.y); 358 else if (point.y < rect.top + hotDist) 359 scrollV = (point.y - rect.top) - hotDist; 360 } 361 // scroll 362 if (scrollV != 0.0 && fScrollView) { 363 if (BScrollBar* scrollBar = fScrollView->ScrollBar(B_VERTICAL)) { 364 float value = scrollBar->Value(); 365 scrollBar->SetValue(scrollBar->Value() + scrollV); 366 if (scrollBar->Value() != value) { 367 // update mouse position 368 uint32 buttons; 369 BPoint point; 370 GetMouse(&point, &buttons, false); 371 uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; 372 MouseMoved(point, transit, &fDragMessageCopy); 373 } 374 } 375 } 376 break; 377 } 378 case B_MODIFIERS_CHANGED: 379 ModifiersChanged(); 380 break; 381 case B_MOUSE_WHEEL_CHANGED: { 382 BListView::MessageReceived( message ); 383 BPoint point; 384 uint32 buttons; 385 GetMouse(&point, &buttons, false); 386 uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; 387 MouseMoved(point, transit, &fDragMessageCopy); 388 break; 389 } 390 default: 391 BListView::MessageReceived( message ); 392 break; 393 } 394 } 395 } 396 397 // KeyDown 398 void 399 DragSortableListView::KeyDown( const char* bytes, int32 numBytes ) 400 { 401 if ( numBytes < 1 ) 402 return; 403 404 if ( ( bytes[0] == B_BACKSPACE ) || ( bytes[0] == B_DELETE ) ) 405 RemoveSelected(); 406 407 BListView::KeyDown( bytes, numBytes ); 408 } 409 410 // MouseDown 411 void 412 DragSortableListView::MouseDown( BPoint where ) 413 { 414 int32 clicks = 1; 415 uint32 buttons = 0; 416 Window()->CurrentMessage()->FindInt32("clicks", &clicks); 417 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 418 int32 clickedIndex = -1; 419 for (int32 i = 0; BListItem* item = ItemAt(i); i++) { 420 if (ItemFrame(i).Contains(where)) { 421 if (clicks == 2) { 422 // only do something if user clicked the same item twice 423 if (fLastClickedItem == item) 424 DoubleClicked(i); 425 } else { 426 // remember last clicked item 427 fLastClickedItem = item; 428 } 429 clickedIndex = i; 430 break; 431 } 432 } 433 if (clickedIndex == -1) 434 fLastClickedItem = NULL; 435 436 BListItem* item = ItemAt(clickedIndex); 437 if (ListType() == B_MULTIPLE_SELECTION_LIST 438 && item && (buttons & B_SECONDARY_MOUSE_BUTTON)) { 439 if (item->IsSelected()) 440 Deselect(clickedIndex); 441 else 442 Select(clickedIndex, true); 443 } else { 444 BListView::MouseDown(where); 445 } 446 } 447 448 // MouseMoved 449 void 450 DragSortableListView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg) 451 { 452 if (msg && AcceptDragMessage(msg)) { 453 switch (transit) { 454 case B_ENTERED_VIEW: 455 case B_INSIDE_VIEW: { 456 // remember drag message 457 // this is needed to react on modifier changes 458 _SetDragMessage(msg); 459 // set drop target through virtual function 460 SetDropTargetRect(msg, where); 461 // go into autoscrolling mode 462 BRect r = Bounds(); 463 r.InsetBy(0.0, r.Height() * SCROLL_AREA); 464 SetAutoScrolling(!r.Contains(where)); 465 break; 466 } 467 case B_EXITED_VIEW: 468 // forget drag message 469 _SetDragMessage(NULL); 470 SetAutoScrolling(false); 471 // fall through 472 case B_OUTSIDE_VIEW: 473 _RemoveDropAnticipationRect(); 474 break; 475 } 476 } else { 477 _RemoveDropAnticipationRect(); 478 BListView::MouseMoved(where, transit, msg); 479 _SetDragMessage(NULL); 480 SetAutoScrolling(false); 481 482 BCursor cursor(B_HAND_CURSOR); 483 SetViewCursor(&cursor, true); 484 } 485 fLastMousePos = where; 486 } 487 488 // MouseUp 489 void 490 DragSortableListView::MouseUp( BPoint where ) 491 { 492 // remove drop mark 493 _SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) ); 494 SetAutoScrolling(false); 495 // be sure to forget drag message 496 _SetDragMessage(NULL); 497 BListView::MouseUp( where ); 498 499 BCursor cursor(B_HAND_CURSOR); 500 SetViewCursor(&cursor, true); 501 } 502 503 // DrawItem 504 void 505 DragSortableListView::DrawItem( BListItem *item, BRect itemFrame, bool complete ) 506 { 507 DrawListItem( this, IndexOf( item ), itemFrame ); 508 /* if (IsFocus()) { 509 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 510 StrokeRect(Bounds()); 511 }*/ 512 } 513 514 // MouseWheelChanged 515 bool 516 DragSortableListView::MouseWheelChanged(float x, float y) 517 { 518 BPoint where; 519 uint32 buttons; 520 GetMouse(&where, &buttons, false); 521 if (Bounds().Contains(where)) 522 return true; 523 else 524 return false; 525 } 526 527 // SetDragCommand 528 void 529 DragSortableListView::SetDragCommand(uint32 command) 530 { 531 fDragCommand = command; 532 } 533 534 // ModifiersChaned 535 void 536 DragSortableListView::ModifiersChanged() 537 { 538 SetDropTargetRect(&fDragMessageCopy, fLastMousePos); 539 } 540 541 // SetItemFocused 542 void 543 DragSortableListView::SetItemFocused(int32 index) 544 { 545 InvalidateItem(fFocusedIndex); 546 InvalidateItem(index); 547 fFocusedIndex = index; 548 } 549 550 // AcceptDragMessage 551 bool 552 DragSortableListView::AcceptDragMessage(const BMessage* message) const 553 { 554 return message->what == fDragCommand; 555 } 556 557 // SetDropTargetRect 558 void 559 DragSortableListView::SetDropTargetRect(const BMessage* message, BPoint where) 560 561 { 562 if (AcceptDragMessage(message)) { 563 bool copy = modifiers() & B_SHIFT_KEY; 564 bool replaceAll = !message->HasPointer("list") && copy; 565 // when dragging something in from ie Tracker, the 566 // user has to hold shift down to replace everything 567 // (opposite meaning of the normal shift behaviour) 568 BRect r = Bounds(); 569 if (replaceAll) { 570 r.bottom--; // compensate for scrollbar offset 571 _SetDropAnticipationRect(r); 572 fDropIndex = -1; 573 } else { 574 // offset where by half of item height 575 r = ItemFrame(0); 576 where.y += r.Height() / 2.0; 577 578 int32 index = IndexOf(where); 579 if (index < 0) 580 index = CountItems(); 581 _SetDropIndex(index); 582 583 const uchar* cursorData = copy ? kCopyCursor : B_HAND_CURSOR; 584 BCursor cursor(cursorData); 585 SetViewCursor(&cursor, true); 586 } 587 } 588 } 589 590 // SetAutoScrolling 591 void 592 DragSortableListView::SetAutoScrolling(bool enable) 593 { 594 if (fScrollPulse && enable) 595 return; 596 if (enable) { 597 BMessenger messenger(this, Window()); 598 BMessage message(MSG_TICK); 599 fScrollPulse = new BMessageRunner(messenger, &message, 40000LL); 600 } else { 601 delete fScrollPulse; 602 fScrollPulse = NULL; 603 } 604 } 605 606 // DoesAutoScrolling 607 bool 608 DragSortableListView::DoesAutoScrolling() const 609 { 610 return fScrollPulse; 611 } 612 613 // ScrollTo 614 void 615 DragSortableListView::ScrollTo(int32 index) 616 { 617 if (index < 0) 618 index = 0; 619 if (index >= CountItems()) 620 index = CountItems() - 1; 621 622 if (ItemAt(index)) { 623 BRect itemFrame = ItemFrame(index); 624 BRect bounds = Bounds(); 625 if (itemFrame.top < bounds.top) { 626 ScrollTo(itemFrame.LeftTop()); 627 } else if (itemFrame.bottom > bounds.bottom) { 628 ScrollTo(BPoint(0.0, itemFrame.bottom - bounds.Height())); 629 } 630 } 631 } 632 633 // MoveItems 634 void 635 DragSortableListView::MoveItems(const BList& indices, int32 index) 636 { 637 DeselectAll(); 638 // we remove the items while we look at them, the insertion index is decreased 639 // when the items index is lower, so that we insert at the right spot after 640 // removal 641 BList removedItems; 642 int32 count = indices.CountItems(); 643 for (int32 i = 0; i < count; i++) { 644 int32 removeIndex = (int32)(addr_t)indices.ItemAtFast(i) - i; 645 BListItem* item = RemoveItem(removeIndex); 646 if (item && removedItems.AddItem((void*)item)) { 647 if (removeIndex < index) 648 index--; 649 } 650 // else ??? -> blow up 651 } 652 count = removedItems.CountItems(); 653 for (int32 i = 0; i < count; i++) { 654 BListItem* item = (BListItem*)removedItems.ItemAtFast(i); 655 if (AddItem(item, index)) { 656 // after we're done, the newly inserted items will be selected 657 Select(index, true); 658 // next items will be inserted after this one 659 index++; 660 } else 661 delete item; 662 } 663 } 664 665 // CopyItems 666 void 667 DragSortableListView::CopyItems(const BList& indices, int32 toIndex) 668 { 669 DeselectAll(); 670 // by inserting the items after we copied all items first, we avoid 671 // cloning an item we already inserted and messing everything up 672 // in other words, don't touch the list before we know which items 673 // need to be cloned 674 BList clonedItems; 675 int32 count = indices.CountItems(); 676 for (int32 i = 0; i < count; i++) { 677 int32 index = (int32)(addr_t)indices.ItemAtFast(i); 678 BListItem* item = CloneItem(index); 679 if (item && !clonedItems.AddItem((void*)(addr_t)item)) 680 delete item; 681 } 682 count = clonedItems.CountItems(); 683 for (int32 i = 0; i < count; i++) { 684 BListItem* item = (BListItem*)clonedItems.ItemAtFast(i); 685 if (AddItem(item, toIndex)) { 686 // after we're done, the newly inserted items will be selected 687 Select(toIndex, true); 688 // next items will be inserted after this one 689 toIndex++; 690 } else 691 delete item; 692 } 693 } 694 695 // RemoveItemList 696 void 697 DragSortableListView::RemoveItemList(const BList& indices) 698 { 699 int32 count = indices.CountItems(); 700 for (int32 i = 0; i < count; i++) { 701 int32 index = (int32)(addr_t)indices.ItemAtFast(i) - i; 702 delete RemoveItem(index); 703 } 704 } 705 706 // GetSelectedItems 707 void 708 DragSortableListView::GetSelectedItems(BList& indices) 709 { 710 for (int32 i = 0; true; i++) { 711 int32 index = CurrentSelection(i); 712 if (index < 0) 713 break; 714 if (!indices.AddItem((void*)(addr_t)index)) 715 break; 716 } 717 } 718 719 // RemoveSelected 720 void 721 DragSortableListView::RemoveSelected() 722 { 723 BList indices; 724 GetSelectedItems(indices); 725 int32 index = CurrentSelection() - 1; 726 727 DeselectAll(); 728 729 if (indices.CountItems() > 0) 730 RemoveItemList(indices); 731 732 if (CountItems() > 0) { 733 if (index < 0) 734 index = 0; 735 736 Select(index); 737 } 738 } 739 740 // RemoveAll 741 void 742 DragSortableListView::RemoveAll() 743 { 744 BList indices; 745 int32 count = CountItems(); 746 for (int32 i = 0; i < count; i++) { 747 if (!indices.AddItem((void*)(addr_t)i)) 748 break; 749 } 750 751 if (indices.CountItems() > 0) 752 RemoveItemList(indices); 753 } 754 755 // CountSelectedItems 756 int32 757 DragSortableListView::CountSelectedItems() const 758 { 759 int32 count = 0; 760 while (CurrentSelection(count) >= 0) 761 count++; 762 return count; 763 } 764 765 // SelectAll 766 void 767 DragSortableListView::SelectAll() 768 { 769 Select(0, CountItems() - 1); 770 } 771 772 // DeleteItem 773 bool 774 DragSortableListView::DeleteItem(int32 index) 775 { 776 BListItem* item = ItemAt(index); 777 if (item && RemoveItem(item)) { 778 delete item; 779 return true; 780 } 781 return false; 782 } 783 784 // _SetDropAnticipationRect 785 void 786 DragSortableListView::_SetDropAnticipationRect(BRect r) 787 { 788 if (fDropRect != r) { 789 if (fDropRect.IsValid()) 790 Invalidate(fDropRect); 791 fDropRect = r; 792 if (fDropRect.IsValid()) 793 Invalidate(fDropRect); 794 } 795 } 796 797 // _SetDropIndex 798 void 799 DragSortableListView::_SetDropIndex(int32 index) 800 { 801 if (fDropIndex != index) { 802 fDropIndex = index; 803 if (fDropIndex >= 0) { 804 int32 count = CountItems(); 805 if (fDropIndex == count) { 806 BRect r; 807 if (ItemAt(count - 1)) { 808 r = ItemFrame(count - 1); 809 r.top = r.bottom; 810 r.bottom = r.top + 1.0; 811 } else { 812 r = Bounds(); 813 r.bottom--; // compensate for scrollbars moved slightly out of window 814 } 815 _SetDropAnticipationRect(r); 816 } else { 817 BRect r = ItemFrame(fDropIndex); 818 r.top--; 819 r.bottom = r.top + 1.0; 820 _SetDropAnticipationRect(r); 821 } 822 } 823 } 824 } 825 826 // _RemoveDropAnticipationRect 827 void 828 DragSortableListView::_RemoveDropAnticipationRect() 829 { 830 _SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0)); 831 _SetDropIndex(-1); 832 } 833 834 // _SetDragMessage 835 void 836 DragSortableListView::_SetDragMessage(const BMessage* message) 837 { 838 if (message) 839 fDragMessageCopy = *message; 840 else 841 fDragMessageCopy.what = 0; 842 } 843 844 845 846 // SimpleListView class 847 SimpleListView::SimpleListView(BRect frame, BMessage* selectionChangeMessage) 848 : DragSortableListView(frame, "playlist listview", 849 B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL, 850 B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS), 851 fSelectionChangeMessage(selectionChangeMessage) 852 { 853 } 854 855 // SimpleListView class 856 SimpleListView::SimpleListView(BRect frame, const char* name, 857 BMessage* selectionChangeMessage, 858 list_view_type type, 859 uint32 resizingMode, uint32 flags) 860 : DragSortableListView(frame, name, type, resizingMode, flags), 861 fSelectionChangeMessage(selectionChangeMessage) 862 { 863 } 864 865 // destructor 866 SimpleListView::~SimpleListView() 867 { 868 delete fSelectionChangeMessage; 869 } 870 871 // MessageReceived 872 void 873 SimpleListView::MessageReceived( BMessage* message) 874 { 875 switch (message->what) { 876 default: 877 DragSortableListView::MessageReceived(message); 878 break; 879 } 880 } 881 882 // SelectionChanged 883 void 884 SimpleListView::SelectionChanged() 885 { 886 BLooper* looper = Looper(); 887 if (fSelectionChangeMessage && looper) { 888 BMessage message(*fSelectionChangeMessage); 889 looper->PostMessage(&message); 890 } 891 } 892 893 // CloneItem 894 BListItem* 895 SimpleListView::CloneItem(int32 atIndex) const 896 { 897 BListItem* clone = NULL; 898 if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(atIndex))) 899 clone = new SimpleItem(item->Text()); 900 return clone; 901 } 902 903 // DrawListItem 904 void 905 SimpleListView::DrawListItem(BView* owner, int32 index, BRect frame) const 906 { 907 if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(index))) { 908 uint32 flags = FLAGS_NONE; 909 if (index == fFocusedIndex) 910 flags |= FLAGS_FOCUSED; 911 if (index % 2) 912 flags |= FLAGS_TINTED_LINE; 913 item->Draw(owner, frame, flags); 914 } 915 } 916 917 // MakeDragMessage 918 void 919 SimpleListView::MakeDragMessage(BMessage* message) const 920 { 921 if (message) { 922 message->AddPointer( "list", (void*)dynamic_cast<const DragSortableListView*>(this)); 923 int32 index; 924 for (int32 i = 0; (index = CurrentSelection(i)) >= 0; i++) 925 message->AddInt32( "index", index ); 926 } 927 } 928 929