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
SimpleItem(const char * name)46 SimpleItem::SimpleItem( const char *name )
47 : BStringItem( name )
48 {
49 }
50
~SimpleItem()51 SimpleItem::~SimpleItem()
52 {
53 }
54
55 // SimpleItem::DrawItem
56 void
Draw(BView * owner,BRect frame,uint32 flags)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
DrawBackground(BView * owner,BRect frame,uint32 flags)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
DragSortableListView(BRect frame,const char * name,list_view_type type,uint32 resizingMode,uint32 flags)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
~DragSortableListView()120 DragSortableListView::~DragSortableListView()
121 {
122 delete fScrollPulse;
123 }
124
125 // AttachedToWindow
126 void
AttachedToWindow()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
DetachedFromWindow()138 DragSortableListView::DetachedFromWindow()
139 {
140 }
141
142 // FrameResized
143 void
FrameResized(float width,float height)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
Draw(BRect updateRect)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
ScrollTo(BPoint where)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
TargetedByScrollView(BScrollView * scrollView)211 DragSortableListView::TargetedByScrollView(BScrollView* scrollView)
212 {
213 fScrollView = scrollView;
214 BListView::TargetedByScrollView(scrollView);
215 }
216
217 // InitiateDrag
218 bool
InitiateDrag(BPoint point,int32 index,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
WindowActivated(bool active)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
MessageReceived(BMessage * message)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
KeyDown(const char * bytes,int32 numBytes)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
MouseDown(BPoint where)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
MouseMoved(BPoint where,uint32 transit,const BMessage * msg)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
MouseUp(BPoint where)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
DrawItem(BListItem * item,BRect itemFrame,bool complete)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
MouseWheelChanged(float x,float y)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
SetDragCommand(uint32 command)529 DragSortableListView::SetDragCommand(uint32 command)
530 {
531 fDragCommand = command;
532 }
533
534 // ModifiersChaned
535 void
ModifiersChanged()536 DragSortableListView::ModifiersChanged()
537 {
538 SetDropTargetRect(&fDragMessageCopy, fLastMousePos);
539 }
540
541 // SetItemFocused
542 void
SetItemFocused(int32 index)543 DragSortableListView::SetItemFocused(int32 index)
544 {
545 InvalidateItem(fFocusedIndex);
546 InvalidateItem(index);
547 fFocusedIndex = index;
548 }
549
550 // AcceptDragMessage
551 bool
AcceptDragMessage(const BMessage * message) const552 DragSortableListView::AcceptDragMessage(const BMessage* message) const
553 {
554 return message->what == fDragCommand;
555 }
556
557 // SetDropTargetRect
558 void
SetDropTargetRect(const BMessage * message,BPoint where)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
SetAutoScrolling(bool enable)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
DoesAutoScrolling() const608 DragSortableListView::DoesAutoScrolling() const
609 {
610 return fScrollPulse;
611 }
612
613 // ScrollTo
614 void
ScrollTo(int32 index)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
MoveItems(const BList & indices,int32 index)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
CopyItems(const BList & indices,int32 toIndex)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
RemoveItemList(const BList & indices)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
GetSelectedItems(BList & indices)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
RemoveSelected()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
RemoveAll()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
CountSelectedItems() const757 DragSortableListView::CountSelectedItems() const
758 {
759 int32 count = 0;
760 while (CurrentSelection(count) >= 0)
761 count++;
762 return count;
763 }
764
765 // SelectAll
766 void
SelectAll()767 DragSortableListView::SelectAll()
768 {
769 Select(0, CountItems() - 1);
770 }
771
772 // DeleteItem
773 bool
DeleteItem(int32 index)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
_SetDropAnticipationRect(BRect r)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
_SetDropIndex(int32 index)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
_RemoveDropAnticipationRect()828 DragSortableListView::_RemoveDropAnticipationRect()
829 {
830 _SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0));
831 _SetDropIndex(-1);
832 }
833
834 // _SetDragMessage
835 void
_SetDragMessage(const BMessage * message)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
SimpleListView(BRect frame,BMessage * selectionChangeMessage)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
SimpleListView(BRect frame,const char * name,BMessage * selectionChangeMessage,list_view_type type,uint32 resizingMode,uint32 flags)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
~SimpleListView()866 SimpleListView::~SimpleListView()
867 {
868 delete fSelectionChangeMessage;
869 }
870
871 // MessageReceived
872 void
MessageReceived(BMessage * message)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
SelectionChanged()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*
CloneItem(int32 atIndex) const895 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
DrawListItem(BView * owner,int32 index,BRect frame) const905 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
MakeDragMessage(BMessage * message) const919 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