]> git.sesse.net Git - vlc/blob - modules/gui/beos/ListViews.cpp
Attempt to port new BeOS features from the stable branch.
[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.1 2002/09/30 18:30:27 titer 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
26 #include <Bitmap.h>
27 #include <String.h>
28
29 /* VLC headers */
30 #include <vlc/vlc.h>
31 #include <vlc/intf.h>
32
33 #include "VlcWrapper.h"
34 #include "InterfaceWindow.h"
35 #include "ListViews.h"
36 #include "MsgVals.h"
37
38 #define MAX_DRAG_HEIGHT         200.0
39 #define ALPHA                           170
40 #define TEXT_OFFSET                     20.0
41
42 /*****************************************************************************
43  * PlaylistItem class
44  *****************************************************************************/
45 PlaylistItem::PlaylistItem( const char *name )
46         : BStringItem( name )
47 {
48 }
49
50 PlaylistItem::~PlaylistItem()
51 {
52 }
53
54 /*****************************************************************************
55  * PlaylistItem::DrawItem
56  *****************************************************************************/
57 void
58 PlaylistItem::Draw( BView *owner, BRect frame, bool tintedLine,
59                                         bool active, bool playing )
60 {
61         rgb_color color = (rgb_color){ 255, 255, 255, 255 };
62         if ( tintedLine )
63                 color = tint_color( color, 1.04 );
64         // background
65         if ( IsSelected() )
66                 color = tint_color( color, B_DARKEN_2_TINT );
67         owner->SetLowColor( color );
68         owner->FillRect( frame, B_SOLID_LOW );
69         // label
70         owner->SetHighColor( 0, 0, 0, 255 );
71         font_height fh;
72         owner->GetFontHeight( &fh );
73         BString truncatedString( Text() );
74         owner->TruncateString( &truncatedString, B_TRUNCATE_MIDDLE,
75                                                    frame.Width() - TEXT_OFFSET - 4.0 );
76         owner->DrawString( truncatedString.String(),
77                                            BPoint( frame.left + TEXT_OFFSET,
78                                                            frame.top + fh.ascent + 1.0 ) );
79         // playmark
80         if ( active )
81         {
82                 rgb_color black = (rgb_color){ 0, 0, 0, 255 };
83                 rgb_color green = (rgb_color){ 0, 255, 0, 255 };
84                 BRect r( 0.0, 0.0, 10.0, 10.0 );
85                 r.OffsetTo( frame.left + 4.0,
86                                         ceilf( ( frame.top + frame.bottom ) / 2.0 ) - 5.0 );
87                 if ( !playing )
88                         green = tint_color( color, B_DARKEN_1_TINT );
89                 rgb_color lightGreen = tint_color( green, B_LIGHTEN_2_TINT );
90                 rgb_color darkGreen = tint_color( green, B_DARKEN_2_TINT );
91                 BPoint arrow[3];
92                 arrow[0] = r.LeftTop();
93                 arrow[1] = r.LeftBottom();
94                 arrow[2].x = r.right;
95                 arrow[2].y = ( r.top + r.bottom ) / 2.0;
96                 owner->BeginLineArray( 6 );
97                         // black outline
98                         owner->AddLine( arrow[0], arrow[1], black );
99                         owner->AddLine( BPoint( arrow[1].x + 1.0, arrow[1].y - 1.0 ),
100                                                         arrow[2], black );
101                         owner->AddLine( arrow[0], arrow[2], black );
102                         // inset arrow
103                         arrow[0].x += 1.0;
104                         arrow[0].y += 2.0;
105                         arrow[1].x += 1.0;
106                         arrow[1].y -= 2.0;
107                         arrow[2].x -= 2.0;
108                         // highlights and shadow
109                         owner->AddLine( arrow[1], arrow[2], darkGreen );
110                         owner->AddLine( arrow[0], arrow[2], lightGreen );
111                         owner->AddLine( arrow[0], arrow[1], lightGreen );
112                 owner->EndLineArray();
113                 // fill green
114                 arrow[0].x += 1.0;
115                 arrow[0].y += 1.0;
116                 arrow[1].x += 1.0;
117                 arrow[1].y -= 1.0;
118                 arrow[2].x -= 2.0;
119                 owner->SetHighColor( green );
120                 owner->FillPolygon( arrow, 3 );
121         }
122 }
123
124 /*****************************************************************************
125  * DragSortableListView class
126  *****************************************************************************/
127 DragSortableListView::DragSortableListView( BRect frame, const char* name,
128                                                                                         list_view_type type, uint32 resizingMode,
129                                                                                         uint32 flags )
130         : BListView( frame, name, type, resizingMode, flags ),
131           fDropIndex( -1 )
132 {
133         SetViewColor( B_TRANSPARENT_32_BIT );
134 }
135
136 DragSortableListView::~DragSortableListView()
137 {
138 }
139
140 /*****************************************************************************
141  * DragSortableListView::Draw
142  *****************************************************************************/
143 void
144 DragSortableListView::Draw( BRect updateRect )
145 {
146         int32 firstIndex = IndexOf( updateRect.LeftTop() );
147         int32 lastIndex = IndexOf( updateRect.RightBottom() );
148         if ( firstIndex >= 0 )
149         {
150                 if ( lastIndex < firstIndex )
151                         lastIndex = CountItems() - 1;
152                 // update rect contains items
153                 BRect r( updateRect );
154                 for ( int32 i = firstIndex; i <= lastIndex; i++)
155                 {
156                         r = ItemFrame( i );
157                         DrawListItem( this, i, r );
158                 }
159                 updateRect.top = r.bottom + 1.0;
160                 if ( updateRect.IsValid() )
161                 {
162                         SetLowColor( 255, 255, 255, 255 );
163                         FillRect( updateRect, B_SOLID_LOW );
164                 }
165         }
166         else
167         {
168                 SetLowColor( 255, 255, 255, 255 );
169                 FillRect( updateRect, B_SOLID_LOW );
170         }
171 }
172
173 /*****************************************************************************
174  * DragSortableListView::InitiateDrag
175  *****************************************************************************/
176 bool
177 DragSortableListView::InitiateDrag( BPoint point, int32 index, bool )
178 {
179 return false;
180         bool success = false;
181         BListItem* item = ItemAt( CurrentSelection( 0 ) );
182         if ( !item )
183         {
184                 // workarround a timing problem
185                 Select( index );
186                 item = ItemAt( index );
187         }
188         if ( item )
189         {
190                 // create drag message
191                 BMessage msg( B_SIMPLE_DATA );
192                 MakeDragMessage( &msg );
193                 // figure out drag rect
194                 float width = Bounds().Width();
195                 BRect dragRect(0.0, 0.0, width, -1.0);
196                 // figure out, how many items fit into our bitmap
197                 int32 numItems;
198                 bool fade = false;
199                 for (numItems = 0; BListItem* item = ItemAt( CurrentSelection( numItems ) ); numItems++) {
200                         dragRect.bottom += item->Height();
201                         if ( dragRect.Height() > MAX_DRAG_HEIGHT ) {
202                                 fade = true;
203                                 dragRect.bottom = MAX_DRAG_HEIGHT;
204                                 numItems++;
205                                 break;
206                         }
207                 }
208                 BBitmap* dragBitmap = new BBitmap( dragRect, B_RGB32, true );
209                 if ( dragBitmap && dragBitmap->IsValid() ) {
210                         if ( BView *v = new BView( dragBitmap->Bounds(), "helper", B_FOLLOW_NONE, B_WILL_DRAW ) ) {
211                                 dragBitmap->AddChild( v );
212                                 dragBitmap->Lock();
213                                 BRect itemBounds( dragRect) ;
214                                 itemBounds.bottom = 0.0;
215                                 // let all selected items, that fit into our drag_bitmap, draw
216                                 for ( int32 i = 0; i < numItems; i++ ) {
217                                         int32 index = CurrentSelection( i );
218                                         BListItem* item = ItemAt( index );
219                                         itemBounds.bottom = itemBounds.top + item->Height() - 1.0;
220                                         if ( itemBounds.bottom > dragRect.bottom )
221                                                 itemBounds.bottom = dragRect.bottom;
222                                         DrawListItem( v, index, itemBounds );
223                                         itemBounds.top = itemBounds.bottom + 1.0;
224                                 }
225                                 // make a black frame arround the edge
226                                 v->SetHighColor( 0, 0, 0, 255 );
227                                 v->StrokeRect( v->Bounds() );
228                                 v->Sync();
229         
230                                 uint8 *bits = (uint8 *)dragBitmap->Bits();
231                                 int32 height = (int32)dragBitmap->Bounds().Height() + 1;
232                                 int32 width = (int32)dragBitmap->Bounds().Width() + 1;
233                                 int32 bpr = dragBitmap->BytesPerRow();
234         
235                                 if (fade) {
236                                         for ( int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr ) {
237                                                 uint8 *line = bits + 3;
238                                                 for (uint8 *end = line + 4 * width; line < end; line += 4)
239                                                         *line = ALPHA;
240                                         }
241                                         for ( int32 y = height - ALPHA / 2; y < height; y++, bits += bpr ) {
242                                                 uint8 *line = bits + 3;
243                                                 for (uint8 *end = line + 4 * width; line < end; line += 4)
244                                                         *line = (height - y) << 1;
245                                         }
246                                 } else {
247                                         for ( int32 y = 0; y < height; y++, bits += bpr ) {
248                                                 uint8 *line = bits + 3;
249                                                 for (uint8 *end = line + 4 * width; line < end; line += 4)
250                                                         *line = ALPHA;
251                                         }
252                                 }
253                                 dragBitmap->Unlock();
254                                 success = true;
255                         }
256                 }
257                 if (success)
258                         DragMessage( &msg, dragBitmap, B_OP_ALPHA, BPoint( 0.0, 0.0 ) );
259                 else {
260                         delete dragBitmap;
261                         DragMessage( &msg, dragRect.OffsetToCopy( point ), this );
262                 }
263         }
264         return success;
265 }
266
267 /*****************************************************************************
268  * DragSortableListView::WindowActivated
269  *****************************************************************************/
270 void
271 DragSortableListView::WindowActivated( bool active )
272 {
273         // workarround for buggy focus indication of BScrollView
274         if ( BView* view = Parent() )
275                 view->Invalidate();
276 }
277
278 /*****************************************************************************
279  * DragSortableListView::MessageReceived
280  *****************************************************************************/
281 void
282 DragSortableListView::MessageReceived(BMessage* message)
283 {
284         BListItem *item = NULL;
285         DragSortableListView *list = NULL;
286         if ( message->FindPointer( "list", (void **)&list ) == B_OK
287                  && list == this )
288         {
289                 int32 count = CountItems();
290                 if ( fDropIndex < 0 || fDropIndex > count )
291                         fDropIndex = count;
292                 bool copy = ( modifiers() & B_SHIFT_KEY );
293                 for ( int32 i = 0; message->FindPointer( "item", i, (void **)&item ) == B_OK; i++ )
294                 {
295                         
296                         if ( HasItem( item ) )
297                         {
298                                 BListItem* itemToAdd = NULL;
299                                 int32 index = IndexOf( item );
300                                 if ( copy )
301                                 {
302                                         // add cloned item
303                                         itemToAdd = CloneItem( index );
304                                         Deselect( IndexOf( item ) );
305                                 }
306                                 else
307                                 {
308                                         // drag sort
309                                         if ( index < fDropIndex )
310                                                 fDropIndex--;
311                                         if ( RemoveItem( item ) )
312                                                 itemToAdd = item;
313                                 }
314                                 if ( itemToAdd )
315                                 {
316                                         if ( AddItem( itemToAdd, fDropIndex ) )
317                                                 Select( IndexOf( itemToAdd ), true );
318                                         else
319                                                 delete itemToAdd;
320                                 }
321                         }
322                         fDropIndex++;
323                 }
324                 fDropIndex = -1;
325         } else
326                 BListView::MessageReceived( message );
327 }
328
329 /*****************************************************************************
330  * DragSortableListView::MouseMoved
331  *****************************************************************************/
332 void
333 DragSortableListView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg)
334 {
335         if ( msg && msg->what == B_SIMPLE_DATA )
336         {
337                 switch ( transit )
338                 {
339                         case B_ENTERED_VIEW:
340                         {
341                                 // draw drop mark
342                                 BRect r(ItemFrame(0L));
343                                 where.y += r.Height() / 2.0;
344                                 int32 count = CountItems();
345                                 bool found = false;
346                                 for (int32 index = 0; index <= count; index++)
347                                 {
348                                         r = ItemFrame(index);
349                                         if (r.Contains(where))
350                                         {
351                                                 SetHighColor(255, 0, 0, 255);
352                                                 StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_HIGH);
353                                                 r.top++;
354                                                 StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_HIGH);
355                                                 fDropIndex = index;
356                                                 found = true;
357                                                 break;
358                                         }
359                                 }
360                                 if (found)
361                                         break;
362                                 // mouse is after last item
363                                 fDropIndex = count;
364                                 r = Bounds();
365                                 if (count > 0)
366                                         r.top = ItemFrame(count - 1).bottom + 1.0;
367                                 SetHighColor(255, 0, 0, 255);
368                                 StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_HIGH);
369                                 r.top++;
370                                 StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_HIGH);
371                                 break;
372                         }
373                         case B_INSIDE_VIEW:
374                         {
375                                 // draw drop mark and invalidate previous drop mark
376                                 BRect r(ItemFrame(0L));
377                                 where.y += r.Height() / 2.0;
378                                 int32 count = CountItems();
379                                 // mouse still after last item?
380                                 if (fDropIndex == count)
381                                 {
382                                         r = Bounds();
383                                         if (count > 0)
384                                                 r.top = ItemFrame(count - 1).bottom + 1.0;
385                                         if (r.Contains(where))
386                                                 break;
387                                         else
388                                         {
389                                                 r.bottom = r.top + 2.0;
390                                                 Invalidate(r);
391                                         }
392                                 }
393                                 // mouse still over same item?
394                                 if (ItemFrame(fDropIndex).Contains(where))
395                                         break;
396                                 else
397                                         InvalidateItem(fDropIndex);
398
399                                 // mouse over new item
400                                 bool found = false;
401                                 for (int32 index = 0; index <= count; index++)
402                                 {
403                                         r = ItemFrame(index);
404                                         if (r.Contains(where))
405                                         {
406                                                 SetHighColor(255, 0, 0, 255);
407                                                 StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_HIGH);
408                                                 r.top++;
409                                                 StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_HIGH);
410                                                 fDropIndex = index;
411                                                 found = true;
412                                                 break;
413                                         }
414                                 }
415                                 if (found)
416                                         break;
417                                 // mouse is after last item
418                                 fDropIndex = count;
419                                 r = Bounds();
420                                 if (count > 0)
421                                         r.top = ItemFrame(count - 1).bottom + 1.0;
422                                 SetHighColor(255, 0, 0, 255);
423                                 StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_HIGH);
424                                 r.top++;
425                                 StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_HIGH);
426                                 break;
427                         }
428                         case B_EXITED_VIEW:
429                         {
430                                 int32 count = CountItems();
431                                 if (count > 0)
432                                 {
433                                         if (fDropIndex == count)
434                                         {
435                                                 BRect r(Bounds());
436                                                 r.top = ItemFrame(count - 1).bottom + 1.0;
437                                                 r.bottom = r.top + 2.0;
438                                                 Invalidate(r);
439                                         }
440                                         else
441                                                 InvalidateItem(fDropIndex);
442                                 }
443                                 break;
444                         }
445                         case B_OUTSIDE_VIEW:
446                                 break;
447                 }
448         }
449         else
450                 BListView::MouseMoved(where, transit, msg);
451 }
452
453 /*****************************************************************************
454  * DragSortableListView::MouseUp
455  *****************************************************************************/
456 void
457 DragSortableListView::MouseUp( BPoint where )
458 {
459         // remove drop mark
460         if ( fDropIndex >= 0 && fDropIndex < CountItems() )
461                 InvalidateItem( fDropIndex );
462         BListView::MouseUp( where );
463 }
464
465 /*****************************************************************************
466  * DragSortableListView::DrawItem
467  *****************************************************************************/
468 void
469 DragSortableListView::DrawItem( BListItem *item, BRect itemFrame, bool complete )
470 {
471         DrawListItem( this, IndexOf( item ), itemFrame );
472 }
473
474
475 /*****************************************************************************
476  * PlaylistView class
477  *****************************************************************************/
478 PlaylistView::PlaylistView( BRect frame, InterfaceWindow* mainWindow )
479         : DragSortableListView( frame, "playlist listview",
480                                                         B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES,
481                                                         B_WILL_DRAW | B_NAVIGABLE | B_PULSE_NEEDED
482                                                         | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE ),
483           fCurrentIndex( -1 ),
484           fPlaying( false ),
485           fMainWindow( mainWindow )
486 {
487 }
488
489 PlaylistView::~PlaylistView()
490 {
491 }
492
493 /*****************************************************************************
494  * PlaylistView::AttachedToWindow
495  *****************************************************************************/
496 void
497 PlaylistView::AttachedToWindow()
498 {
499         // get pulse message every two frames
500         Window()->SetPulseRate(80000);
501 }
502
503 /*****************************************************************************
504  * PlaylistView::MouseDown
505  *****************************************************************************/
506 void
507 PlaylistView::MouseDown( BPoint where )
508 {
509         int32 clicks = 1;
510         Window()->CurrentMessage()->FindInt32( "clicks", &clicks );
511         bool handled = false;
512         for ( int32 i = 0; PlaylistItem* item = (PlaylistItem*)ItemAt( i ); i++ )
513         {
514                 BRect r = ItemFrame( i );
515                 if ( r.Contains( where ) )
516                 {
517                         if ( clicks == 2 )
518                         {
519                                 /* Intf_VLCWrapper::playlistJumpTo( i ); */
520                                 handled = true;
521                         }
522                         else if ( i == fCurrentIndex )
523                         {
524                                 r.right = r.left + TEXT_OFFSET;
525                                 if ( r.Contains ( where ) )
526                                 {
527                                         fMainWindow->PostMessage( PAUSE_PLAYBACK );
528                                         InvalidateItem( i );
529                                         handled = true;
530                                 }
531                         }
532                         break;
533                 }
534         }
535         if ( !handled )
536                 DragSortableListView::MouseDown(where);
537 }
538
539 /*****************************************************************************
540  * PlaylistView::KeyDown
541  *****************************************************************************/
542 void
543 PlaylistView::KeyDown( const char* bytes, int32 numBytes )
544 {
545         if (numBytes < 1)
546                 return;
547                 
548         if ( ( bytes[0] == B_BACKSPACE ) || ( bytes[0] == B_DELETE ) )
549         {
550                 int32 i = CurrentSelection();
551                 if ( BListItem *item = ItemAt( i ) )
552                 {
553 /*                      if ( RemoveItem( item ) )
554                         {
555                                 delete item;
556                                 Select( i + 1 );
557                         }*/
558                 }
559         }
560         DragSortableListView::KeyDown( bytes, numBytes );
561 }
562
563 /*****************************************************************************
564  * PlaylistView::Pulse
565  *****************************************************************************/
566 void
567 PlaylistView::Pulse()
568 {
569         if (fMainWindow->IsStopped())
570                 SetPlaying( false );
571 }
572
573 /*****************************************************************************
574  * PlaylistView::CloneItem
575  *****************************************************************************/
576 BListItem*
577 PlaylistView::CloneItem( int32 atIndex ) const
578 {
579         BListItem* clone = NULL;
580         if ( PlaylistItem* item = dynamic_cast<PlaylistItem*>( ItemAt( atIndex ) ) )
581                 clone = new PlaylistItem( item->Text() );
582         return clone;
583 }
584
585 /*****************************************************************************
586  * PlaylistView::DrawListItem
587  *****************************************************************************/
588 void
589 PlaylistView::DrawListItem( BView* owner, int32 index, BRect frame ) const
590 {
591         if ( PlaylistItem* item = dynamic_cast<PlaylistItem*>( ItemAt( index ) ) )
592                 item->Draw( owner,  frame, index % 2, index == fCurrentIndex, fPlaying );
593 }
594
595 /*****************************************************************************
596  * PlaylistView::MakeDragMessage
597  *****************************************************************************/
598 void
599 PlaylistView::MakeDragMessage( BMessage* message ) const
600 {
601         if ( message )
602         {
603                 message->AddPointer( "list", (void*)this );
604                 for ( int32 i = 0; BListItem* item = ItemAt( CurrentSelection( i ) ); i++ )
605                         message->AddPointer( "item", (void*)item );
606         }
607 }
608
609 /*****************************************************************************
610  * PlaylistView::SetCurrent
611  *****************************************************************************/
612 void
613 PlaylistView::SetCurrent( int32 index )
614 {
615         if ( fCurrentIndex != index )
616         {
617                 InvalidateItem( fCurrentIndex );
618                 fCurrentIndex = index;
619                 InvalidateItem( fCurrentIndex );
620         }
621 }
622
623 /*****************************************************************************
624  * PlaylistView::SetPlaying
625  *****************************************************************************/
626 void
627 PlaylistView::SetPlaying( bool playing )
628 {
629         if ( fPlaying != playing )
630         {
631                 fPlaying = playing;
632                 InvalidateItem( fCurrentIndex );
633         }
634 }