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