]> git.sesse.net Git - vlc/blob - modules/gui/beos/ListViews.cpp
cosmetic fix (drop mark would reappear if SHIFT was pressed)
[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.4 2003/02/02 11:29:12 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                 fDragMessageCopy.what = 0;
374                 BListView::MouseMoved(where, transit, msg);
375         }
376 }
377
378 /*****************************************************************************
379  * DragSortableListView::MouseUp
380  *****************************************************************************/
381 void
382 DragSortableListView::MouseUp( BPoint where )
383 {
384         // remove drop mark
385         _SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
386         fDragMessageCopy.what = 0;
387         BListView::MouseUp( where );
388 }
389
390 /*****************************************************************************
391  * DragSortableListView::DrawItem
392  *****************************************************************************/
393 void
394 DragSortableListView::DrawItem( BListItem *item, BRect itemFrame, bool complete )
395 {
396         DrawListItem( this, IndexOf( item ), itemFrame );
397 }
398
399 /*****************************************************************************
400  * DragSortableListView::ModifiersChaned
401  *****************************************************************************/
402 void
403 DragSortableListView::ModifiersChanged()
404 {
405         BPoint where;
406         uint32 buttons;
407         GetMouse( &where, &buttons, false );
408         uint32 transit = Bounds().Contains( where ) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
409         MouseMoved( where, transit, &fDragMessageCopy );
410 }
411
412 /*****************************************************************************
413  * DragSortableListView::MoveItems
414  *****************************************************************************/
415 void
416 DragSortableListView::MoveItems( BList& items, int32 index )
417 {
418         DeselectAll();
419         // we remove the items while we look at them, the insertion index is decreased
420         // when the items index is lower, so that we insert at the right spot after
421         // removal
422         BList removedItems;
423         int32 count = items.CountItems();
424         for ( int32 i = 0; i < count; i++ )
425         {
426                 BListItem* item = (BListItem*)items.ItemAt( i );
427                 int32 removeIndex = IndexOf( item );
428                 if ( RemoveItem( item ) && removedItems.AddItem( (void*)item ) )
429                 {
430                         if ( removeIndex < index )
431                                 index--;
432                 }
433                 // else ??? -> blow up
434         }
435         for ( int32 i = 0; BListItem* item = (BListItem*)removedItems.ItemAt( i ); i++ )
436         {
437                 if ( AddItem( item, index ) )
438                 {
439                         // after we're done, the newly inserted items will be selected
440                         Select( index, true );
441                         // next items will be inserted after this one
442                         index++;
443                 }
444                 else
445                         delete item;
446         }
447 }
448
449 /*****************************************************************************
450  * DragSortableListView::CopyItems
451  *****************************************************************************/
452 void
453 DragSortableListView::CopyItems( BList& items, int32 index )
454 {
455         DeselectAll();
456         // by inserting the items after we copied all items first, we avoid
457         // cloning an item we already inserted and messing everything up
458         // in other words, don't touch the list before we know which items
459         // need to be cloned
460         BList clonedItems;
461         int32 count = items.CountItems();
462         for ( int32 i = 0; i < count; i++ )
463         {
464                 BListItem* item = CloneItem( IndexOf( (BListItem*)items.ItemAt( i ) ) );
465                 if ( item && !clonedItems.AddItem( (void*)item ) )
466                         delete item;
467         }
468         for ( int32 i = 0; BListItem* item = (BListItem*)clonedItems.ItemAt( i ); i++ )
469         {
470                 if ( AddItem( item, index ) )
471                 {
472                         // after we're done, the newly inserted items will be selected
473                         Select( index, true );
474                         // next items will be inserted after this one
475                         index++;
476                 }
477                 else
478                         delete item;
479         }
480 }
481
482 /*****************************************************************************
483  * DragSortableListView::RemoveItemList
484  *****************************************************************************/
485 void
486 DragSortableListView::RemoveItemList( BList& items )
487 {
488         int32 count = items.CountItems();
489         for ( int32 i = 0; i < count; i++ )
490         {
491                 BListItem* item = (BListItem*)items.ItemAt( i );
492                 if ( RemoveItem( item ) )
493                         delete item;
494         }
495 }
496
497 /*****************************************************************************
498  * DragSortableListView::RemoveSelected
499  *****************************************************************************/
500 void
501 DragSortableListView::RemoveSelected()
502 {
503         BList items;
504         for ( int32 i = 0; BListItem* item = ItemAt( CurrentSelection( i ) ); i++ )
505                 items.AddItem( (void*)item );
506         RemoveItemList( items );
507 }
508
509 /*****************************************************************************
510  * DragSortableListView::_SetDropAnticipationRect
511  *****************************************************************************/
512 void
513 DragSortableListView::_SetDropAnticipationRect( BRect r )
514 {
515         if ( fDropRect != r )
516         {
517                 if ( fDropRect.IsValid() )
518                         Invalidate( fDropRect );
519                 fDropRect = r;
520                 if ( fDropRect.IsValid() )
521                         Invalidate( fDropRect );
522         }
523 }
524
525 /*****************************************************************************
526  * DragSortableListView::_SetDropAnticipationRect
527  *****************************************************************************/
528 void
529 DragSortableListView::_SetDropIndex( int32 index )
530 {
531         if ( fDropIndex != index )
532         {
533                 fDropIndex = index;
534                 if ( fDropIndex == -1 )
535                         _SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
536                 else
537                 {
538                         int32 count = CountItems();
539                         if ( fDropIndex == count )
540                         {
541                                 BRect r;
542                                 if ( BListItem* item = ItemAt( count - 1 ) )
543                                 {
544                                         r = ItemFrame( count - 1 );
545                                         r.top = r.bottom + 1.0;
546                                         r.bottom = r.top + 1.0;
547                                 }
548                                 else
549                                 {
550                                         r = Bounds();
551                                         r.bottom--;     // compensate for scrollbars moved slightly out of window
552                                 }
553                                 _SetDropAnticipationRect( r );
554                         }
555                         else
556                         {
557                                 BRect r = ItemFrame( fDropIndex );
558                                 r.bottom = r.top + 1.0;
559                                 _SetDropAnticipationRect( r );
560                         }
561                 }
562         }
563 }
564
565 /*****************************************************************************
566  * DragSortableListView::_RemoveDropAnticipationRect
567  *****************************************************************************/
568 void
569 DragSortableListView::_RemoveDropAnticipationRect()
570 {
571         _SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
572         _SetDropIndex( -1 );
573 }
574
575
576 /*****************************************************************************
577  * PlaylistView class
578  *****************************************************************************/
579 PlaylistView::PlaylistView( BRect frame, InterfaceWindow* mainWindow,
580                             VlcWrapper* wrapper,
581                             BMessage* selectionChangeMessage )
582         : DragSortableListView( frame, "playlist listview",
583                                                         B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES,
584                                                         B_WILL_DRAW | B_NAVIGABLE | B_PULSE_NEEDED
585                                                         | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE ),
586           fCurrentIndex( -1 ),
587           fPlaying( false ),
588           fMainWindow( mainWindow ),
589           fSelectionChangeMessage( selectionChangeMessage ),
590           fVlcWrapper( wrapper )
591 {
592 }
593
594 PlaylistView::~PlaylistView()
595 {
596         delete fSelectionChangeMessage;
597 }
598
599 /*****************************************************************************
600  * PlaylistView::AttachedToWindow
601  *****************************************************************************/
602 void
603 PlaylistView::AttachedToWindow()
604 {
605         // get pulse message every two frames
606         Window()->SetPulseRate( 80000 );
607 }
608
609 /*****************************************************************************
610  * PlaylistView::MessageReceived
611  *****************************************************************************/
612 void
613 PlaylistView::MessageReceived( BMessage* message)
614 {
615         switch ( message->what )
616         {
617                 case B_SIMPLE_DATA:
618                         if ( message->HasPointer( "list" ) )
619                         {
620                                 // message comes from ourself
621                                 DragSortableListView::MessageReceived( message );
622                         }
623                         else
624                         {
625                                 // message comes from another app (for example Tracker)
626                                 message->AddInt32( "drop index", fDropIndex );
627                                 fMainWindow->PostMessage( message, fMainWindow );
628                         }
629                         break;
630                 default:
631                         DragSortableListView::MessageReceived( message );
632                         break;
633         }
634 }
635
636 /*****************************************************************************
637  * PlaylistView::MouseDown
638  *****************************************************************************/
639 void
640 PlaylistView::MouseDown( BPoint where )
641 {
642         int32 clicks = 1;
643         Window()->CurrentMessage()->FindInt32( "clicks", &clicks );
644         bool handled = false;
645         for ( int32 i = 0; PlaylistItem* item = (PlaylistItem*)ItemAt( i ); i++ )
646         {
647                 BRect r = ItemFrame( i );
648                 if ( r.Contains( where ) )
649                 {
650                         if ( clicks == 2 )
651                         {
652                                 fVlcWrapper->PlaylistJumpTo( i );
653                                 handled = true;
654                         }
655                         else if ( i == fCurrentIndex )
656                         {
657                                 r.right = r.left + TEXT_OFFSET;
658                                 if ( r.Contains ( where ) )
659                                 {
660                                         fMainWindow->PostMessage( PAUSE_PLAYBACK );
661                                         InvalidateItem( i );
662                                         handled = true;
663                                 }
664                         }
665                         break;
666                 }
667         }
668         if ( !handled )
669                 DragSortableListView::MouseDown(where);
670 }
671
672 /*****************************************************************************
673  * PlaylistView::KeyDown
674  *****************************************************************************/
675 void
676 PlaylistView::KeyDown( const char* bytes, int32 numBytes )
677 {
678         if ( numBytes < 1 )
679                 return;
680                 
681         if ( ( bytes[0] == B_BACKSPACE ) || ( bytes[0] == B_DELETE ) )
682         {
683                 RemoveSelected();
684         }
685         DragSortableListView::KeyDown( bytes, numBytes );
686 }
687
688 /*****************************************************************************
689  * PlaylistView::Pulse
690  *****************************************************************************/
691 void
692 PlaylistView::Pulse()
693 {
694         if ( fMainWindow->IsStopped() )
695                 SetPlaying( false );
696 }
697
698 /*****************************************************************************
699  * PlaylistView::SelectionChanged
700  *****************************************************************************/
701 void
702 PlaylistView::SelectionChanged()
703 {
704         BLooper* looper = Looper();
705         if ( fSelectionChangeMessage && looper )
706         {
707                 BMessage message( *fSelectionChangeMessage );
708                 looper->PostMessage( &message );
709         }
710 }
711
712
713 /*****************************************************************************
714  * PlaylistView::MoveItems
715  *****************************************************************************/
716 void
717 PlaylistView::MoveItems( BList& items, int32 index )
718 {
719         DeselectAll();
720         // we remove the items while we look at them, the insertion index is decreased
721         // when the items index is lower, so that we insert at the right spot after
722         // removal
723         if ( fVlcWrapper->PlaylistLock() )
724         {
725                 BList removedItems;
726                 BList removeItems;
727                 int32 count = items.CountItems();
728                 int32 indexOriginal = index;
729                 // remember currently playing item
730                 int32 currentIndex, size;
731                 fVlcWrapper->GetPlaylistInfo( currentIndex, size );
732                 BListItem* playingItem = ItemAt( currentIndex );
733                 // collect item pointers for removal by index
734                 for ( int32 i = 0; i < count; i++ )
735                 {
736                         int32 removeIndex = IndexOf( (BListItem*)items.ItemAt( i ) );
737                         void* item = fVlcWrapper->PlaylistItemAt( removeIndex );
738                         if ( item && removeItems.AddItem( item ) )
739                         {
740                                 if ( removeIndex < index )
741                                         index--;
742                         }
743                         // else ??? -> blow up
744                 }
745                 // actually remove items using pointers
746                 for ( int32 i = 0; i < count; i++ )
747                 {
748                         void* item = fVlcWrapper->PlaylistRemoveItem( removeItems.ItemAt( i ) );
749                         if ( item && !removedItems.AddItem( item ) )
750                                 free( item );
751                 }
752                 // add items at index
753                 for ( int32 i = 0; void* item = removedItems.ItemAt( i ); i++ )
754                 {
755                         if ( fVlcWrapper->PlaylistAddItem( item, index ) )
756                                 // next items will be inserted after this one
757                                 index++;
758                         else
759                                 free( item );
760                 }
761                 // update GUI
762                 DragSortableListView::MoveItems( items, indexOriginal );
763                 // restore currently playing item
764                 for ( int32 i = 0; BListItem* item = ItemAt( i ); i++ )
765                 {
766                         if ( item == playingItem )
767                         {
768                                 fVlcWrapper->PlaylistSetPlaying( i );
769                                 SetCurrent( i );
770                                 break;
771                         }
772                 }
773                 fVlcWrapper->PlaylistUnlock();
774         }
775 }
776
777 /*****************************************************************************
778  * PlaylistView::CopyItems
779  *****************************************************************************/
780 void
781 PlaylistView::CopyItems( BList& items, int32 toIndex )
782 {
783         DeselectAll();
784         // we remove the items while we look at them, the insertion index is decreased
785         // when the items index is lower, so that we insert at the right spot after
786         // removal
787         if ( fVlcWrapper->PlaylistLock() )
788         {
789                 BList clonedItems;
790                 int32 count = items.CountItems();
791                 // remember currently playing item
792                 int32 currentIndex, size;
793                 fVlcWrapper->GetPlaylistInfo( currentIndex, size );
794                 BListItem* playingItem = ItemAt( currentIndex );
795                 // collect cloned item pointers
796                 for ( int32 i = 0; i < count; i++ )
797                 {
798                         int32 index = IndexOf( (BListItem*)items.ItemAt( i ) );
799                         void* item = fVlcWrapper->PlaylistItemAt( index );
800                         void* cloned = fVlcWrapper->PlaylistCloneItem( item );
801                         if ( cloned && !clonedItems.AddItem( cloned ) )
802                                 free( cloned );
803                         
804                 }
805                 // add cloned items at index
806                 int32 index = toIndex;
807                 for ( int32 i = 0; void* item = clonedItems.ItemAt( i ); i++ )
808                 {
809                         if ( fVlcWrapper->PlaylistAddItem( item, index ) )
810                                 // next items will be inserted after this one
811                                 index++;
812                         else
813                                 free( item );
814                 }
815                 // update GUI
816                 DragSortableListView::CopyItems( items, toIndex );
817                 // restore currently playing item
818                 for ( int32 i = 0; BListItem* item = ItemAt( i ); i++ )
819                 {
820                         if ( item == playingItem )
821                         {
822                                 fVlcWrapper->PlaylistSetPlaying( i );
823                                 SetCurrent( i );
824                                 break;
825                         }
826                 }
827                 fVlcWrapper->PlaylistUnlock();
828         }
829 }
830
831 /*****************************************************************************
832  * PlaylistView::RemoveItemList
833  *****************************************************************************/
834 void
835 PlaylistView::RemoveItemList( BList& items )
836 {
837         if ( fVlcWrapper->PlaylistLock() )
838         {
839                 // collect item pointers for removal
840                 BList removeItems;
841                 int32 count = items.CountItems();
842                 for ( int32 i = 0; i < count; i++ )
843                 {
844                         int32 index = IndexOf( (BListItem*)items.ItemAt( i ) );
845                         void* item = fVlcWrapper->PlaylistItemAt( index );
846                         if ( item && !removeItems.AddItem( item ) )
847                                 free( item );
848                 }
849                 // remove items from playlist
850                 count = removeItems.CountItems();
851                 for ( int32 i = 0; void* item = removeItems.ItemAt( i ); i++ )
852                 {
853                         fVlcWrapper->PlaylistRemoveItem( item );
854                 }
855                 // update GUI
856                 DragSortableListView::RemoveItemList( items );
857                 fVlcWrapper->PlaylistUnlock();
858         }
859 }
860
861 /*****************************************************************************
862  * PlaylistView::CloneItem
863  *****************************************************************************/
864 BListItem*
865 PlaylistView::CloneItem( int32 atIndex ) const
866 {
867         BListItem* clone = NULL;
868         if ( PlaylistItem* item = dynamic_cast<PlaylistItem*>( ItemAt( atIndex ) ) )
869                 clone = new PlaylistItem( item->Text() );
870         return clone;
871 }
872
873 /*****************************************************************************
874  * PlaylistView::DrawListItem
875  *****************************************************************************/
876 void
877 PlaylistView::DrawListItem( BView* owner, int32 index, BRect frame ) const
878 {
879         if ( PlaylistItem* item = dynamic_cast<PlaylistItem*>( ItemAt( index ) ) )
880                 item->Draw( owner,  frame, index % 2, index == fCurrentIndex, fPlaying );
881 }
882
883 /*****************************************************************************
884  * PlaylistView::MakeDragMessage
885  *****************************************************************************/
886 void
887 PlaylistView::MakeDragMessage( BMessage* message ) const
888 {
889         if ( message )
890         {
891                 message->AddPointer( "list", (void*)this );
892                 int32 index;
893                 for ( int32 i = 0; ( index = CurrentSelection( i ) ) >= 0; i++ )
894                         message->AddInt32( "index", index );
895                         // TODO: add refs to message (inter application communication)
896         }
897 }
898
899 /*****************************************************************************
900  * PlaylistView::SetCurrent
901  *****************************************************************************/
902 void
903 PlaylistView::SetCurrent( int32 index )
904 {
905         if ( fCurrentIndex != index )
906         {
907                 InvalidateItem( fCurrentIndex );
908                 fCurrentIndex = index;
909                 InvalidateItem( fCurrentIndex );
910         }
911 }
912
913 /*****************************************************************************
914  * PlaylistView::SetPlaying
915  *****************************************************************************/
916 void
917 PlaylistView::SetPlaying( bool playing )
918 {
919         if ( fPlaying != playing )
920         {
921                 fPlaying = playing;
922                 InvalidateItem( fCurrentIndex );
923         }
924 }
925
926 /*****************************************************************************
927  * PlaylistView::SetPlaying
928  *****************************************************************************/
929 void
930 PlaylistView::RebuildList()
931 {
932         // remove all items
933         int32 count = CountItems();
934         while ( BListItem* item = RemoveItem( --count ) )
935                 delete item;
936         
937         // rebuild listview from VLC's playlist
938         for ( int i = 0; i < fVlcWrapper->PlaylistSize(); i++ )
939                 AddItem( new PlaylistItem( fVlcWrapper->PlaylistItemName( i ) ) );
940 }