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 24const 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 39enum { 40 MSG_TICK = 'tick', 41}; 42 43using std::nothrow; 44 45// SimpleItem class 46SimpleItem::SimpleItem( const char *name ) 47 : BStringItem( name ) 48{ 49} 50 51SimpleItem::~SimpleItem() 52{ 53} 54 55// SimpleItem::DrawItem 56void 57SimpleItem::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 82void 83SimpleItem::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 105DragSortableListView::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 120DragSortableListView::~DragSortableListView() 121{ 122 delete fScrollPulse; 123} 124 125// AttachedToWindow 126void 127DragSortableListView::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 137void 138DragSortableListView::DetachedFromWindow() 139{ 140} 141 142// FrameResized 143void 144DragSortableListView::FrameResized(float width, float height) 145{ 146 BListView::FrameResized(width, height); 147 Invalidate(); 148} 149 150/* 151// MakeFocus 152void 153DragSortableListView::MakeFocus(bool focused) 154{ 155 if (focused != IsFocus()) { 156 Invalidate(); 157 BListView::MakeFocus(focused); 158 } 159} 160*/ 161// Draw 162void 163DragSortableListView::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 198void 199DragSortableListView::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 210void 211DragSortableListView::TargetedByScrollView(BScrollView* scrollView) 212{ 213 fScrollView = scrollView; 214 BListView::TargetedByScrollView(scrollView); 215} 216 217// InitiateDrag 218bool 219DragSortableListView::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 314void 315DragSortableListView::WindowActivated( bool active ) 316{ 317 // workarround for buggy focus indication of BScrollView 318 if ( BView* view = Parent() ) 319 view->Invalidate(); 320} 321 322// MessageReceived 323void 324DragSortableListView::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 398void 399DragSortableListView::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 411void 412DragSortableListView::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 449void 450DragSortableListView::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 489void 490DragSortableListView::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 504void 505DragSortableListView::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 515bool 516DragSortableListView::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 528void 529DragSortableListView::SetDragCommand(uint32 command) 530{ 531 fDragCommand = command; 532} 533 534// ModifiersChaned 535void 536DragSortableListView::ModifiersChanged() 537{ 538 SetDropTargetRect(&fDragMessageCopy, fLastMousePos); 539} 540 541// SetItemFocused 542void 543DragSortableListView::SetItemFocused(int32 index) 544{ 545 InvalidateItem(fFocusedIndex); 546 InvalidateItem(index); 547 fFocusedIndex = index; 548} 549 550// AcceptDragMessage 551bool 552DragSortableListView::AcceptDragMessage(const BMessage* message) const 553{ 554 return message->what == fDragCommand; 555} 556 557// SetDropTargetRect 558void 559DragSortableListView::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 591void 592DragSortableListView::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 607bool 608DragSortableListView::DoesAutoScrolling() const 609{ 610 return fScrollPulse; 611} 612 613// ScrollTo 614void 615DragSortableListView::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 634void 635DragSortableListView::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 666void 667DragSortableListView::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 696void 697DragSortableListView::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 707void 708DragSortableListView::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 720void 721DragSortableListView::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 741void 742DragSortableListView::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 756int32 757DragSortableListView::CountSelectedItems() const 758{ 759 int32 count = 0; 760 while (CurrentSelection(count) >= 0) 761 count++; 762 return count; 763} 764 765// SelectAll 766void 767DragSortableListView::SelectAll() 768{ 769 Select(0, CountItems() - 1); 770} 771 772// DeleteItem 773bool 774DragSortableListView::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 785void 786DragSortableListView::_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 798void 799DragSortableListView::_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 827void 828DragSortableListView::_RemoveDropAnticipationRect() 829{ 830 _SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0)); 831 _SetDropIndex(-1); 832} 833 834// _SetDragMessage 835void 836DragSortableListView::_SetDragMessage(const BMessage* message) 837{ 838 if (message) 839 fDragMessageCopy = *message; 840 else 841 fDragMessageCopy.what = 0; 842} 843 844 845 846// SimpleListView class 847SimpleListView::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 856SimpleListView::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 866SimpleListView::~SimpleListView() 867{ 868 delete fSelectionChangeMessage; 869} 870 871// MessageReceived 872void 873SimpleListView::MessageReceived( BMessage* message) 874{ 875 switch (message->what) { 876 default: 877 DragSortableListView::MessageReceived(message); 878 break; 879 } 880} 881 882// SelectionChanged 883void 884SimpleListView::SelectionChanged() 885{ 886 BLooper* looper = Looper(); 887 if (fSelectionChangeMessage && looper) { 888 BMessage message(*fSelectionChangeMessage); 889 looper->PostMessage(&message); 890 } 891} 892 893// CloneItem 894BListItem* 895SimpleListView::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 904void 905SimpleListView::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 918void 919SimpleListView::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