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