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 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 79void 80SimpleItem::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 100DragSortableListView::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 115DragSortableListView::~DragSortableListView() 116{ 117 delete fScrollPulse; 118} 119 120// AttachedToWindow 121void 122DragSortableListView::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 132void 133DragSortableListView::DetachedFromWindow() 134{ 135} 136 137// FrameResized 138void 139DragSortableListView::FrameResized(float width, float height) 140{ 141 BListView::FrameResized(width, height); 142 Invalidate(); 143} 144 145/* 146// MakeFocus 147void 148DragSortableListView::MakeFocus(bool focused) 149{ 150 if (focused != IsFocus()) { 151 Invalidate(); 152 BListView::MakeFocus(focused); 153 } 154} 155*/ 156// Draw 157void 158DragSortableListView::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 193void 194DragSortableListView::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 205void 206DragSortableListView::TargetedByScrollView(BScrollView* scrollView) 207{ 208 fScrollView = scrollView; 209 BListView::TargetedByScrollView(scrollView); 210} 211 212// InitiateDrag 213bool 214DragSortableListView::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 309void 310DragSortableListView::WindowActivated( bool active ) 311{ 312 // workarround for buggy focus indication of BScrollView 313 if ( BView* view = Parent() ) 314 view->Invalidate(); 315} 316 317// MessageReceived 318void 319DragSortableListView::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*)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 393void 394DragSortableListView::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 406void 407DragSortableListView::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 444void 445DragSortableListView::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 484void 485DragSortableListView::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 499void 500DragSortableListView::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 510bool 511DragSortableListView::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 523void 524DragSortableListView::SetDragCommand(uint32 command) 525{ 526 fDragCommand = command; 527} 528 529// ModifiersChaned 530void 531DragSortableListView::ModifiersChanged() 532{ 533 SetDropTargetRect(&fDragMessageCopy, fLastMousePos); 534} 535 536// SetItemFocused 537void 538DragSortableListView::SetItemFocused(int32 index) 539{ 540 InvalidateItem(fFocusedIndex); 541 InvalidateItem(index); 542 fFocusedIndex = index; 543} 544 545// AcceptDragMessage 546bool 547DragSortableListView::AcceptDragMessage(const BMessage* message) const 548{ 549 return message->what == fDragCommand; 550} 551 552// SetDropTargetRect 553void 554DragSortableListView::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 586void 587DragSortableListView::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 602bool 603DragSortableListView::DoesAutoScrolling() const 604{ 605 return fScrollPulse; 606} 607 608// ScrollTo 609void 610DragSortableListView::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 629void 630DragSortableListView::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)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 661void 662DragSortableListView::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)indices.ItemAtFast(i); 673 BListItem* item = CloneItem(index); 674 if (item && !clonedItems.AddItem((void*)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 691void 692DragSortableListView::RemoveItemList(const BList& indices) 693{ 694 int32 count = indices.CountItems(); 695 for (int32 i = 0; i < count; i++) { 696 int32 index = (int32)indices.ItemAtFast(i) - i; 697 delete RemoveItem(index); 698 } 699} 700 701// GetSelectedItems 702void 703DragSortableListView::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*)index)) 710 break; 711 } 712} 713 714// RemoveSelected 715void 716DragSortableListView::RemoveSelected() 717{ 718 BList indices; 719 GetSelectedItems(indices); 720 721 DeselectAll(); 722 723 if (indices.CountItems() > 0) 724 RemoveItemList(indices); 725} 726 727// RemoveAll 728void 729DragSortableListView::RemoveAll() 730{ 731 BList indices; 732 int32 count = CountItems(); 733 for (int32 i = 0; i < count; i++) { 734 if (!indices.AddItem((void*)i)) 735 break; 736 } 737 738 if (indices.CountItems() > 0) 739 RemoveItemList(indices); 740} 741 742// CountSelectedItems 743int32 744DragSortableListView::CountSelectedItems() const 745{ 746 int32 count = 0; 747 while (CurrentSelection(count) >= 0) 748 count++; 749 return count; 750} 751 752// SelectAll 753void 754DragSortableListView::SelectAll() 755{ 756 Select(0, CountItems() - 1); 757} 758 759// DeleteItem 760bool 761DragSortableListView::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 772void 773DragSortableListView::_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 785void 786DragSortableListView::_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 814void 815DragSortableListView::_RemoveDropAnticipationRect() 816{ 817 _SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0)); 818 _SetDropIndex(-1); 819} 820 821// _SetDragMessage 822void 823DragSortableListView::_SetDragMessage(const BMessage* message) 824{ 825 if (message) 826 fDragMessageCopy = *message; 827 else 828 fDragMessageCopy.what = 0; 829} 830 831 832 833// SimpleListView class 834SimpleListView::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 843SimpleListView::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 853SimpleListView::~SimpleListView() 854{ 855 delete fSelectionChangeMessage; 856} 857 858// MessageReceived 859void 860SimpleListView::MessageReceived( BMessage* message) 861{ 862 switch (message->what) { 863 default: 864 DragSortableListView::MessageReceived(message); 865 break; 866 } 867} 868 869// SelectionChanged 870void 871SimpleListView::SelectionChanged() 872{ 873 BLooper* looper = Looper(); 874 if (fSelectionChangeMessage && looper) { 875 BMessage message(*fSelectionChangeMessage); 876 looper->PostMessage(&message); 877 } 878} 879 880// CloneItem 881BListItem* 882SimpleListView::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 891void 892SimpleListView::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 905void 906SimpleListView::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