⛏️ index : haiku.git

/*
 * Copyright 2006-2007, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus <superstippi@gmx.de>
 */
#include <stdio.h>
#include <malloc.h>
#include <new>

#include <Bitmap.h>
#include <Cursor.h>
#include <Entry.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <ScrollBar.h>
#include <ScrollView.h>
#include <String.h>
#include <Window.h>

#include "ListViews.h"

const unsigned char kCopyCursor[] = { 16, 1, 1, 1,
	0x00, 0x00, 0x70, 0x00, 0x48, 0x00, 0x48, 0x00,
	0x27, 0xc0, 0x24, 0xb8, 0x12, 0x54, 0x10, 0x02,
	0x79, 0xe2, 0x99, 0x22, 0x85, 0x7a, 0x61, 0x4a,
	0x19, 0xca, 0x04, 0x4a, 0x02, 0x78, 0x00, 0x00,

	0x00, 0x00, 0x70, 0x00, 0x78, 0x00, 0x78, 0x00,
	0x3f, 0xc0, 0x3f, 0xf8, 0x1f, 0xfc, 0x1f, 0xfe,
	0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfe,
	0x1f, 0xfe, 0x07, 0xfe, 0x03, 0xf8, 0x00, 0x00 };

#define MAX_DRAG_HEIGHT		200.0
#define ALPHA				170
#define TEXT_OFFSET			5.0

enum {
	MSG_TICK	= 'tick',
};

using std::nothrow;

// SimpleItem class
SimpleItem::SimpleItem( const char *name )
	: BStringItem( name )
{
}

SimpleItem::~SimpleItem()
{
}

// SimpleItem::DrawItem
void
SimpleItem::Draw(BView *owner, BRect frame, uint32 flags)
{
	DrawBackground(owner, frame, flags);
	// label
	if (IsSelected())
		owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
	else
		owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
	font_height fh;
	owner->GetFontHeight( &fh );
	const char* text = Text();
	BString truncatedString( text );
	owner->TruncateString( &truncatedString, B_TRUNCATE_MIDDLE,
						   frame.Width() - TEXT_OFFSET - 4.0 );
	float height = frame.Height();
	float textHeight = fh.ascent + fh.descent;
	BPoint textPoint;
	textPoint.x = frame.left + TEXT_OFFSET;
	textPoint.y = frame.top
				  + ceilf(height / 2.0 - textHeight / 2.0
				  		  + fh.ascent);
	owner->DrawString(truncatedString.String(), textPoint);
}

// SimpleItem::DrawBackground
void
SimpleItem::DrawBackground(BView *owner, BRect frame, uint32 flags)
{
	// stroke a blue frame around the item if it's focused
	if (flags & FLAGS_FOCUSED) {
		owner->SetLowColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
		owner->StrokeRect(frame, B_SOLID_LOW);
		frame.InsetBy(1.0, 1.0);
	}
	// figure out bg-color
	rgb_color color = ui_color(B_LIST_BACKGROUND_COLOR);

	if (IsSelected())
		color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);

	if ( flags & FLAGS_TINTED_LINE )
		color = tint_color(color, 1.06);
	// background
	owner->SetLowColor(color);
	owner->FillRect(frame, B_SOLID_LOW);
}

// DragSortableListView class
DragSortableListView::DragSortableListView(BRect frame, const char* name,
										   list_view_type type, uint32 resizingMode,
										   uint32 flags)
	: BListView(frame, name, type, resizingMode, flags),
	  fDropRect(0.0, 0.0, -1.0, -1.0),
	  fScrollPulse(NULL),
	  fDropIndex(-1),
	  fLastClickedItem(NULL),
	  fScrollView(NULL),
	  fDragCommand(B_SIMPLE_DATA),
	  fFocusedIndex(-1)
{
	SetViewColor(B_TRANSPARENT_32_BIT);
}

DragSortableListView::~DragSortableListView()
{
	delete fScrollPulse;
}

// AttachedToWindow
void
DragSortableListView::AttachedToWindow()
{
	BListView::AttachedToWindow();

	// work arround a bug in BListView
	BRect bounds = Bounds();
	BListView::FrameResized(bounds.Width(), bounds.Height());
}

// DetachedFromWindow
void
DragSortableListView::DetachedFromWindow()
{
}

// FrameResized
void
DragSortableListView::FrameResized(float width, float height)
{
	BListView::FrameResized(width, height);
	Invalidate();
}

/*
// MakeFocus
void
DragSortableListView::MakeFocus(bool focused)
{
	if (focused != IsFocus()) {
		Invalidate();
		BListView::MakeFocus(focused);
	}
}
*/
// Draw
void
DragSortableListView::Draw( BRect updateRect )
{
	int32 firstIndex = IndexOf(updateRect.LeftTop());
	int32 lastIndex = IndexOf(updateRect.RightBottom());
	if (firstIndex >= 0) {
		if (lastIndex < firstIndex)
			lastIndex = CountItems() - 1;
		// update rect contains items
		BRect r = updateRect;
		for (int32 i = firstIndex; i <= lastIndex; i++) {
			r = ItemFrame(i);
			DrawListItem(this, i, r);
		}
		updateRect.top = r.bottom + 1.0;
		if (updateRect.IsValid()) {
			SetLowColor(ui_color(B_LIST_BACKGROUND_COLOR));
			FillRect(updateRect, B_SOLID_LOW);
		}
	} else {
		SetLowColor(ui_color(B_LIST_BACKGROUND_COLOR));
		FillRect(updateRect, B_SOLID_LOW);
	}
	// drop anticipation indication
	if (fDropRect.IsValid()) {
		SetHighColor(255, 0, 0, 255);
		StrokeRect(fDropRect);
	}
/*	// focus indication
	if (IsFocus()) {
		SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
		StrokeRect(Bounds());
	}*/
}

// ScrollTo
void
DragSortableListView::ScrollTo(BPoint where)
{
	uint32 buttons;
	BPoint point;
	GetMouse(&point, &buttons, false);
	uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
	MouseMoved(point, transit, &fDragMessageCopy);
	BListView::ScrollTo(where);
}

// TargetedByScrollView
void
DragSortableListView::TargetedByScrollView(BScrollView* scrollView)
{
	fScrollView = scrollView;
	BListView::TargetedByScrollView(scrollView);
}

// InitiateDrag
bool
DragSortableListView::InitiateDrag( BPoint point, int32 index, bool )
{
	// supress drag&drop while an item is focused
	if (fFocusedIndex >= 0)
		return false;

	bool success = false;
	BListItem* item = ItemAt( CurrentSelection( 0 ) );
	if ( !item ) {
		// workarround a timing problem
		Select( index );
		item = ItemAt( index );
	}
	if ( item ) {
		// create drag message
		BMessage msg( fDragCommand );
		MakeDragMessage( &msg );
		// figure out drag rect
		float width = Bounds().Width();
		BRect dragRect(0.0, 0.0, width, -1.0);
		// figure out, how many items fit into our bitmap
		int32 numItems;
		bool fade = false;
		for (numItems = 0; BListItem* item = ItemAt( CurrentSelection( numItems ) ); numItems++) {
			dragRect.bottom += ceilf( item->Height() ) + 1.0;
			if ( dragRect.Height() > MAX_DRAG_HEIGHT ) {
				fade = true;
				dragRect.bottom = MAX_DRAG_HEIGHT;
				numItems++;
				break;
			}
		}
		BBitmap* dragBitmap = new BBitmap( dragRect, B_RGB32, true );
		if ( dragBitmap && dragBitmap->IsValid() ) {
			if ( BView *v = new BView( dragBitmap->Bounds(), "helper", B_FOLLOW_NONE, B_WILL_DRAW ) ) {
				dragBitmap->AddChild( v );
				dragBitmap->Lock();
				BRect itemBounds( dragRect) ;
				itemBounds.bottom = 0.0;
				// let all selected items, that fit into our drag_bitmap, draw
				for ( int32 i = 0; i < numItems; i++ ) {
					int32 index = CurrentSelection( i );
					BListItem* item = ItemAt( index );
					itemBounds.bottom = itemBounds.top + ceilf( item->Height() );
					if ( itemBounds.bottom > dragRect.bottom )
						itemBounds.bottom = dragRect.bottom;
					DrawListItem( v, index, itemBounds );
					itemBounds.top = itemBounds.bottom + 1.0;
				}
				// make a black frame arround the edge
				v->SetHighColor( 0, 0, 0, 255 );
				v->StrokeRect( v->Bounds() );
				v->Sync();

				uint8 *bits = (uint8 *)dragBitmap->Bits();
				int32 height = (int32)dragBitmap->Bounds().Height() + 1;
				int32 width = (int32)dragBitmap->Bounds().Width() + 1;
				int32 bpr = dragBitmap->BytesPerRow();

				if (fade) {
					for ( int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr ) {
						uint8 *line = bits + 3;
						for (uint8 *end = line + 4 * width; line < end; line += 4)
							*line = ALPHA;
					}
					for ( int32 y = height - ALPHA / 2; y < height; y++, bits += bpr ) {
						uint8 *line = bits + 3;
						for (uint8 *end = line + 4 * width; line < end; line += 4)
							*line = (height - y) << 1;
					}
				} else {
					for ( int32 y = 0; y < height; y++, bits += bpr ) {
						uint8 *line = bits + 3;
						for (uint8 *end = line + 4 * width; line < end; line += 4)
							*line = ALPHA;
					}
				}
				dragBitmap->Unlock();
			}
		} else {
			delete dragBitmap;
			dragBitmap = NULL;
		}
		if (dragBitmap)
			DragMessage( &msg, dragBitmap, B_OP_ALPHA, BPoint( 0.0, 0.0 ) );
		else
			DragMessage( &msg, dragRect.OffsetToCopy( point ), this );

		_SetDragMessage(&msg);
		success = true;
	}
	return success;
}

// WindowActivated
void
DragSortableListView::WindowActivated( bool active )
{
	// workarround for buggy focus indication of BScrollView
	if ( BView* view = Parent() )
		view->Invalidate();
}

// MessageReceived
void
DragSortableListView::MessageReceived(BMessage* message)
{
	if (AcceptDragMessage(message)) {
		DragSortableListView *list = NULL;
		if (message->FindPointer("list", (void **)&list) == B_OK
			&& list == this) {
			int32 count = CountItems();
			if (fDropIndex < 0 || fDropIndex > count)
				fDropIndex = count;
			BList indices;
			int32 index;
			for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK; i++)
				indices.AddItem((void*)(addr_t)index);
			if (indices.CountItems() > 0) {
				if (modifiers() & B_SHIFT_KEY)
					CopyItems(indices, fDropIndex);
				else
					MoveItems(indices, fDropIndex);
			}
			fDropIndex = -1;
		}
	} else {
		switch (message->what) {
			case MSG_TICK: {
				float scrollV = 0.0;
				BRect rect(Bounds());
				BPoint point;
				uint32 buttons;
				GetMouse(&point, &buttons, false);
				if (rect.Contains(point)) {
					// calculate the vertical scrolling offset
					float hotDist = rect.Height() * SCROLL_AREA;
					if (point.y > rect.bottom - hotDist)
						scrollV = hotDist - (rect.bottom - point.y);
					else if (point.y < rect.top + hotDist)
						scrollV = (point.y - rect.top) - hotDist;
				}
				// scroll
				if (scrollV != 0.0 && fScrollView) {
					if (BScrollBar* scrollBar = fScrollView->ScrollBar(B_VERTICAL)) {
						float value = scrollBar->Value();
						scrollBar->SetValue(scrollBar->Value() + scrollV);
						if (scrollBar->Value() != value) {
							// update mouse position
							uint32 buttons;
							BPoint point;
							GetMouse(&point, &buttons, false);
							uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
							MouseMoved(point, transit, &fDragMessageCopy);
						}
					}
				}
				break;
			}
			case B_MODIFIERS_CHANGED:
				ModifiersChanged();
				break;
			case B_MOUSE_WHEEL_CHANGED: {
				BListView::MessageReceived( message );
				BPoint point;
				uint32 buttons;
				GetMouse(&point, &buttons, false);
				uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
				MouseMoved(point, transit, &fDragMessageCopy);
				break;
			}
			default:
				BListView::MessageReceived( message );
				break;
		}
	}
}

// KeyDown
void
DragSortableListView::KeyDown( const char* bytes, int32 numBytes )
{
	if (numBytes < 1)
		return;

	if (bytes[0] == B_BACKSPACE)
		RemoveSelected();

	BListView::KeyDown(bytes, numBytes);
}

// MouseDown
void
DragSortableListView::MouseDown( BPoint where )
{
	int32 clicks = 1;
	uint32 buttons = 0;
	Window()->CurrentMessage()->FindInt32("clicks", &clicks);
	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
	int32 clickedIndex = -1;
	for (int32 i = 0; BListItem* item = ItemAt(i); i++) {
		if (ItemFrame(i).Contains(where)) {
			if (clicks == 2) {
				// only do something if user clicked the same item twice
				if (fLastClickedItem == item)
					DoubleClicked(i);
			} else {
				// remember last clicked item
				fLastClickedItem = item;
			}
			clickedIndex = i;
			break;
		}
	}
	if (clickedIndex == -1)
		fLastClickedItem = NULL;

	BListItem* item = ItemAt(clickedIndex);
	if (ListType() == B_MULTIPLE_SELECTION_LIST
		&& item && (buttons & B_SECONDARY_MOUSE_BUTTON)) {
		if (item->IsSelected())
			Deselect(clickedIndex);
		else
			Select(clickedIndex, true);
	} else {
		BListView::MouseDown(where);
	}
}

// MouseMoved
void
DragSortableListView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg)
{
	if (msg && AcceptDragMessage(msg)) {
		switch (transit) {
			case B_ENTERED_VIEW:
			case B_INSIDE_VIEW: {
				// remember drag message
				// this is needed to react on modifier changes
				_SetDragMessage(msg);
				// set drop target through virtual function
				SetDropTargetRect(msg, where);
				// go into autoscrolling mode
				BRect r = Bounds();
				r.InsetBy(0.0, r.Height() * SCROLL_AREA);
				SetAutoScrolling(!r.Contains(where));
				break;
			}
			case B_EXITED_VIEW:
				// forget drag message
				_SetDragMessage(NULL);
				SetAutoScrolling(false);
				// fall through
			case B_OUTSIDE_VIEW:
				_RemoveDropAnticipationRect();
				break;
		}
	} else {
		_RemoveDropAnticipationRect();
		BListView::MouseMoved(where, transit, msg);
		_SetDragMessage(NULL);
		SetAutoScrolling(false);

		BCursor cursor(B_HAND_CURSOR);
		SetViewCursor(&cursor, true);
	}
	fLastMousePos = where;
}

// MouseUp
void
DragSortableListView::MouseUp( BPoint where )
{
	// remove drop mark
	_SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
	SetAutoScrolling(false);
	// be sure to forget drag message
	_SetDragMessage(NULL);
	BListView::MouseUp( where );

	BCursor cursor(B_HAND_CURSOR);
	SetViewCursor(&cursor, true);
}

// DrawItem
void
DragSortableListView::DrawItem( BListItem *item, BRect itemFrame, bool complete )
{
	DrawListItem( this, IndexOf( item ), itemFrame );
/*	if (IsFocus()) {
		SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
		StrokeRect(Bounds());
	}*/
}

// MouseWheelChanged
bool
DragSortableListView::MouseWheelChanged(float x, float y)
{
	BPoint where;
	uint32 buttons;
	GetMouse(&where, &buttons, false);
	if (Bounds().Contains(where))
		return true;
	else
		return false;
}

// SetDragCommand
void
DragSortableListView::SetDragCommand(uint32 command)
{
	fDragCommand = command;
}

// ModifiersChaned
void
DragSortableListView::ModifiersChanged()
{
	SetDropTargetRect(&fDragMessageCopy, fLastMousePos);
}

// SetItemFocused
void
DragSortableListView::SetItemFocused(int32 index)
{
	InvalidateItem(fFocusedIndex);
	InvalidateItem(index);
	fFocusedIndex = index;
}

// AcceptDragMessage
bool
DragSortableListView::AcceptDragMessage(const BMessage* message) const
{
	return message->what == fDragCommand;
}

// SetDropTargetRect
void
DragSortableListView::SetDropTargetRect(const BMessage* message, BPoint where)

{
	if (AcceptDragMessage(message)) {
		bool copy = modifiers() & B_SHIFT_KEY;
		bool replaceAll = !message->HasPointer("list") && copy;
			// when dragging something in from ie Tracker, the
			// user has to hold shift down to replace everything
			// (opposite meaning of the normal shift behaviour)
		BRect r = Bounds();
		if (replaceAll) {
			r.bottom--;	// compensate for scrollbar offset
			_SetDropAnticipationRect(r);
			fDropIndex = -1;
		} else {
			// offset where by half of item height
			r = ItemFrame(0);
			where.y += r.Height() / 2.0;

			int32 index = IndexOf(where);
			if (index < 0)
				index = CountItems();
			_SetDropIndex(index);

			const uchar* cursorData = copy ? kCopyCursor : B_HAND_CURSOR;
			BCursor cursor(cursorData);
			SetViewCursor(&cursor, true);
		}
	}
}

// SetAutoScrolling
void
DragSortableListView::SetAutoScrolling(bool enable)
{
	if (fScrollPulse && enable)
		return;
	if (enable) {
		BMessenger messenger(this, Window());
		BMessage message(MSG_TICK);
		fScrollPulse = new BMessageRunner(messenger, &message, 40000LL);
	} else {
		delete fScrollPulse;
		fScrollPulse = NULL;
	}
}

// DoesAutoScrolling
bool
DragSortableListView::DoesAutoScrolling() const
{
	return fScrollPulse;
}

// ScrollTo
void
DragSortableListView::ScrollTo(int32 index)
{
	if (index < 0)
		index = 0;
	if (index >= CountItems())
		index = CountItems() - 1;

	if (ItemAt(index)) {
		BRect itemFrame = ItemFrame(index);
		BRect bounds = Bounds();
		if (itemFrame.top < bounds.top) {
			ScrollTo(itemFrame.LeftTop());
		} else if (itemFrame.bottom > bounds.bottom) {
			ScrollTo(BPoint(0.0, itemFrame.bottom - bounds.Height()));
		}
	}
}

// MoveItems
void
DragSortableListView::MoveItems(const BList& indices, int32 index)
{
	DeselectAll();
	// we remove the items while we look at them, the insertion index is decreased
	// when the items index is lower, so that we insert at the right spot after
	// removal
	BList removedItems;
	int32 count = indices.CountItems();
	for (int32 i = 0; i < count; i++) {
		int32 removeIndex = (int32)(addr_t)indices.ItemAtFast(i) - i;
		BListItem* item = RemoveItem(removeIndex);
		if (item && removedItems.AddItem((void*)item)) {
			if (removeIndex < index)
				index--;
		}
		// else ??? -> blow up
	}
	count = removedItems.CountItems();
	for (int32 i = 0; i < count; i++) {
		BListItem* item = (BListItem*)removedItems.ItemAtFast(i);
		if (AddItem(item, index)) {
			// after we're done, the newly inserted items will be selected
			Select(index, true);
			// next items will be inserted after this one
			index++;
		} else
			delete item;
	}
}

// CopyItems
void
DragSortableListView::CopyItems(const BList& indices, int32 toIndex)
{
	DeselectAll();
	// by inserting the items after we copied all items first, we avoid
	// cloning an item we already inserted and messing everything up
	// in other words, don't touch the list before we know which items
	// need to be cloned
	BList clonedItems;
	int32 count = indices.CountItems();
	for (int32 i = 0; i < count; i++) {
		int32 index = (int32)(addr_t)indices.ItemAtFast(i);
		BListItem* item = CloneItem(index);
		if (item && !clonedItems.AddItem((void*)(addr_t)item))
			delete item;
	}
	count = clonedItems.CountItems();
	for (int32 i = 0; i < count; i++) {
		BListItem* item = (BListItem*)clonedItems.ItemAtFast(i);
		if (AddItem(item, toIndex)) {
			// after we're done, the newly inserted items will be selected
			Select(toIndex, true);
			// next items will be inserted after this one
			toIndex++;
		} else
			delete item;
	}
}

// RemoveItemList
void
DragSortableListView::RemoveItemList(const BList& indices)
{
	int32 count = indices.CountItems();
	for (int32 i = 0; i < count; i++) {
		int32 index = (int32)(addr_t)indices.ItemAtFast(i) - i;
		delete RemoveItem(index);
	}
}

// GetSelectedItems
void
DragSortableListView::GetSelectedItems(BList& indices)
{
	for (int32 i = 0; true; i++) {
		int32 index = CurrentSelection(i);
		if (index < 0)
			break;
		if (!indices.AddItem((void*)(addr_t)index))
			break;
	}
}

// RemoveSelected
void
DragSortableListView::RemoveSelected()
{
	BList indices;
	GetSelectedItems(indices);
	int32 index = CurrentSelection() - 1;

	DeselectAll();

	if (indices.CountItems() > 0)
		RemoveItemList(indices);

	if (CountItems() > 0) {
		if (index < 0)
			index = 0;

		Select(index);
	}
}

// RemoveAll
void
DragSortableListView::RemoveAll()
{
	BList indices;
	int32 count = CountItems();
	for (int32 i = 0; i < count; i++) {
		if (!indices.AddItem((void*)(addr_t)i))
			break;
	}

	if (indices.CountItems() > 0)
		RemoveItemList(indices);
}

// CountSelectedItems
int32
DragSortableListView::CountSelectedItems() const
{
	int32 count = 0;
	while (CurrentSelection(count) >= 0)
		count++;
	return count;
}

// SelectAll
void
DragSortableListView::SelectAll()
{
	Select(0, CountItems() - 1);
}

// DeleteItem
bool
DragSortableListView::DeleteItem(int32 index)
{
	BListItem* item = ItemAt(index);
	if (item && RemoveItem(item)) {
		delete item;
		return true;
	}
	return false;
}

// _SetDropAnticipationRect
void
DragSortableListView::_SetDropAnticipationRect(BRect r)
{
	if (fDropRect != r) {
		if (fDropRect.IsValid())
			Invalidate(fDropRect);
		fDropRect = r;
		if (fDropRect.IsValid())
			Invalidate(fDropRect);
	}
}

// _SetDropIndex
void
DragSortableListView::_SetDropIndex(int32 index)
{
	if (fDropIndex != index) {
		fDropIndex = index;
		if (fDropIndex >= 0) {
			int32 count = CountItems();
			if (fDropIndex == count) {
				BRect r;
				if (ItemAt(count - 1)) {
					r = ItemFrame(count - 1);
					r.top = r.bottom;
					r.bottom = r.top + 1.0;
				} else {
					r = Bounds();
					r.bottom--;	// compensate for scrollbars moved slightly out of window
				}
				_SetDropAnticipationRect(r);
			} else {
				BRect r = ItemFrame(fDropIndex);
				r.top--;
				r.bottom = r.top + 1.0;
				_SetDropAnticipationRect(r);
			}
		}
	}
}

// _RemoveDropAnticipationRect
void
DragSortableListView::_RemoveDropAnticipationRect()
{
	_SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0));
	_SetDropIndex(-1);
}

// _SetDragMessage
void
DragSortableListView::_SetDragMessage(const BMessage* message)
{
	if (message)
		fDragMessageCopy = *message;
	else
		fDragMessageCopy.what = 0;
}



// SimpleListView class
SimpleListView::SimpleListView(BRect frame, BMessage* selectionChangeMessage)
	: DragSortableListView(frame, "playlist listview",
						   B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL,
						   B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS),
	  fSelectionChangeMessage(selectionChangeMessage)
{
}

// SimpleListView class
SimpleListView::SimpleListView(BRect frame, const char* name,
							   BMessage* selectionChangeMessage,
							   list_view_type type,
							   uint32 resizingMode, uint32 flags)
	: DragSortableListView(frame, name, type, resizingMode, flags),
	  fSelectionChangeMessage(selectionChangeMessage)
{
}

// destructor
SimpleListView::~SimpleListView()
{
	delete fSelectionChangeMessage;
}

// MessageReceived
void
SimpleListView::MessageReceived( BMessage* message)
{
	switch (message->what) {
		default:
			DragSortableListView::MessageReceived(message);
			break;
	}
}

// SelectionChanged
void
SimpleListView::SelectionChanged()
{
	BLooper* looper = Looper();
	if (fSelectionChangeMessage && looper) {
		BMessage message(*fSelectionChangeMessage);
		looper->PostMessage(&message);
	}
}

// CloneItem
BListItem*
SimpleListView::CloneItem(int32 atIndex) const
{
	BListItem* clone = NULL;
	if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(atIndex)))
		clone = new SimpleItem(item->Text());
	return clone;
}

// DrawListItem
void
SimpleListView::DrawListItem(BView* owner, int32 index, BRect frame) const
{
	if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(index))) {
		uint32 flags  = FLAGS_NONE;
		if (index == fFocusedIndex)
			flags |= FLAGS_FOCUSED;
		if (index % 2)
			flags |= FLAGS_TINTED_LINE;
		item->Draw(owner, frame, flags);
	}
}

// MakeDragMessage
void
SimpleListView::MakeDragMessage(BMessage* message) const
{
	if (message) {
		message->AddPointer( "list", (void*)dynamic_cast<const DragSortableListView*>(this));
		int32 index;
		for (int32 i = 0; (index = CurrentSelection(i)) >= 0; i++)
			message->AddInt32( "index", index );
	}
}