xref: /haiku/src/apps/mediaplayer/playlist/ListViews.cpp (revision 5e7964b0a929555415798dea3373db9ac4611caa)
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