]> git.sesse.net Git - vlc/blob - modules/gui/wxwidgets/playlist_manager.cpp
[patch] unifying meta-information access, the 2nd by Daniel Stränger
[vlc] / modules / gui / wxwidgets / playlist_manager.cpp
1 /*****************************************************************************
2  * playlist_manager.cpp : wxWindows plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Olivier Teulière <ipkiss@via.ecp.fr>
8  *          Clément Stenac <zorglub@videolan.org>
9  *          Gildas Bazin <gbazin@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/OR MODIFy
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include "playlist_manager.hpp"
30 #include "interface.hpp"
31
32 #include "bitmaps/type_unknown.xpm"
33 #include "bitmaps/type_afile.xpm"
34 #include "bitmaps/type_vfile.xpm"
35 #include "bitmaps/type_net.xpm"
36 #include "bitmaps/type_card.xpm"
37 #include "bitmaps/type_disc.xpm"
38 #include "bitmaps/type_cdda.xpm"
39 #include "bitmaps/type_directory.xpm"
40 #include "bitmaps/type_playlist.xpm"
41 #include "bitmaps/type_node.xpm"
42
43 #include <wx/dynarray.h>
44 #include <wx/imaglist.h>
45 #include "vlc_meta.h"
46
47 namespace wxvlc {
48 /* Callback prototype */
49 static int PlaylistChanged( vlc_object_t *, const char *,
50                             vlc_value_t, vlc_value_t, void * );
51 static int PlaylistNext( vlc_object_t *, const char *,
52                          vlc_value_t, vlc_value_t, void * );
53 static int ItemChanged( vlc_object_t *, const char *,
54                         vlc_value_t, vlc_value_t, void * );
55 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
56                       vlc_value_t oval, vlc_value_t nval, void *param );
57 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
58                       vlc_value_t oval, vlc_value_t nval, void *param );
59
60 /*****************************************************************************
61  * Event Table.
62  *****************************************************************************/
63
64 /* IDs for the controls and the menu commands */
65 enum
66 {
67     TreeCtrl_Event,
68
69     UpdateItem_Event,
70     AppendItem_Event,
71     RemoveItem_Event,
72 };
73
74 DEFINE_LOCAL_EVENT_TYPE( wxEVT_PLAYLIST );
75
76 BEGIN_EVENT_TABLE(PlaylistManager, wxPanel)
77     /* Tree control events */
78     EVT_TREE_ITEM_ACTIVATED( TreeCtrl_Event, PlaylistManager::OnActivateItem )
79
80     /* Custom events */
81     EVT_COMMAND(-1, wxEVT_PLAYLIST, PlaylistManager::OnPlaylistEvent)
82 END_EVENT_TABLE()
83
84 /*****************************************************************************
85  * PlaylistItem class
86  ****************************************************************************/
87 class PlaylistItem : public wxTreeItemData
88 {
89 public:
90     PlaylistItem( playlist_item_t *p_item ) : i_id(p_item->input.i_id) {}
91     int i_id;
92 };
93
94 /*****************************************************************************
95  * Constructor.
96  *****************************************************************************/
97 PlaylistManager::PlaylistManager( intf_thread_t *_p_intf, wxWindow *p_parent ):
98     wxPanel( p_parent, -1, wxDefaultPosition, wxSize(0,0) )
99 {
100     /* Initializations */
101     p_intf = _p_intf;
102     b_need_update = VLC_FALSE;
103     i_items_to_append = 0;
104     i_cached_item_id = -1;
105     i_update_counter = 0;
106
107     p_playlist = (playlist_t *)
108         vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
109     if( p_playlist == NULL ) return;
110
111     var_Create( p_intf, "random", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
112     var_Create( p_intf, "loop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
113     var_Create( p_intf, "repeat", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );;
114
115     /* Create the tree */
116     treectrl = new wxTreeCtrl( this, TreeCtrl_Event,
117                                wxDefaultPosition, wxDefaultSize,
118                                wxTR_HIDE_ROOT | wxTR_LINES_AT_ROOT|
119                                wxTR_NO_LINES |
120                                wxTR_HAS_BUTTONS | wxTR_TWIST_BUTTONS |
121                                wxTR_MULTIPLE | wxTR_EXTENDED );
122
123     /* Add everything to the panel */
124     sizer = new wxBoxSizer( wxHORIZONTAL );
125     SetSizer( sizer );
126     sizer->Add( treectrl, 1, wxEXPAND );
127     sizer->Layout();
128     sizer->Fit( this );
129
130     /* Create image list */
131     wxImageList *p_images = new wxImageList( 16 , 16, TRUE );
132
133     /* FIXME: absolutely needs to be in the right order FIXME */
134     p_images->Add( wxIcon( type_unknown_xpm ) );
135     p_images->Add( wxIcon( type_afile_xpm ) );
136     p_images->Add( wxIcon( type_vfile_xpm ) );
137     p_images->Add( wxIcon( type_directory_xpm ) );
138     p_images->Add( wxIcon( type_disc_xpm ) );
139     p_images->Add( wxIcon( type_cdda_xpm ) );
140     p_images->Add( wxIcon( type_card_xpm ) );
141     p_images->Add( wxIcon( type_net_xpm ) );
142     p_images->Add( wxIcon( type_playlist_xpm ) );
143     p_images->Add( wxIcon( type_node_xpm ) );
144     treectrl->AssignImageList( p_images );
145
146     /* Reduce font size */
147     wxFont font = treectrl->GetFont(); font.SetPointSize(9);
148     treectrl->SetFont( font );
149
150 #if wxUSE_DRAG_AND_DROP
151     /* Associate drop targets with the playlist */
152     SetDropTarget( new DragAndDrop( p_intf, VLC_TRUE ) );
153 #endif
154
155     /* Update the playlist */
156     Rebuild( VLC_TRUE );
157
158     /*
159      * We want to be notified of playlist changes
160      */
161
162     /* Some global changes happened -> Rebuild all */
163     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
164
165     /* We went to the next item */
166     var_AddCallback( p_playlist, "playlist-current", PlaylistNext, this );
167
168     /* One item has been updated */
169     var_AddCallback( p_playlist, "item-change", ItemChanged, this );
170
171     var_AddCallback( p_playlist, "item-append", ItemAppended, this );
172     var_AddCallback( p_playlist, "item-deleted", ItemDeleted, this );
173 }
174
175 PlaylistManager::~PlaylistManager()
176 {
177     if( p_playlist == NULL ) return;
178
179     var_DelCallback( p_playlist, "item-change", ItemChanged, this );
180     var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this );
181     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
182     var_DelCallback( p_playlist, "item-append", ItemAppended, this );
183     var_DelCallback( p_playlist, "item-deleted", ItemDeleted, this );
184     vlc_object_release( p_playlist );
185 }
186
187 /*****************************************************************************
188  * PlaylistChanged: callback triggered by the intf-change playlist variable
189  *  We don't rebuild the playlist directly here because we don't want the
190  *  caller to block for a too long time.
191  *****************************************************************************/
192 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
193                             vlc_value_t oval, vlc_value_t nval, void *param )
194 {
195     PlaylistManager *p_playlist = (PlaylistManager *)param;
196     p_playlist->b_need_update = VLC_TRUE;
197     return VLC_SUCCESS;
198 }
199
200 /*****************************************************************************
201  * Next: callback triggered by the playlist-current playlist variable
202  *****************************************************************************/
203 static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
204                          vlc_value_t oval, vlc_value_t nval, void *param )
205 {
206     PlaylistManager *p_playlist = (PlaylistManager *)param;
207
208     wxCommandEvent event( wxEVT_PLAYLIST, UpdateItem_Event );
209     event.SetInt( oval.i_int );
210     p_playlist->AddPendingEvent( event );
211     event.SetInt( nval.i_int );
212     p_playlist->AddPendingEvent( event );
213
214     return VLC_SUCCESS;
215 }
216
217 /*****************************************************************************
218  * Update functions
219  *****************************************************************************/
220 void PlaylistManager::CreateNode( playlist_item_t *p_node, wxTreeItemId parent)
221 {
222     wxTreeItemId node =
223         treectrl->AppendItem( parent, wxL2U( p_node->input.psz_name ), -1, -1,
224                               new PlaylistItem( p_node ) );
225     treectrl->SetItemImage( node, p_node->input.i_type );
226
227     UpdateNodeChildren( p_node, node );
228 }
229
230 void PlaylistManager::UpdateNode( playlist_item_t *p_node, wxTreeItemId node )
231 {
232     wxTreeItemIdValue cookie;
233     wxTreeItemId child;
234
235     for( int i = 0; i < p_node->i_children ; i++ )
236     {
237         if( !i ) child = treectrl->GetFirstChild( node, cookie);
238         else child = treectrl->GetNextChild( node, cookie );
239
240         if( !child.IsOk() )
241         {
242             /* Not enough children */
243             CreateNode( p_node->pp_children[i], node );
244             /* Keep the tree pointer up to date */
245             child = treectrl->GetNextChild( node, cookie );
246         }
247     }
248
249     treectrl->SetItemImage( node, p_node->input.i_type );
250
251 }
252
253 void PlaylistManager::UpdateNodeChildren( playlist_item_t *p_node,
254                                           wxTreeItemId node )
255 {
256     for( int i = 0; i< p_node->i_children ; i++ )
257     {
258         /* Append the item */
259         if( p_node->pp_children[i]->i_children == -1 )
260         {
261             wxTreeItemId item =
262                 treectrl->AppendItem( node,
263                     wxL2U( p_node->pp_children[i]->input.psz_name ), -1,-1,
264                            new PlaylistItem( p_node->pp_children[i]) );
265
266             UpdateTreeItem( item );
267         }
268         else
269         {
270             CreateNode( p_node->pp_children[i], node );
271         }
272     }
273 }
274
275 void PlaylistManager::UpdateTreeItem( wxTreeItemId item )
276 {
277     if( ! item.IsOk() ) return;
278
279     wxTreeItemData *p_data = treectrl->GetItemData( item );
280     if( !p_data ) return;
281
282     LockPlaylist( p_intf->p_sys, p_playlist );
283     playlist_item_t *p_item =
284         playlist_ItemGetById( p_playlist, ((PlaylistItem *)p_data)->i_id );
285     if( !p_item )
286     {
287         UnlockPlaylist( p_intf->p_sys, p_playlist );
288         return;
289     }
290
291     wxString msg;
292     wxString duration = wxU( "" );
293     char *psz_author =
294         vlc_input_item_GetInfo( &p_item->input,
295                                 _(VLC_META_INFO_CAT), _(VLC_META_ARTIST) );
296     if( !psz_author )
297     {
298         UnlockPlaylist( p_intf->p_sys, p_playlist );
299         return;
300     }
301
302     char psz_duration[MSTRTIME_MAX_SIZE];
303     mtime_t dur = p_item->input.i_duration;
304
305     if( dur != -1 )
306     {
307         secstotimestr( psz_duration, dur/1000000 );
308         duration.Append( wxU( " ( " ) +  wxString( wxU( psz_duration ) ) +
309                          wxU( " )" ) );
310     }
311
312     if( !strcmp( psz_author, "" ) || p_item->input.b_fixed_name == VLC_TRUE )
313     {
314         msg = wxString( wxU( p_item->input.psz_name ) ) + duration;
315     }
316     else
317     {
318         msg = wxString(wxU( psz_author )) + wxT(" - ") +
319                     wxString(wxU(p_item->input.psz_name)) + duration;
320     }
321     free( psz_author );
322     treectrl->SetItemText( item , msg );
323     treectrl->SetItemImage( item, p_item->input.i_type );
324
325     if( p_playlist->status.p_item == p_item )
326     {
327         treectrl->SetItemBold( item, true );
328         while( treectrl->GetItemParent( item ).IsOk() )
329         {
330             item = treectrl->GetItemParent( item );
331             treectrl->Expand( item );
332         }
333     }
334     else
335     {
336         treectrl->SetItemBold( item, false );
337     }
338     UnlockPlaylist( p_intf->p_sys, p_playlist );
339 }
340
341 void PlaylistManager::AppendItem( wxCommandEvent& event )
342 {
343     playlist_add_t *p_add = (playlist_add_t *)event.GetClientData();
344     playlist_item_t *p_item = NULL;
345     wxTreeItemId item, node;
346
347     i_items_to_append--;
348
349     /* No need to do anything if the playlist is going to be rebuilt */
350     if( b_need_update ) return;
351
352     //if( p_add->i_view != i_current_view ) goto update;
353
354     node = FindItem( treectrl->GetRootItem(), p_add->i_node );
355     if( !node.IsOk() ) goto update;
356
357     p_item = playlist_ItemGetById( p_playlist, p_add->i_item );
358     if( !p_item ) goto update;
359
360     item = FindItem( treectrl->GetRootItem(), p_add->i_item );
361     if( item.IsOk() ) goto update;
362
363     item = treectrl->AppendItem( node, wxL2U( p_item->input.psz_name ), -1,-1,
364                                  new PlaylistItem( p_item ) );
365     treectrl->SetItemImage( item, p_item->input.i_type );
366
367     if( item.IsOk() && p_item->i_children == -1 ) UpdateTreeItem( item );
368
369 update:
370     return;
371 }
372
373 void PlaylistManager::UpdateItem( int i )
374 {
375     if( i < 0 ) return; /* Sanity check */
376
377     wxTreeItemId item = FindItem( treectrl->GetRootItem(), i );
378     if( item.IsOk() ) UpdateTreeItem( item );
379 }
380
381 void PlaylistManager::RemoveItem( int i )
382 {
383     if( i <= 0 ) return; /* Sanity check */
384
385     wxTreeItemId item = FindItem( treectrl->GetRootItem(), i );
386     if( item.IsOk() )
387     {
388         treectrl->Delete( item );
389
390         /* Invalidate cache */
391         i_cached_item_id = -1;
392     }
393 }
394
395 /* This function is called on a regular basis */
396 void PlaylistManager::Update()
397 {
398     i_update_counter++;
399
400     /* If the playlist isn't show there's no need to update it */
401     if( !IsShown() ) return;
402
403     if( this->b_need_update )
404     {
405         this->b_need_update = VLC_FALSE;
406         Rebuild( VLC_TRUE );
407     }
408
409     /* Updating the playing status every 0.5s is enough */
410     if( i_update_counter % 5 ) return;
411 }
412
413 /**********************************************************************
414  * Rebuild the playlist
415  **********************************************************************/
416 void PlaylistManager::Rebuild( vlc_bool_t b_root )
417 {
418     playlist_view_t *p_view;
419
420     i_items_to_append = 0;
421     i_cached_item_id = -1;
422
423     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
424
425     treectrl->DeleteAllItems();
426     treectrl->AddRoot( wxU(_("root" )), -1, -1,
427                        new PlaylistItem( p_view->p_root ) );
428
429     wxTreeItemId root = treectrl->GetRootItem();
430     UpdateNode( p_view->p_root, root );
431 }
432
433 /**********************************************************************
434  * Search functions (internal)
435  **********************************************************************/
436
437 /* Find a wxItem from a playlist id */
438 wxTreeItemId PlaylistManager::FindItem( wxTreeItemId root, int i_id )
439 {
440     wxTreeItemIdValue cookie;
441     PlaylistItem *p_wxcurrent;
442     wxTreeItemId dummy, search, item, child;
443
444     if( i_id < 0 ) return dummy;
445     if( i_cached_item_id == i_id ) return cached_item;
446
447     p_wxcurrent = (PlaylistItem *)treectrl->GetItemData( root );
448     if( !p_wxcurrent ) return dummy;
449
450     if( p_wxcurrent->i_id == i_id )
451     {
452         i_cached_item_id = i_id;
453         cached_item = root;
454         return root;
455     }
456
457     item = treectrl->GetFirstChild( root, cookie );
458     while( item.IsOk() )
459     {
460         p_wxcurrent = (PlaylistItem *)treectrl->GetItemData( item );
461         if( !p_wxcurrent )
462         {
463             item = treectrl->GetNextChild( root, cookie );
464             continue;
465         }
466
467         if( p_wxcurrent->i_id == i_id )
468         {
469             i_cached_item_id = i_id;
470             cached_item = item;
471             return item;
472         }
473
474         if( treectrl->ItemHasChildren( item ) )
475         {
476             wxTreeItemId search = FindItem( item, i_id );
477             if( search.IsOk() ) return search;
478         }
479
480         item = treectrl->GetNextChild( root, cookie );
481     }
482
483     return dummy;
484 }
485
486 /********************************************************************
487  * Events
488  ********************************************************************/
489 void PlaylistManager::OnActivateItem( wxTreeEvent& event )
490 {
491     playlist_item_t *p_item, *p_node;
492     wxTreeItemId parent = treectrl->GetItemParent( event.GetItem() );
493     PlaylistItem *p_wxitem = (PlaylistItem *)
494         treectrl->GetItemData( event.GetItem() );
495
496     if( !p_wxitem || !parent.IsOk() ) return;
497
498     PlaylistItem *p_wxparent = (PlaylistItem *)treectrl->GetItemData( parent );
499     if( !p_wxparent ) return;
500
501     LockPlaylist( p_intf->p_sys, p_playlist );
502     p_item = playlist_ItemGetById( p_playlist, p_wxitem->i_id );
503     p_node = playlist_ItemGetById( p_playlist, p_wxparent->i_id );
504     if( !p_item || p_item->i_children >= 0 )
505     {
506         p_node = p_item;
507         p_item = NULL;
508     }
509
510     playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VIEW_CATEGORY,
511                       p_node, p_item );
512     UnlockPlaylist( p_intf->p_sys, p_playlist );
513 }
514
515 void PlaylistManager::OnPlaylistEvent( wxCommandEvent& event )
516 {
517     switch( event.GetId() )
518     {
519     case UpdateItem_Event:
520         UpdateItem( event.GetInt() );
521         break;
522     case AppendItem_Event:
523         AppendItem( event );
524         break;
525     case RemoveItem_Event:
526         RemoveItem( event.GetInt() );
527         break;
528     }
529 }
530
531 /*****************************************************************************
532  * ItemChanged: callback triggered by the item-change playlist variable
533  *****************************************************************************/
534 static int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
535                         vlc_value_t old_val, vlc_value_t new_val, void *param )
536 {
537     PlaylistManager *p_playlist = (PlaylistManager *)param;
538
539     wxCommandEvent event( wxEVT_PLAYLIST, UpdateItem_Event );
540     event.SetInt( new_val.i_int );
541     p_playlist->AddPendingEvent( event );
542
543     return VLC_SUCCESS;
544 }
545 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
546                         vlc_value_t old_val, vlc_value_t new_val, void *param )
547 {
548     PlaylistManager *p_playlist = (PlaylistManager *)param;
549
550     wxCommandEvent event( wxEVT_PLAYLIST, RemoveItem_Event );
551     event.SetInt( new_val.i_int );
552     p_playlist->AddPendingEvent( event );
553
554     return VLC_SUCCESS;
555 }
556
557 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
558                          vlc_value_t oval, vlc_value_t nval, void *param )
559 {
560     PlaylistManager *p_playlist = (PlaylistManager *)param;
561
562     playlist_add_t *p_add = (playlist_add_t *)malloc(sizeof( playlist_add_t));
563     memcpy( p_add, nval.p_address, sizeof( playlist_add_t ) );
564
565     if( p_playlist->i_items_to_append++ > 50 )
566     {
567         /* Too many items waiting to be added, it will be quicker to rebuild
568          * the whole playlist */
569         p_playlist->b_need_update = VLC_TRUE;
570         return VLC_SUCCESS;
571     }
572
573     wxCommandEvent event( wxEVT_PLAYLIST, AppendItem_Event );
574     event.SetClientData( (void *)p_add );
575     p_playlist->AddPendingEvent( event );
576
577     return VLC_SUCCESS;
578 }
579 }