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