]> git.sesse.net Git - vlc/blob - modules/gui/beos/ListViews.cpp
Changes done since Feb 28 2003:
[vlc] / modules / gui / beos / ListViews.cpp
1 /*****************************************************************************
2  * ListViews.h: BeOS interface list view class implementation
3  *****************************************************************************
4  * Copyright (C) 1999, 2000, 2001 VideoLAN
5  * $Id: ListViews.cpp,v 1.3 2003/02/01 12:01:11 stippi Exp $
6  *
7  * Authors: Stephan Aßmus <stippi@yellowbites.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 #include <stdio.h>
25 #include <malloc.h>
26
27 #include <Bitmap.h>
28 #include <String.h>
29
30 /* VLC headers */
31 #include <vlc/vlc.h>
32 #include <vlc/intf.h>
33
34 #include "VlcWrapper.h"
35 #include "InterfaceWindow.h"
36 #include "ListViews.h"
37 #include "MsgVals.h"
38
39 #define MAX_DRAG_HEIGHT         200.0
40 #define ALPHA                           170
41 #define TEXT_OFFSET                     20.0
42
43 /*****************************************************************************
44  * PlaylistItem class
45  *****************************************************************************/
46 PlaylistItem::PlaylistItem( const char *name )
47         : BStringItem( name )
48 {
49 }
50
51 PlaylistItem::~PlaylistItem()
52 {
53 }
54
55 /*****************************************************************************
56  * PlaylistItem::DrawItem
57  *****************************************************************************/
58 void
59 PlaylistItem::Draw( BView *owner, BRect frame, bool tintedLine,
60                                         bool active, bool playing )
61 {
62         rgb_color color = (rgb_color){ 255, 255, 255, 255 };
63         if ( tintedLine )
64                 color = tint_color( color, 1.04 );
65         // background
66         if ( IsSelected() )
67                 color = tint_color( color, B_DARKEN_2_TINT );
68         owner->SetLowColor( color );
69         owner->FillRect( frame, B_SOLID_LOW );
70         // label
71         owner->SetHighColor( 0, 0, 0, 255 );
72         font_height fh;
73         owner->GetFontHeight( &fh );
74         BString truncatedString( Text() );
75         owner->TruncateString( &truncatedString, B_TRUNCATE_MIDDLE,
76                                                    frame.Width() - TEXT_OFFSET - 4.0 );
77         owner->DrawString( truncatedString.String(),
78                                            BPoint( frame.left + TEXT_OFFSET,
79                                                            frame.top + fh.ascent + 1.0 ) );
80         // playmark
81         if ( active )
82         {
83                 rgb_color black = (rgb_color){ 0, 0, 0, 255 };
84                 rgb_color green = (rgb_color){ 0, 255, 0, 255 };
85                 BRect r( 0.0, 0.0, 10.0, 10.0 );
86                 r.OffsetTo( frame.left + 4.0,
87                                         ceilf( ( frame.top + frame.bottom ) / 2.0 ) - 5.0 );
88                 if ( !playing )
89                         green = tint_color( color, B_DARKEN_1_TINT );
90                 rgb_color lightGreen = tint_color( green, B_LIGHTEN_2_TINT );
91                 rgb_color darkGreen = tint_color( green, B_DARKEN_2_TINT );
92                 BPoint arrow[3];
93                 arrow[0] = r.LeftTop();
94                 arrow[1] = r.LeftBottom();
95                 arrow[2].x = r.right;
96                 arrow[2].y = ( r.top + r.bottom ) / 2.0;
97                 owner->BeginLineArray( 6 );
98                         // black outline
99                         owner->AddLine( arrow[0], arrow[1], black );
100                         owner->AddLine( BPoint( arrow[1].x + 1.0, arrow[1].y - 1.0 ),
101                                                         arrow[2], black );
102                         owner->AddLine( arrow[0], arrow[2], black );
103                         // inset arrow
104                         arrow[0].x += 1.0;
105                         arrow[0].y += 2.0;
106                         arrow[1].x += 1.0;
107                         arrow[1].y -= 2.0;
108                         arrow[2].x -= 2.0;
109                         // highlights and shadow
110                         owner->AddLine( arrow[1], arrow[2], darkGreen );
111                         owner->AddLine( arrow[0], arrow[2], lightGreen );
112                         owner->AddLine( arrow[0], arrow[1], lightGreen );
113                 owner->EndLineArray();
114                 // fill green
115                 arrow[0].x += 1.0;
116                 arrow[0].y += 1.0;
117                 arrow[1].x += 1.0;
118                 arrow[1].y -= 1.0;
119                 arrow[2].x -= 2.0;
120                 owner->SetHighColor( green );
121                 owner->FillPolygon( arrow, 3 );
122         }
123 }
124
125 /*****************************************************************************
126  * DragSortableListView class
127  *****************************************************************************/
128 DragSortableListView::DragSortableListView( BRect frame, const char* name,
129                                                                                         list_view_type type, uint32 resizingMode,
130                                                                                         uint32 flags )
131         : BListView( frame, name, type, resizingMode, flags ),
132           fDropRect( 0.0, 0.0, -1.0, -1.0 ),
133           fDropIndex( -1 )
134 {
135         SetViewColor( B_TRANSPARENT_32_BIT );
136 }
137
138 DragSortableListView::~DragSortableListView()
139 {
140 }
141
142 /*****************************************************************************
143  * DragSortableListView::Draw
144  *****************************************************************************/
145 void
146 DragSortableListView::Draw( BRect updateRect )
147 {
148         int32 firstIndex = IndexOf( updateRect.LeftTop() );
149         int32 lastIndex = IndexOf( updateRect.RightBottom() );
150         if ( firstIndex >= 0 )
151         {
152                 if ( lastIndex < firstIndex )
153                         lastIndex = CountItems() - 1;
154                 // update rect contains items
155                 BRect r( updateRect );
156                 for ( int32 i = firstIndex; i <= lastIndex; i++)
157                 {
158                         r = ItemFrame( i );
159                         DrawListItem( this, i, r );
160                 }
161                 updateRect.top = r.bottom + 1.0;
162                 if ( updateRect.IsValid() )
163                 {
164                         SetLowColor( 255, 255, 255, 255 );
165                         FillRect( updateRect, B_SOLID_LOW );
166                 }
167         }
168         else
169         {
170                 SetLowColor( 255, 255, 255, 255 );
171                 FillRect( updateRect, B_SOLID_LOW );
172         }
173         // drop anticipation indication
174         if ( fDropRect.IsValid() )
175         {
176                 SetHighColor( 255, 0, 0, 255 );
177                 StrokeRect( fDropRect );
178         }
179 }
180
181 /*****************************************************************************
182  * DragSortableListView::InitiateDrag
183  *****************************************************************************/
184 bool
185 DragSortableListView::InitiateDrag( BPoint point, int32 index, bool )
186 {
187         bool success = false;
188         BListItem* item = ItemAt( CurrentSelection( 0 ) );
189         if ( !item )
190         {
191                 // workarround a timing problem
192                 Select( index );
193                 item = ItemAt( index );
194         }
195         if ( item )
196         {
197                 // create drag message
198                 BMessage msg( B_SIMPLE_DATA );
199                 MakeDragMessage( &msg );
200                 // figure out drag rect
201                 float width = Bounds().Width();
202                 BRect dragRect(0.0, 0.0, width, -1.0);
203                 // figure out, how many items fit into our bitmap
204                 int32 numItems;
205                 bool fade = false;
206                 for (numItems = 0; BListItem* item = ItemAt( CurrentSelection( numItems ) ); numItems++) {
207                         dragRect.bottom += ceilf( item->Height() ) + 1.0;
208                         if ( dragRect.Height() > MAX_DRAG_HEIGHT ) {
209                                 fade = true;
210                                 dragRect.bottom = MAX_DRAG_HEIGHT;
211                                 numItems++;
212                                 break;
213                         }
214                 }
215                 BBitmap* dragBitmap = new BBitmap( dragRect, B_RGB32, true );
216                 if ( dragBitmap && dragBitmap->IsValid() ) {
217                         if ( BView *v = new BView( dragBitmap->Bounds(), "helper", B_FOLLOW_NONE, B_WILL_DRAW ) ) {
218                                 dragBitmap->AddChild( v );
219                                 dragBitmap->Lock();
220                                 BRect itemBounds( dragRect) ;
221                                 itemBounds.bottom = 0.0;
222                                 // let all selected items, that fit into our drag_bitmap, draw
223                                 for ( int32 i = 0; i < numItems; i++ ) {
224                                         int32 index = CurrentSelection( i );
225                                         BListItem* item = ItemAt( index );
226                                         itemBounds.bottom = itemBounds.top + ceilf( item->Height() );
227                                         if ( itemBounds.bottom > dragRect.bottom )
228                                                 itemBounds.bottom = dragRect.bottom;
229                                         DrawListItem( v, index, itemBounds );
230                                         itemBounds.top = itemBounds.bottom + 1.0;
231                                 }
232                                 // make a black frame arround the edge
233                                 v->SetHighColor( 0, 0, 0, 255 );
234                                 v->StrokeRect( v->Bounds() );
235                                 v->Sync();
236         
237                                 uint8 *bits = (uint8 *)dragBitmap->Bits();
238                                 int32 height = (int32)dragBitmap->Bounds().Height() + 1;
239                                 int32 width = (int32)dragBitmap->Bounds().Width() + 1;
240                                 int32 bpr = dragBitmap->BytesPerRow();
241         
242                                 if (fade) {
243                                         for ( int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr ) {
244                                                 uint8 *line = bits + 3;
245                                                 for (uint8 *end = line + 4 * width; line < end; line += 4)
246                                                         *line = ALPHA;
247                                         }
248                                         for ( int32 y = height - ALPHA / 2; y < height; y++, bits += bpr ) {
249                                                 uint8 *line = bits + 3;
250                                                 for (uint8 *end = line + 4 * width; line < end; line += 4)
251                                                         *line = (height - y) << 1;
252                                         }
253                                 } else {
254                                         for ( int32 y = 0; y < height; y++, bits += bpr ) {
255                                                 uint8 *line = bits + 3;
256                                                 for (uint8 *end = line + 4 * width; line < end; line += 4)
257                                                         *line = ALPHA;
258                                         }
259                                 }
260                                 dragBitmap->Unlock();
261                                 success = true;
262                         }
263                 }
264                 if (success)
265                         DragMessage( &msg, dragBitmap, B_OP_ALPHA, BPoint( 0.0, 0.0 ) );
266                 else {
267                         delete dragBitmap;
268                         DragMessage( &msg, dragRect.OffsetToCopy( point ), this );
269                 }
270         }
271         return success;
272 }
273
274 /*****************************************************************************
275  * DragSortableListView::WindowActivated
276  *****************************************************************************/
277 void
278 DragSortableListView::WindowActivated( bool active )
279 {
280         // workarround for buggy focus indication of BScrollView
281         if ( BView* view = Parent() )
282                 view->Invalidate();
283 }
284
285 /*****************************************************************************
286  * DragSortableListView::MessageReceived
287  *****************************************************************************/
288 void
289 DragSortableListView::MessageReceived(BMessage* message)
290 {
291         switch ( message->what )
292         {
293                 case B_MODIFIERS_CHANGED:
294                         ModifiersChanged();
295                         break;
296                 case B_SIMPLE_DATA:
297                 {
298                         DragSortableListView *list = NULL;
299                         if ( message->FindPointer( "list", (void **)&list ) == B_OK
300                                  && list == this )
301                         {
302                                 int32 count = CountItems();
303                                 if ( fDropIndex < 0 || fDropIndex > count )
304                                         fDropIndex = count;
305                                 BList items;
306                                 int32 index;
307                                 for ( int32 i = 0; message->FindInt32( "index", i, &index ) == B_OK; i++ )
308                                         if ( BListItem* item = ItemAt(index) )
309                                                 items.AddItem( (void*)item );
310                                 if ( items.CountItems() > 0 )
311                                 {
312                                         if ( modifiers() & B_SHIFT_KEY )
313                                                 CopyItems( items, fDropIndex );
314                                         else
315                                                 MoveItems( items, fDropIndex );
316                                 }
317                                 fDropIndex = -1;
318                         }
319                         break;
320                 }
321                 default:
322                         BListView::MessageReceived( message );
323                         break;
324         }
325 }
326
327 /*****************************************************************************
328  * DragSortableListView::MouseMoved
329  *****************************************************************************/
330 void
331 DragSortableListView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg)
332 {
333         if ( msg && msg->what == B_SIMPLE_DATA )
334         {
335                 bool replaceAll = !msg->HasPointer("list") && !(modifiers() & B_SHIFT_KEY);
336                 switch ( transit )
337                 {
338                         case B_ENTERED_VIEW:
339                         case B_INSIDE_VIEW:
340                         {
341                                 if ( replaceAll )
342                                 {
343                                         BRect r( Bounds() );
344                                         r.bottom--;     // compensate for scrollbar offset
345                                         _SetDropAnticipationRect( r );
346                                         fDropIndex = -1;
347                                 }
348                                 else
349                                 {
350                                         // offset where by half of item height
351                                         BRect r( ItemFrame( 0 ) );
352                                         where.y += r.Height() / 2.0;
353         
354                                         int32 index = IndexOf( where );
355                                         if ( index < 0 )
356                                                 index = CountItems();
357                                         _SetDropIndex( index );
358                                 }
359                                 break;
360                         }
361                         case B_EXITED_VIEW:
362                         case B_OUTSIDE_VIEW:
363                                 _RemoveDropAnticipationRect();
364                                 break;
365                 }
366                 // remember drag message
367                 // this is needed to react on modifier changes
368                 fDragMessageCopy = *msg;
369         }
370         else
371         {
372                 _RemoveDropAnticipationRect();
373                 BListView::MouseMoved(where, transit, msg);
374         }
375 }
376
377 /*****************************************************************************
378  * DragSortableListView::MouseUp
379  *****************************************************************************/
380 void
381 DragSortableListView::MouseUp( BPoint where )
382 {
383         // remove drop mark
384         _SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
385         BListView::MouseUp( where );
386 }
387
388 /*****************************************************************************
389  * DragSortableListView::DrawItem
390  *****************************************************************************/
391 void
392 DragSortableListView::DrawItem( BListItem *item, BRect itemFrame, bool complete )
393 {
394         DrawListItem( this, IndexOf( item ), itemFrame );
395 }
396
397 /*****************************************************************************
398  * DragSortableListView::ModifiersChaned
399  *****************************************************************************/
400 void
401 DragSortableListView::ModifiersChanged()
402 {
403         BPoint where;
404         uint32 buttons;
405         GetMouse( &where, &buttons, false );
406         uint32 transit = Bounds().Contains( where ) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
407         MouseMoved( where, transit, &fDragMessageCopy );
408 }
409
410 /*****************************************************************************
411  * DragSortableListView::MoveItems
412  *****************************************************************************/
413 void
414 DragSortableListView::MoveItems( BList& items, int32 index )
415 {
416         DeselectAll();
417         // we remove the items while we look at them, the insertion index is decreased
418         // when the items index is lower, so that we insert at the right spot after
419         // removal
420         BList removedItems;
421         int32 count = items.CountItems();
422         for ( int32 i = 0; i < count; i++ )
423         {
424                 BListItem* item = (BListItem*)items.ItemAt( i );
425                 int32 removeIndex = IndexOf( item );
426                 if ( RemoveItem( item ) && removedItems.AddItem( (void*)item ) )
427                 {
428                         if ( removeIndex < index )
429                                 index--;
430                 }
431                 // else ??? -> blow up
432         }
433         for ( int32 i = 0; BListItem* item = (BListItem*)removedItems.ItemAt( i ); i++ )
434         {
435                 if ( AddItem( item, index ) )
436                 {
437                         // after we're done, the newly inserted items will be selected
438                         Select( index, true );
439                         // next items will be inserted after this one
440                         index++;
441                 }
442                 else
443                         delete item;
444         }
445 }
446
447 /*****************************************************************************
448  * DragSortableListView::CopyItems
449  *****************************************************************************/
450 void
451 DragSortableListView::CopyItems( BList& items, int32 index )
452 {
453         DeselectAll();
454         // by inserting the items after we copied all items first, we avoid
455         // cloning an item we already inserted and messing everything up
456         // in other words, don't touch the list before we know which items
457         // need to be cloned
458         BList clonedItems;
459         int32 count = items.CountItems();
460         for ( int32 i = 0; i < count; i++ )
461         {
462                 BListItem* item = CloneItem( IndexOf( (BListItem*)items.ItemAt( i ) ) );
463                 if ( item && !clonedItems.AddItem( (void*)item ) )
464                         delete item;
465         }
466         for ( int32 i = 0; BListItem* item = (BListItem*)clonedItems.ItemAt( i ); i++ )
467         {
468                 if ( AddItem( item, index ) )
469                 {
470                         // after we're done, the newly inserted items will be selected
471                         Select( index, true );
472                         // next items will be inserted after this one
473                         index++;
474                 }
475                 else
476                         delete item;
477         }
478 }
479
480 /*****************************************************************************
481  * DragSortableListView::RemoveItemList
482  *****************************************************************************/
483 void
484 DragSortableListView::RemoveItemList( BList& items )
485 {
486         int32 count = items.CountItems();
487         for ( int32 i = 0; i < count; i++ )
488         {
489                 BListItem* item = (BListItem*)items.ItemAt( i );
490                 if ( RemoveItem( item ) )
491                         delete item;
492         }
493 }
494
495 /*****************************************************************************
496  * DragSortableListView::RemoveSelected
497  *****************************************************************************/
498 void
499 DragSortableListView::RemoveSelected()
500 {
501         BList items;
502         for ( int32 i = 0; BListItem* item = ItemAt( CurrentSelection( i ) ); i++ )
503                 items.AddItem( (void*)item );
504         RemoveItemList( items );
505 }
506
507 /*****************************************************************************
508  * DragSortableListView::_SetDropAnticipationRect
509  *****************************************************************************/
510 void
511 DragSortableListView::_SetDropAnticipationRect( BRect r )
512 {
513         if ( fDropRect != r )
514         {
515                 if ( fDropRect.IsValid() )
516                         Invalidate( fDropRect );
517                 fDropRect = r;
518                 if ( fDropRect.IsValid() )
519                         Invalidate( fDropRect );
520         }
521 }
522
523 /*****************************************************************************
524  * DragSortableListView::_SetDropAnticipationRect
525  *****************************************************************************/
526 void
527 DragSortableListView::_SetDropIndex( int32 index )
528 {
529         if ( fDropIndex != index )
530         {
531                 fDropIndex = index;
532                 if ( fDropIndex == -1 )
533                         _SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
534                 else
535                 {
536                         int32 count = CountItems();
537                         if ( fDropIndex == count )
538                         {
539                                 BRect r;
540                                 if ( BListItem* item = ItemAt( count - 1 ) )
541                                 {
542                                         r = ItemFrame( count - 1 );
543                                         r.top = r.bottom + 1.0;
544                                         r.bottom = r.top + 1.0;
545                                 }
546                                 else
547                                 {
548                                         r = Bounds();
549                                         r.bottom--;     // compensate for scrollbars moved slightly out of window
550                                 }
551                                 _SetDropAnticipationRect( r );
552                         }
553                         else
554                         {
555                                 BRect r = ItemFrame( fDropIndex );
556                                 r.bottom = r.top + 1.0;
557                                 _SetDropAnticipationRect( r );
558                         }
559                 }
560         }
561 }
562
563 /*****************************************************************************
564  * DragSortableListView::_RemoveDropAnticipationRect
565  *****************************************************************************/
566 void
567 DragSortableListView::_RemoveDropAnticipationRect()
568 {
569         _SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
570         _SetDropIndex( -1 );
571 }
572
573
574 /*****************************************************************************
575  * PlaylistView class
576  *****************************************************************************/
577 PlaylistView::PlaylistView( BRect frame, InterfaceWindow* mainWindow,
578                             VlcWrapper* wrapper,
579                             BMessage* selectionChangeMessage )
580         : DragSortableListView( frame, "playlist listview",
581                                                         B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES,
582                                                         B_WILL_DRAW | B_NAVIGABLE | B_PULSE_NEEDED
583                                                         | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE ),
584           fCurrentIndex( -1 ),
585           fPlaying( false ),
586           fMainWindow( mainWindow ),
587           fSelectionChangeMessage( selectionChangeMessage ),
588           fVlcWrapper( wrapper )
589 {
590 }
591
592 PlaylistView::~PlaylistView()
593 {
594         delete fSelectionChangeMessage;
595 }
596
597 /*****************************************************************************
598  * PlaylistView::AttachedToWindow
599  *****************************************************************************/
600 void
601 PlaylistView::AttachedToWindow()
602 {
603         // get pulse message every two frames
604         Window()->SetPulseRate( 80000 );
605 }
606
607 /*****************************************************************************
608  * PlaylistView::MessageReceived
609  *****************************************************************************/
610 void
611 PlaylistView::MessageReceived( BMessage* message)
612 {
613         switch ( message->what )
614         {
615                 case B_SIMPLE_DATA:
616                         if ( message->HasPointer( "list" ) )
617                         {
618                                 // message comes from ourself
619                                 DragSortableListView::MessageReceived( message );
620                         }
621                         else
622                         {
623                                 // message comes from another app (for example Tracker)
624                                 message->AddInt32( "drop index", fDropIndex );
625                                 fMainWindow->PostMessage( message, fMainWindow );
626                         }
627                         break;
628                 default:
629                         DragSortableListView::MessageReceived( message );
630                         break;
631         }
632 }
633
634 /*****************************************************************************
635  * PlaylistView::MouseDown
636  *****************************************************************************/
637 void
638 PlaylistView::MouseDown( BPoint where )
639 {
640         int32 clicks = 1;
641         Window()->CurrentMessage()->FindInt32( "clicks", &clicks );
642         bool handled = false;
643         for ( int32 i = 0; PlaylistItem* item = (PlaylistItem*)ItemAt( i ); i++ )
644         {
645                 BRect r = ItemFrame( i );
646                 if ( r.Contains( where ) )
647                 {
648                         if ( clicks == 2 )
649                         {
650                                 fVlcWrapper->PlaylistJumpTo( i );
651                                 handled = true;
652                         }
653                         else if ( i == fCurrentIndex )
654                         {
655                                 r.right = r.left + TEXT_OFFSET;
656                                 if ( r.Contains ( where ) )
657                                 {
658                                         fMainWindow->PostMessage( PAUSE_PLAYBACK );
659                                         InvalidateItem( i );
660                                         handled = true;
661                                 }
662                         }
663                         break;
664                 }
665         }
666         if ( !handled )
667                 DragSortableListView::MouseDown(where);
668 }
669
670 /*****************************************************************************
671  * PlaylistView::KeyDown
672  *****************************************************************************/
673 void
674 PlaylistView::KeyDown( const char* bytes, int32 numBytes )
675 {
676         if ( numBytes < 1 )
677                 return;
678                 
679         if ( ( bytes[0] == B_BACKSPACE ) || ( bytes[0] == B_DELETE ) )
680         {
681                 RemoveSelected();
682         }
683         DragSortableListView::KeyDown( bytes, numBytes );
684 }
685
686 /*****************************************************************************
687  * PlaylistView::Pulse
688  *****************************************************************************/
689 void
690 PlaylistView::Pulse()
691 {
692         if ( fMainWindow->IsStopped() )
693                 SetPlaying( false );
694 }
695
696 /*****************************************************************************
697  * PlaylistView::SelectionChanged
698  *****************************************************************************/
699 void
700 PlaylistView::SelectionChanged()
701 {
702         BLooper* looper = Looper();
703         if ( fSelectionChangeMessage && looper )
704         {
705                 BMessage message( *fSelectionChangeMessage );
706                 looper->PostMessage( &message );
707         }
708 }
709
710
711 /*****************************************************************************
712  * PlaylistView::MoveItems
713  *****************************************************************************/
714 void
715 PlaylistView::MoveItems( BList& items, int32 index )
716 {
717         DeselectAll();
718         // we remove the items while we look at them, the insertion index is decreased
719         // when the items index is lower, so that we insert at the right spot after
720         // removal
721         if ( fVlcWrapper->PlaylistLock() )
722         {
723                 BList removedItems;
724                 BList removeItems;
725                 int32 count = items.CountItems();
726                 int32 indexOriginal = index;
727                 // remember currently playing item
728                 int32 currentIndex, size;
729                 fVlcWrapper->GetPlaylistInfo( currentIndex, size );
730                 BListItem* playingItem = ItemAt( currentIndex );
731                 // collect item pointers for removal by index
732                 for ( int32 i = 0; i < count; i++ )
733                 {
734                         int32 removeIndex = IndexOf( (BListItem*)items.ItemAt( i ) );
735                         void* item = fVlcWrapper->PlaylistItemAt( removeIndex );
736                         if ( item && removeItems.AddItem( item ) )
737                         {
738                                 if ( removeIndex < index )
739                                         index--;
740                         }
741                         // else ??? -> blow up
742                 }
743                 // actually remove items using pointers
744                 for ( int32 i = 0; i < count; i++ )
745                 {
746                         void* item = fVlcWrapper->PlaylistRemoveItem( removeItems.ItemAt( i ) );
747                         if ( item && !removedItems.AddItem( item ) )
748                                 free( item );
749                 }
750                 // add items at index
751                 for ( int32 i = 0; void* item = removedItems.ItemAt( i ); i++ )
752                 {
753                         if ( fVlcWrapper->PlaylistAddItem( item, index ) )
754                                 // next items will be inserted after this one
755                                 index++;
756                         else
757                                 free( item );
758                 }
759                 // update GUI
760                 DragSortableListView::MoveItems( items, indexOriginal );
761                 // restore currently playing item
762                 for ( int32 i = 0; BListItem* item = ItemAt( i ); i++ )
763                 {
764                         if ( item == playingItem )
765                         {
766                                 fVlcWrapper->PlaylistSetPlaying( i );
767                                 SetCurrent( i );
768                                 break;
769                         }
770                 }
771                 fVlcWrapper->PlaylistUnlock();
772         }
773 }
774
775 /*****************************************************************************
776  * PlaylistView::CopyItems
777  *****************************************************************************/
778 void
779 PlaylistView::CopyItems( BList& items, int32 toIndex )
780 {
781         DeselectAll();
782         // we remove the items while we look at them, the insertion index is decreased
783         // when the items index is lower, so that we insert at the right spot after
784         // removal
785         if ( fVlcWrapper->PlaylistLock() )
786         {
787                 BList clonedItems;
788                 int32 count = items.CountItems();
789                 // remember currently playing item
790                 int32 currentIndex, size;
791                 fVlcWrapper->GetPlaylistInfo( currentIndex, size );
792                 BListItem* playingItem = ItemAt( currentIndex );
793                 // collect cloned item pointers
794                 for ( int32 i = 0; i < count; i++ )
795                 {
796                         int32 index = IndexOf( (BListItem*)items.ItemAt( i ) );
797                         void* item = fVlcWrapper->PlaylistItemAt( index );
798                         void* cloned = fVlcWrapper->PlaylistCloneItem( item );
799                         if ( cloned && !clonedItems.AddItem( cloned ) )
800                                 free( cloned );
801                         
802                 }
803                 // add cloned items at index
804                 int32 index = toIndex;
805                 for ( int32 i = 0; void* item = clonedItems.ItemAt( i ); i++ )
806                 {
807                         if ( fVlcWrapper->PlaylistAddItem( item, index ) )
808                                 // next items will be inserted after this one
809                                 index++;
810                         else
811                                 free( item );
812                 }
813                 // update GUI
814                 DragSortableListView::CopyItems( items, toIndex );
815                 // restore currently playing item
816                 for ( int32 i = 0; BListItem* item = ItemAt( i ); i++ )
817                 {
818                         if ( item == playingItem )
819                         {
820                                 fVlcWrapper->PlaylistSetPlaying( i );
821                                 SetCurrent( i );
822                                 break;
823                         }
824                 }
825                 fVlcWrapper->PlaylistUnlock();
826         }
827 }
828
829 /*****************************************************************************
830  * PlaylistView::RemoveItemList
831  *****************************************************************************/
832 void
833 PlaylistView::RemoveItemList( BList& items )
834 {
835         if ( fVlcWrapper->PlaylistLock() )
836         {
837                 // collect item pointers for removal
838                 BList removeItems;
839                 int32 count = items.CountItems();
840                 for ( int32 i = 0; i < count; i++ )
841                 {
842                         int32 index = IndexOf( (BListItem*)items.ItemAt( i ) );
843                         void* item = fVlcWrapper->PlaylistItemAt( index );
844                         if ( item && !removeItems.AddItem( item ) )
845                                 free( item );
846                 }
847                 // remove items from playlist
848                 count = removeItems.CountItems();
849                 for ( int32 i = 0; void* item = removeItems.ItemAt( i ); i++ )
850                 {
851                         fVlcWrapper->PlaylistRemoveItem( item );
852                 }
853                 // update GUI
854                 DragSortableListView::RemoveItemList( items );
855                 fVlcWrapper->PlaylistUnlock();
856         }
857 }
858
859 /*****************************************************************************
860  * PlaylistView::CloneItem
861  *****************************************************************************/
862 BListItem*
863 PlaylistView::CloneItem( int32 atIndex ) const
864 {
865         BListItem* clone = NULL;
866         if ( PlaylistItem* item = dynamic_cast<PlaylistItem*>( ItemAt( atIndex ) ) )
867                 clone = new PlaylistItem( item->Text() );
868         return clone;
869 }
870
871 /*****************************************************************************
872  * PlaylistView::DrawListItem
873  *****************************************************************************/
874 void
875 PlaylistView::DrawListItem( BView* owner, int32 index, BRect frame ) const
876 {
877         if ( PlaylistItem* item = dynamic_cast<PlaylistItem*>( ItemAt( index ) ) )
878                 item->Draw( owner,  frame, index % 2, index == fCurrentIndex, fPlaying );
879 }
880
881 /*****************************************************************************
882  * PlaylistView::MakeDragMessage
883  *****************************************************************************/
884 void
885 PlaylistView::MakeDragMessage( BMessage* message ) const
886 {
887         if ( message )
888         {
889                 message->AddPointer( "list", (void*)this );
890                 int32 index;
891                 for ( int32 i = 0; ( index = CurrentSelection( i ) ) >= 0; i++ )
892                         message->AddInt32( "index", index );
893                         // TODO: add refs to message (inter application communication)
894         }
895 }
896
897 /*****************************************************************************
898  * PlaylistView::SetCurrent
899  *****************************************************************************/
900 void
901 PlaylistView::SetCurrent( int32 index )
902 {
903         if ( fCurrentIndex != index )
904         {
905                 InvalidateItem( fCurrentIndex );
906                 fCurrentIndex = index;
907                 InvalidateItem( fCurrentIndex );
908         }
909 }
910
911 /*****************************************************************************
912  * PlaylistView::SetPlaying
913  *****************************************************************************/
914 void
915 PlaylistView::SetPlaying( bool playing )
916 {
917         if ( fPlaying != playing )
918         {
919                 fPlaying = playing;
920                 InvalidateItem( fCurrentIndex );
921         }
922 }
923
924 /*****************************************************************************
925  * PlaylistView::SetPlaying
926  *****************************************************************************/
927 void
928 PlaylistView::RebuildList()
929 {
930         // remove all items
931         int32 count = CountItems();
932         while ( BListItem* item = RemoveItem( --count ) )
933                 delete item;
934         
935         // rebuild listview from VLC's playlist
936         for ( int i = 0; i < fVlcWrapper->PlaylistSize(); i++ )
937                 AddItem( new PlaylistItem( fVlcWrapper->PlaylistItemName( i ) ) );
938 }