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