]> git.sesse.net Git - vlc/blob - modules/gui/wxwidgets/dialogs/playlist.cpp
Add CDDB support to CDDA module (Refs:#459)
[vlc] / modules / gui / wxwidgets / dialogs / playlist.cpp
1 /*****************************************************************************
2  * playlist.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  *
10  * This program is free software; you can redistribute it and/OR MODIFy
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "dialogs/playlist.hpp"
29 #include "dialogs/iteminfo.hpp"
30 #include "interface.hpp" // Needed for D&D - TODO: Split
31
32 #include "bitmaps/shuffle.xpm"
33 #include "bitmaps/repeat.xpm"
34 #include "bitmaps/loop.xpm"
35
36 #include "bitmaps/type_unknown.xpm"
37 #include "bitmaps/type_afile.xpm"
38 #include "bitmaps/type_vfile.xpm"
39 #include "bitmaps/type_net.xpm"
40 #include "bitmaps/type_card.xpm"
41 #include "bitmaps/type_disc.xpm"
42 #include "bitmaps/type_cdda.xpm"
43 #include "bitmaps/type_directory.xpm"
44 #include "bitmaps/type_playlist.xpm"
45 #include "bitmaps/type_node.xpm"
46
47 #include <wx/dynarray.h>
48 #include <wx/imaglist.h>
49
50 #include <vlc_meta.h>
51
52 #define HELP_SHUFFLE N_( "Shuffle" )
53 #define HELP_LOOP N_( "Repeat All" )
54 #define HELP_REPEAT N_( "Repeat One" )
55
56 namespace wxvlc {
57 /* Callback prototype */
58 static int PlaylistChanged( vlc_object_t *, const char *,
59                             vlc_value_t, vlc_value_t, void * );
60 static int PlaylistNext( vlc_object_t *, const char *,
61                          vlc_value_t, vlc_value_t, void * );
62 static int ItemChanged( vlc_object_t *, const char *,
63                         vlc_value_t, vlc_value_t, void * );
64 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
65                       vlc_value_t oval, vlc_value_t nval, void *param );
66 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
67                       vlc_value_t oval, vlc_value_t nval, void *param );
68
69 /*****************************************************************************
70  * Event Table.
71  *****************************************************************************/
72
73 /* IDs for the controls and the menu commands */
74 enum
75 {
76     /* menu items */
77     AddFile_Event = 1,
78     AddDir_Event,
79     AddMRL_Event,
80     Close_Event,
81     Open_Event,
82     Save_Event,
83
84     SortTitle_Event,
85     RSortTitle_Event,
86     Randomize_Event,
87
88     DeleteSelection_Event,
89     Random_Event,
90     Loop_Event,
91     Repeat_Event,
92
93     PopupPlay_Event,
94     PopupPlayThis_Event,
95     PopupPreparse_Event,
96     PopupSort_Event,
97     PopupDel_Event,
98     PopupInfo_Event,
99
100     SearchText_Event,
101     Search_Event,
102
103     /* controls */
104     TreeCtrl_Event,
105
106     Browse_Event,  /* For export playlist */
107
108     /* custom events */
109     UpdateItem_Event,
110     AppendItem_Event,
111     RemoveItem_Event,
112
113     MenuDummy_Event = wxID_HIGHEST + 999,
114
115     FirstView_Event = wxID_HIGHEST + 1000,
116     LastView_Event = wxID_HIGHEST + 1100,
117
118     FirstSD_Event = wxID_HIGHEST + 2000,
119     LastSD_Event = wxID_HIGHEST + 2100,
120 };
121
122 DEFINE_LOCAL_EVENT_TYPE( wxEVT_PLAYLIST );
123
124 BEGIN_EVENT_TABLE(Playlist, wxFrame)
125     EVT_SIZE(Playlist::OnSize)
126
127     /* Menu events */
128     EVT_MENU(AddFile_Event, Playlist::OnAddFile)
129     EVT_MENU(AddDir_Event, Playlist::OnAddDir)
130     EVT_MENU(AddMRL_Event, Playlist::OnAddMRL)
131     EVT_MENU(Close_Event, Playlist::OnMenuClose)
132     EVT_MENU(Open_Event, Playlist::OnOpen)
133     EVT_MENU(Save_Event, Playlist::OnSave)
134
135     EVT_MENU(SortTitle_Event, Playlist::OnSort)
136     EVT_MENU(RSortTitle_Event, Playlist::OnSort)
137
138     EVT_MENU(Randomize_Event, Playlist::OnSort)
139
140     EVT_MENU(DeleteSelection_Event, Playlist::OnDeleteSelection)
141
142     EVT_MENU_OPEN( Playlist::OnMenuOpen )
143     EVT_MENU( -1, Playlist::OnMenuEvent )
144
145     EVT_TOOL(Random_Event, Playlist::OnRandom)
146     EVT_TOOL(Repeat_Event, Playlist::OnRepeat)
147     EVT_TOOL(Loop_Event, Playlist::OnLoop)
148
149     /* Popup events */
150     EVT_MENU( PopupPlay_Event, Playlist::OnPopupPlay)
151     EVT_MENU( PopupPlayThis_Event, Playlist::OnPopupPlay)
152     EVT_MENU( PopupPreparse_Event, Playlist::OnPopupPreparse)
153     EVT_MENU( PopupSort_Event, Playlist::OnPopupSort)
154     EVT_MENU( PopupDel_Event, Playlist::OnPopupDel)
155     EVT_MENU( PopupInfo_Event, Playlist::OnPopupInfo)
156
157     /* Tree control events */
158     EVT_TREE_ITEM_ACTIVATED( TreeCtrl_Event, Playlist::OnActivateItem )
159     EVT_TREE_KEY_DOWN( -1, Playlist::OnKeyDown )
160
161     EVT_CONTEXT_MENU( Playlist::OnPopup )
162
163     /* Button events */
164     EVT_BUTTON( Search_Event, Playlist::OnSearch)
165     EVT_BUTTON( Save_Event, Playlist::OnSave)
166
167     /*EVT_TEXT( SearchText_Event, Playlist::OnSearchTextChange )*/
168     EVT_TEXT_ENTER( SearchText_Event, Playlist::OnSearch )
169
170     /* Custom events */
171     EVT_COMMAND(-1, wxEVT_PLAYLIST, Playlist::OnPlaylistEvent)
172
173     /* Special events : we don't want to destroy the window when the user
174      * clicks on (X) */
175     EVT_CLOSE(Playlist::OnClose)
176 END_EVENT_TABLE()
177
178 /*****************************************************************************
179  * PlaylistItem class
180  ****************************************************************************/
181 class PlaylistItem : public wxTreeItemData
182 {
183 public:
184     PlaylistItem( playlist_item_t *p_item ) : wxTreeItemData()
185     {
186         i_id = p_item->input.i_id;
187     }
188 protected:
189     int i_id;
190 friend class Playlist;
191 };
192
193 /*****************************************************************************
194  * Constructor.
195  *****************************************************************************/
196 Playlist::Playlist( intf_thread_t *_p_intf, wxWindow *p_parent ):
197     wxFrame( p_parent, -1, wxU(_("Playlist")), wxDefaultPosition,
198              wxSize(500,300), wxDEFAULT_FRAME_STYLE )
199 {
200     vlc_value_t val;
201
202     /* Initializations */
203     p_intf = _p_intf;
204     pp_sds = NULL;
205     i_update_counter = 0;
206     i_sort_mode = MODE_NONE;
207     b_need_update = VLC_FALSE;
208     i_items_to_append = 0;
209     p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
210                                                 FIND_ANYWHERE );
211     if( p_playlist == NULL ) return;
212
213     SetIcon( *p_intf->p_sys->p_icon );
214
215     p_view_menu = NULL;
216     p_sd_menu = SDMenu();
217
218     i_current_view = VIEW_CATEGORY;
219     b_changed_view = VLC_FALSE;
220
221     i_title_sorted = 0;
222     i_group_sorted = 0;
223     i_duration_sorted = 0;
224
225     var_Create( p_intf, "random", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
226     var_Create( p_intf, "loop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
227     var_Create( p_intf, "repeat", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );;
228
229     /* Create our "Manage" menu */
230     wxMenu *manage_menu = new wxMenu;
231     manage_menu->Append( AddFile_Event, wxU(_("&Simple Add File...")) );
232     manage_menu->Append( AddDir_Event, wxU(_("Add &Directory...")) );
233     manage_menu->Append( AddMRL_Event, wxU(_("&Add MRL...")) );
234     manage_menu->AppendSeparator();
235     manage_menu->Append( MenuDummy_Event, wxU(_("Services discovery")),
236                          p_sd_menu );
237     manage_menu->AppendSeparator();
238     manage_menu->Append( Open_Event, wxU(_("&Open Playlist...")) );
239     manage_menu->Append( Save_Event, wxU(_("&Save Playlist...")) );
240     manage_menu->AppendSeparator();
241     manage_menu->Append( Close_Event, wxU(_("&Close")) );
242
243     /* Create our "Sort" menu */
244     wxMenu *sort_menu = new wxMenu;
245     sort_menu->Append( SortTitle_Event, wxU(_("Sort by &title")) );
246     sort_menu->Append( RSortTitle_Event, wxU(_("&Reverse sort by title")) );
247     sort_menu->AppendSeparator();
248     sort_menu->Append( Randomize_Event, wxU(_("&Shuffle Playlist")) );
249
250     /* Create our "Selection" menu */
251     wxMenu *selection_menu = new wxMenu;
252     selection_menu->Append( DeleteSelection_Event, wxU(_("D&elete")) );
253
254     /* Create our "View" menu */
255     ViewMenu();
256
257     /* Append the freshly created menus to the menu bar */
258     wxMenuBar *menubar = new wxMenuBar();
259     menubar->Append( manage_menu, wxU(_("&Manage")) );
260     menubar->Append( sort_menu, wxU(_("S&ort")) );
261     menubar->Append( selection_menu, wxU(_("&Selection")) );
262     menubar->Append( p_view_menu, wxU(_("&View items") ) );
263
264     /* Attach the menu bar to the frame */
265     SetMenuBar( menubar );
266
267     /* Create the popup menu */
268     node_popup = new wxMenu;
269     node_popup->Append( PopupPlay_Event, wxU(_("Play")) );
270     node_popup->Append( PopupPlayThis_Event, wxU(_("Play this branch")) );
271     node_popup->Append( PopupPreparse_Event, wxU(_("Preparse")) );
272     node_popup->Append( PopupSort_Event, wxU(_("Sort this branch")) );
273     node_popup->Append( PopupDel_Event, wxU(_("Delete")) );
274     node_popup->Append( PopupInfo_Event, wxU(_("Info")) );
275
276     item_popup = new wxMenu;
277     item_popup->Append( PopupPlay_Event, wxU(_("Play")) );
278     item_popup->Append( PopupPreparse_Event, wxU(_("Preparse")) );
279     item_popup->Append( PopupDel_Event, wxU(_("Delete")) );
280     item_popup->Append( PopupInfo_Event, wxU(_("Info")) );
281
282     /* Create a panel to put everything in */
283     wxPanel *playlist_panel = new wxPanel( this, -1 );
284     playlist_panel->SetAutoLayout( TRUE );
285
286     /* Create the toolbar */
287     wxToolBar *toolbar =
288         CreateToolBar( wxTB_HORIZONTAL | wxTB_FLAT );
289
290     /* Create the random tool */
291     toolbar->AddTool( Random_Event, wxT(""), wxBitmap(shuffle_on_xpm),
292                        wxBitmap(shuffle_on_xpm), wxITEM_CHECK,
293                        wxU(_(HELP_SHUFFLE) ) );
294     var_Get( p_intf, "random", &val );
295     toolbar->ToggleTool( Random_Event, val.b_bool );
296
297     /* Create the Loop tool */
298     toolbar->AddTool( Loop_Event, wxT(""), wxBitmap( loop_xpm),
299                       wxBitmap( loop_xpm), wxITEM_CHECK,
300                       wxU(_(HELP_LOOP )  ) );
301     var_Get( p_intf, "loop", &val );
302     toolbar->ToggleTool( Loop_Event, val.b_bool );
303
304     /* Create the Repeat one checkbox */
305     toolbar->AddTool( Repeat_Event, wxT(""), wxBitmap( repeat_xpm),
306                       wxBitmap( repeat_xpm), wxITEM_CHECK,
307                       wxU(_(HELP_REPEAT )  ) );
308     var_Get( p_intf, "repeat", &val );
309     toolbar->ToggleTool( Repeat_Event, val.b_bool ) ;
310
311     /* Create the Search Textbox */
312     search_text = new wxTextCtrl( toolbar, SearchText_Event, wxT(""),
313                                   wxDefaultPosition, wxSize(100, -1),
314                                   wxTE_PROCESS_ENTER);
315
316     /* Create the search button */
317     search_button = new wxButton( toolbar , Search_Event, wxU(_("Search")) );
318
319     toolbar->AddControl( new wxControl( toolbar, -1, wxDefaultPosition,
320                          wxSize(16, 16), wxBORDER_NONE ) );
321     toolbar->AddControl( search_text );
322     toolbar->AddControl( new wxControl( toolbar, -1, wxDefaultPosition,
323                          wxSize(5, 5), wxBORDER_NONE ) );
324     toolbar->AddControl( search_button );
325     search_button->SetDefault();
326     toolbar->Realize();
327
328     /* Create the tree */
329     treectrl = new wxTreeCtrl( playlist_panel, TreeCtrl_Event,
330                                wxDefaultPosition, wxDefaultSize,
331                                wxTR_HIDE_ROOT | wxTR_LINES_AT_ROOT|
332                                wxTR_NO_LINES |
333                                wxTR_HAS_BUTTONS | wxTR_TWIST_BUTTONS |
334                                wxTR_MULTIPLE | wxTR_EXTENDED );
335
336     /* Create image list */
337     wxImageList *p_images = new wxImageList( 16 , 16, TRUE );
338
339     /* FIXME: absolutely needs to be in the right order FIXME */
340     p_images->Add( wxIcon( type_unknown_xpm ) );
341     p_images->Add( wxIcon( type_afile_xpm ) );
342     p_images->Add( wxIcon( type_vfile_xpm ) );
343     p_images->Add( wxIcon( type_directory_xpm ) );
344     p_images->Add( wxIcon( type_disc_xpm ) );
345     p_images->Add( wxIcon( type_cdda_xpm ) );
346     p_images->Add( wxIcon( type_card_xpm ) );
347     p_images->Add( wxIcon( type_net_xpm ) );
348     p_images->Add( wxIcon( type_playlist_xpm ) );
349     p_images->Add( wxIcon( type_node_xpm ) );
350     treectrl->AssignImageList( p_images );
351
352     treectrl->AddRoot( wxU(_("root" )), -1, -1, NULL );
353
354     /* Reduce font size */
355     wxFont font= treectrl->GetFont();
356     font.SetPointSize(9);
357     treectrl->SetFont( font );
358
359     wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
360     panel_sizer->Add( treectrl, 1, wxEXPAND | wxALL, 5 );
361     panel_sizer->Layout();
362
363     playlist_panel->SetSizerAndFit( panel_sizer );
364
365     int pi_widths[1] =  { -1 };
366     statusbar = CreateStatusBar( 1 );
367     statusbar->SetStatusWidths( 1, pi_widths );
368
369 #if wxUSE_DRAG_AND_DROP
370     /* Associate drop targets with the playlist */
371     SetDropTarget( new DragAndDrop( p_intf, VLC_TRUE ) );
372 #endif
373
374     i_saved_id = -1;
375
376
377     /* We want to be noticed of playlist changes */
378
379     /* Some global changes happened -> Rebuild all */
380     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
381
382     /* We went to the next item */
383     var_AddCallback( p_playlist, "playlist-current", PlaylistNext, this );
384
385     /* One item has been updated */
386     var_AddCallback( p_playlist, "item-change", ItemChanged, this );
387
388     var_AddCallback( p_playlist, "item-append", ItemAppended, this );
389     var_AddCallback( p_playlist, "item-deleted", ItemDeleted, this );
390
391     /* Update the playlist */
392     Rebuild( VLC_TRUE );
393
394 }
395
396 Playlist::~Playlist()
397 {
398     if( pp_sds != NULL ) free( pp_sds );
399
400     if( p_playlist == NULL ) return;
401
402     var_DelCallback( p_playlist, "item-change", ItemChanged, this );
403     var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this );
404     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
405     var_DelCallback( p_playlist, "item-append", ItemAppended, this );
406     var_DelCallback( p_playlist, "item-deleted", ItemDeleted, this );
407     vlc_object_release( p_playlist );
408 }
409
410 /**********************************************************************
411  * Update functions
412  **********************************************************************/
413
414 /* Update a node */
415 void Playlist::UpdateNode( playlist_item_t *p_node, wxTreeItemId node )
416 {
417     wxTreeItemIdValue cookie;
418     wxTreeItemId child;
419     for( int i = 0; i< p_node->i_children ; i++ )
420     {
421         if( i == 0 )
422         {
423             child = treectrl->GetFirstChild( node, cookie);
424         }
425         else
426         {
427             child = treectrl->GetNextChild( node, cookie );
428         }
429
430         if( !child.IsOk() )
431         {
432             /* Not enough children */
433             CreateNode( p_node->pp_children[i], node );
434             /* Keep the tree pointer up to date */
435             child = treectrl->GetNextChild( node, cookie );
436         }
437     }
438     treectrl->SetItemImage( node, p_node->input.i_type );
439
440 }
441
442 /* Creates the node p_node as last child of parent */
443 void Playlist::CreateNode( playlist_item_t *p_node, wxTreeItemId parent )
444 {
445     wxTreeItemId node =
446         treectrl->AppendItem( parent, wxL2U( p_node->input.psz_name ),
447                               -1,-1, new PlaylistItem( p_node ) );
448     treectrl->SetItemImage( node, p_node->input.i_type );
449
450     UpdateNodeChildren( p_node, node );
451 }
452
453 /* Update all children (recursively) of this node */
454 void Playlist::UpdateNodeChildren( playlist_item_t *p_node,
455                                    wxTreeItemId node )
456 {
457
458     for( int i = 0; i< p_node->i_children ; i++ )
459     {
460         /* Append the item */
461         if( p_node->pp_children[i]->i_children == -1 )
462         {
463             wxTreeItemId item =
464                 treectrl->AppendItem( node,
465                     wxL2U( p_node->pp_children[i]->input.psz_name ), -1,-1,
466                            new PlaylistItem( p_node->pp_children[i]) );
467
468             UpdateTreeItem( item );
469         }
470         else
471         {
472             CreateNode( p_node->pp_children[i], node );
473         }
474     }
475 }
476
477 /* Update an item in the tree */
478 void Playlist::UpdateTreeItem( wxTreeItemId item )
479 {
480     if( ! item.IsOk() ) return;
481
482     wxTreeItemData *p_data = treectrl->GetItemData( item );
483     if( !p_data ) return;
484
485     LockPlaylist( p_intf->p_sys, p_playlist );
486     playlist_item_t *p_item = playlist_ItemGetById( p_playlist,
487                                           ((PlaylistItem *)p_data)->i_id );
488     if( !p_item )
489     {
490         UnlockPlaylist( p_intf->p_sys, p_playlist );
491         return;
492     }
493
494     wxString msg;
495     wxString duration = wxU( "" );
496     char *psz_author = vlc_input_item_GetInfo( &p_item->input,
497                                                      _("Meta-information"),
498                                                      VLC_META_ARTIST );
499     if( !psz_author )
500     {
501         UnlockPlaylist( p_intf->p_sys, p_playlist );
502         return;
503     }
504
505     char psz_duration[MSTRTIME_MAX_SIZE];
506     mtime_t dur = p_item->input.i_duration;
507
508     if( dur != -1 )
509     {
510         secstotimestr( psz_duration, dur/1000000 );
511         duration.Append( wxU( " ( " ) +  wxString( wxU( psz_duration ) ) +
512                          wxU( " )" ) );
513     }
514
515     if( !strcmp( psz_author, "" ) || p_item->input.b_fixed_name == VLC_TRUE )
516     {
517         msg = wxString( wxU( p_item->input.psz_name ) ) + duration;
518     }
519     else
520     {
521         msg = wxString(wxU( psz_author )) + wxT(" - ") +
522                     wxString(wxU(p_item->input.psz_name)) + duration;
523     }
524     free( psz_author );
525     treectrl->SetItemText( item , msg );
526     treectrl->SetItemImage( item, p_item->input.i_type );
527
528     if( p_playlist->status.p_item == p_item )
529     {
530         treectrl->SetItemBold( item, true );
531         while( treectrl->GetItemParent( item ).IsOk() )
532         {
533             item = treectrl->GetItemParent( item );
534             treectrl->Expand( item );
535         }
536     }
537     else
538     {
539         treectrl->SetItemBold( item, false );
540     }
541     UnlockPlaylist( p_intf->p_sys, p_playlist );
542 }
543
544 /* Process a AppendIt em request */
545 void Playlist::AppendItem( wxCommandEvent& event )
546 {
547     playlist_add_t *p_add = (playlist_add_t *)event.GetClientData();
548     playlist_item_t *p_item = NULL;
549     wxTreeItemId item, node;
550
551     i_items_to_append--;
552
553     /* No need to do anything if the playlist is going to be rebuilt */
554     if( b_need_update ) return;
555
556     if( p_add->i_view != i_current_view ) goto update;
557
558     node = FindItem( treectrl->GetRootItem(), p_add->i_node );
559     if( !node.IsOk() ) goto update;
560
561     p_item = playlist_ItemGetById( p_playlist, p_add->i_item );
562     if( !p_item ) goto update;
563
564     item = FindItem( treectrl->GetRootItem(), p_add->i_item );
565     if( item.IsOk() ) goto update;
566
567     item = treectrl->AppendItem( node,
568                                  wxL2U( p_item->input.psz_name ), -1,-1,
569                                  new PlaylistItem( p_item ) );
570     treectrl->SetItemImage( item, p_item->input.i_type );
571
572     if( item.IsOk() && p_item->i_children == -1 )
573     {
574         UpdateTreeItem( item );
575     }
576
577 update:
578     int i_count = CountItems( treectrl->GetRootItem());
579     if( i_count != p_playlist->i_size )
580     {
581         statusbar->SetStatusText( wxString::Format( wxU(_(
582                                   "%i items in playlist (%i not shown)")),
583                                   p_playlist->i_size,
584                                   p_playlist->i_size - i_count ) );
585         if( !b_changed_view )
586         {
587             i_current_view = VIEW_CATEGORY;
588             b_changed_view = VLC_TRUE;
589             b_need_update = VLC_TRUE;
590         }
591     }
592     else
593     {
594         statusbar->SetStatusText( wxString::Format( wxU(_(
595                                   "%i items in playlist")),
596                                   p_playlist->i_size ), 0 );
597     }
598
599     return;
600 }
601
602 /* Process a updateitem request */
603 void Playlist::UpdateItem( int i )
604 {
605     if( i < 0 ) return; /* Sanity check */
606
607     wxTreeItemId item = FindItem( treectrl->GetRootItem(), i );
608
609     if( item.IsOk() )
610     {
611         UpdateTreeItem( item );
612     }
613 }
614
615 void Playlist::RemoveItem( int i )
616 {
617     if( i <= 0 ) return; /* Sanity check */
618     if( i == i_saved_id ) i_saved_id = -1;
619
620     wxTreeItemId item = FindItem( treectrl->GetRootItem(), i );
621
622     if( item.IsOk() )
623     {
624         treectrl->Delete( item );
625     }
626 }
627
628
629 /**********************************************************************
630  * Search functions (internal)
631  **********************************************************************/
632
633 /* Find a wxItem from a playlist id */
634 wxTreeItemId Playlist::FindItem( wxTreeItemId root, int i_id )
635 {
636     wxTreeItemIdValue cookie;
637     PlaylistItem *p_wxcurrent;
638     wxTreeItemId search;
639     wxTreeItemId item = treectrl->GetFirstChild( root, cookie );
640     wxTreeItemId child;
641
642     p_wxcurrent = (PlaylistItem *)treectrl->GetItemData( root );
643
644     if( i_id < 0 )
645     {
646         wxTreeItemId dummy;
647         return dummy;
648     }
649     if( i_saved_id == i_id )
650     {
651         return saved_tree_item;
652     }
653
654     if( !p_wxcurrent )
655     {
656         wxTreeItemId dummy;
657         return dummy;
658     }
659
660     if( p_wxcurrent->i_id == i_id )
661     {
662         i_saved_id = i_id;
663         saved_tree_item = root;
664         return root;
665     }
666
667     while( item.IsOk() )
668     {
669         p_wxcurrent = (PlaylistItem *)treectrl->GetItemData( item );
670         if( p_wxcurrent->i_id == i_id )
671         {
672             i_saved_id = i_id;
673             saved_tree_item = item;
674             return item;
675         }
676         if( treectrl->ItemHasChildren( item ) )
677         {
678             wxTreeItemId search = FindItem( item, i_id );
679             if( search.IsOk() )
680             {
681                 i_saved_id = i_id;
682                 saved_tree_item = search;
683                 return search;
684             }
685         }
686         item = treectrl->GetNextChild( root, cookie );
687     }
688     /* Not found */
689     wxTreeItemId dummy;
690     return dummy;
691 }
692
693 int Playlist::CountItems( wxTreeItemId root )
694 {
695     wxTreeItemIdValue cookie;
696     int count = 0;
697     wxTreeItemId item = treectrl->GetFirstChild( root, cookie );
698
699     while( item.IsOk() )
700     {
701         if( treectrl->ItemHasChildren( item ) )
702         {
703             count += CountItems( item );
704         }
705         else
706         {
707             playlist_item_t *p_item;
708             LockPlaylist( p_intf->p_sys, p_playlist );
709             p_item = playlist_ItemGetById( p_playlist, ((PlaylistItem *)treectrl->GetItemData( item ))->i_id );
710             if( p_item && p_item->i_children == -1 )
711                 count++;
712             UnlockPlaylist( p_intf->p_sys, p_playlist );
713         }
714         item = treectrl->GetNextChild( root, cookie );
715     }
716     return count;
717 }
718
719 /* Find a wxItem from a name (from current) */
720 wxTreeItemId Playlist::FindItemByName( wxTreeItemId root, wxString search_string, wxTreeItemId current, vlc_bool_t *pb_current_found )
721 {
722     wxTreeItemIdValue cookie;
723     wxTreeItemId search;
724     wxTreeItemId item = treectrl->GetFirstChild( root, cookie );
725     wxTreeItemId child;
726
727     while( item.IsOk() )
728     {
729         if( treectrl->GetItemText( item).Lower().Contains(
730                                                  search_string.Lower() ) )
731         {
732             if( !current.IsOk() || *pb_current_found == VLC_TRUE )
733             {
734                 return item;
735             }
736             else if( current.IsOk() && item == current )
737             {
738                 *pb_current_found = VLC_TRUE;
739             }
740         }
741         if( treectrl->ItemHasChildren( item ) )
742         {
743             wxTreeItemId search = FindItemByName( item, search_string, current,
744                                                   pb_current_found );
745             if( search.IsOk() )
746             {
747                 return search;
748             }
749         }
750         item = treectrl->GetNextChild( root, cookie);
751     }
752     /* Not found */
753     wxTreeItemId dummy;
754     return dummy;
755 }
756
757 /**********************************************************************
758  * Rebuild the playlist
759  **********************************************************************/
760 void Playlist::Rebuild( vlc_bool_t b_root )
761 {
762     playlist_view_t *p_view;
763
764     i_items_to_append = 0;
765
766     /* We can remove the callbacks before locking, anyway, we won't
767      * miss anything */
768     if( b_root )
769     {
770         var_DelCallback( p_playlist, "item-change", ItemChanged, this );
771         var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this );
772         var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
773         var_DelCallback( p_playlist, "item-append", ItemAppended, this );
774         var_DelCallback( p_playlist, "item-deleted", ItemDeleted, this );
775
776         /* ...and rebuild it */
777         LockPlaylist( p_intf->p_sys, p_playlist );
778     }
779     i_saved_id = -1;
780
781     p_view = playlist_ViewFind( p_playlist, i_current_view ); /* FIXME */
782
783     /* HACK we should really get new*/
784     treectrl->DeleteAllItems();
785     treectrl->AddRoot( wxU(_("root" )), -1, -1,
786                          new PlaylistItem( p_view->p_root) );
787
788     wxTreeItemId root = treectrl->GetRootItem();
789     UpdateNode( p_view->p_root, root );
790
791     int i_count = CountItems( treectrl->GetRootItem() );
792
793     if( i_count < p_playlist->i_size && !b_changed_view )
794     {
795         i_current_view = VIEW_CATEGORY;
796         b_changed_view = VLC_TRUE;
797         Rebuild( VLC_FALSE );
798     }
799     else if( i_count != p_playlist->i_size )
800     {
801         statusbar->SetStatusText( wxString::Format( wxU(_(
802                                   "%i items in playlist (%i not shown)")),
803                                   p_playlist->i_size,
804                                   p_playlist->i_size - i_count ) );
805     }
806     else
807     {
808         statusbar->SetStatusText( wxString::Format( wxU(_(
809                                   "%i items in playlist")),
810                                   p_playlist->i_size ), 0 );
811     }
812
813     if( b_root )
814     {
815         /* Put callbacks back online */
816         var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
817         var_AddCallback( p_playlist, "playlist-current", PlaylistNext, this );
818         var_AddCallback( p_playlist, "item-change", ItemChanged, this );
819         var_AddCallback( p_playlist, "item-append", ItemAppended, this );
820         var_AddCallback( p_playlist, "item-deleted", ItemDeleted, this );
821
822         UnlockPlaylist( p_intf->p_sys, p_playlist );
823     }
824 }
825
826
827
828 void Playlist::ShowPlaylist( bool show )
829 {
830     if( show ) Rebuild( VLC_TRUE );
831     Show( show );
832 }
833
834 /* This function is called on a regular basis */
835 void Playlist::UpdatePlaylist()
836 {
837     i_update_counter++;
838
839     /* If the playlist isn't show there's no need to update it */
840     if( !IsShown() ) return;
841
842     if( this->b_need_update )
843     {
844         this->b_need_update = VLC_FALSE;
845         Rebuild( VLC_TRUE );
846     }
847
848     /* Updating the playing status every 0.5s is enough */
849     if( i_update_counter % 5 ) return;
850 }
851
852 /*****************************************************************************
853  * Private methods.
854  *****************************************************************************/
855 void Playlist::DeleteTreeItem( wxTreeItemId item )
856 {
857    PlaylistItem *p_wxitem;
858    playlist_item_t *p_item;
859    p_wxitem = (PlaylistItem *)treectrl->GetItemData( item );
860
861    LockPlaylist( p_intf->p_sys, p_playlist );
862    p_item = playlist_ItemGetById( p_playlist, p_wxitem->i_id );
863
864    if( !p_item )
865    {
866        UnlockPlaylist( p_intf->p_sys, p_playlist );
867        return;
868    }
869
870    if( p_item->i_children == -1 ) DeleteItem( p_item->input.i_id );
871    else DeleteNode( p_item );
872
873    RemoveItem( item );
874    UnlockPlaylist( p_intf->p_sys, p_playlist );
875 }
876
877 void Playlist::DeleteItem( int item_id )
878 {
879     playlist_Delete( p_playlist, item_id );
880 }
881
882 void Playlist::DeleteNode( playlist_item_t *p_item )
883 {
884     playlist_NodeDelete( p_playlist, p_item, VLC_TRUE , VLC_FALSE );
885 }
886
887 void Playlist::OnMenuClose( wxCommandEvent& event )
888 {
889     wxCloseEvent cevent;
890     OnClose(cevent);
891 }
892
893 void Playlist::OnClose( wxCloseEvent& WXUNUSED(event) )
894 {
895     Hide();
896 }
897
898 void Playlist::OnSave( wxCommandEvent& WXUNUSED(event) )
899 {
900     struct {
901         char *psz_desc;
902         char *psz_filter;
903         char *psz_module;
904     } formats[] = {{ _("M3U file"), "*.m3u", "export-m3u" }};
905
906     wxString filter = wxT("");
907
908     if( p_playlist->i_size == 0 )
909     {
910         wxMessageBox( wxU(_("Playlist is empty") ), wxU(_("Can't save")),
911                       wxICON_WARNING | wxOK, this );
912         return;
913     }
914
915     for( unsigned int i = 0; i < sizeof(formats)/sizeof(formats[0]); i++)
916     {
917         filter.Append( wxU(formats[i].psz_desc) );
918         filter.Append( wxT("|") );
919         filter.Append( wxU(formats[i].psz_filter) );
920         filter.Append( wxT("|") );
921     }
922     wxFileDialog dialog( this, wxU(_("Save playlist")),
923                          wxT(""), wxT(""), filter, wxSAVE );
924
925     if( dialog.ShowModal() == wxID_OK )
926     {
927         if( dialog.GetPath().mb_str() )
928         {
929             playlist_Export( p_playlist, dialog.GetPath().mb_str(),
930                              formats[dialog.GetFilterIndex()].psz_module );
931         }
932     }
933
934 }
935
936 void Playlist::OnOpen( wxCommandEvent& WXUNUSED(event) )
937 {
938     wxFileDialog dialog( this, wxU(_("Open playlist")), wxT(""), wxT(""),
939         wxT("All playlists|*.pls;*.m3u;*.asx;*.b4s|M3U files|*.m3u"), wxOPEN );
940
941     if( dialog.ShowModal() == wxID_OK )
942     {
943         playlist_Import( p_playlist, dialog.GetPath().mb_str() );
944     }
945 }
946
947 void Playlist::OnAddFile( wxCommandEvent& WXUNUSED(event) )
948 {
949     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE_SIMPLE, 0, 0 );
950
951 }
952
953 void Playlist::OnAddDir( wxCommandEvent& WXUNUSED(event) )
954 {
955     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_DIRECTORY, 0, 0 );
956
957 }
958
959 void Playlist::OnAddMRL( wxCommandEvent& WXUNUSED(event) )
960 {
961     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE, 0, 0 );
962
963 }
964
965 /********************************************************************
966  * Sorting functions
967  ********************************************************************/
968 void Playlist::OnSort( wxCommandEvent& event )
969 {
970     PlaylistItem *p_wxitem;
971     p_wxitem = (PlaylistItem *)treectrl->GetItemData( treectrl->GetRootItem() );
972
973     LockPlaylist( p_intf->p_sys, p_playlist );
974     switch( event.GetId() )
975     {
976         case SortTitle_Event:
977             playlist_RecursiveNodeSort( p_playlist,
978                             playlist_ItemGetById( p_playlist, p_wxitem->i_id ),
979                             SORT_TITLE_NODES_FIRST, ORDER_NORMAL );
980             break;
981         case RSortTitle_Event:
982             playlist_RecursiveNodeSort( p_playlist,
983                             playlist_ItemGetById( p_playlist, p_wxitem->i_id ),
984                             SORT_TITLE_NODES_FIRST, ORDER_REVERSE );
985     }
986     UnlockPlaylist( p_intf->p_sys, p_playlist );
987
988     Rebuild( VLC_TRUE );
989 }
990
991 /**********************************************************************
992  * Search functions (user)
993  **********************************************************************/
994 /*void Playlist::OnSearchTextChange( wxCommandEvent& WXUNUSED(event) )
995 {
996    search_button->SetDefault();
997 }*/
998
999 void Playlist::OnSearch( wxCommandEvent& WXUNUSED(event) )
1000 {
1001     wxString search_string = search_text->GetValue();
1002
1003     vlc_bool_t pb_found = VLC_FALSE;
1004
1005     wxTreeItemId found =
1006      FindItemByName( treectrl->GetRootItem(), search_string,
1007                      search_current, &pb_found );
1008
1009     if( !found.IsOk() )
1010     {
1011         wxTreeItemId dummy;
1012         search_current = dummy;
1013         found =  FindItemByName( treectrl->GetRootItem(), search_string,
1014                                  search_current, &pb_found );
1015     }
1016
1017     if( found.IsOk() )
1018     {
1019         search_current = found;
1020         treectrl->EnsureVisible( found );
1021         treectrl->UnselectAll();
1022         treectrl->SelectItem( found, true );
1023     }
1024 }
1025
1026 /**********************************************************************
1027  * Selection functions
1028  **********************************************************************/
1029 void Playlist::RecursiveDeleteSelection(  wxTreeItemId root )
1030 {
1031     wxTreeItemIdValue cookie;
1032     wxTreeItemId child = treectrl->GetFirstChild( root, cookie );
1033     while( child.IsOk() )
1034     {
1035         if( treectrl->ItemHasChildren( child ) )
1036         {
1037             RecursiveDeleteSelection( child );
1038             if( treectrl->IsSelected(child ) ) DeleteTreeItem( child );
1039         }
1040         else if( treectrl->IsSelected( child ) )
1041             DeleteTreeItem( child );
1042         child = treectrl->GetNextChild( root, cookie );
1043     }
1044 }
1045
1046 void Playlist::OnDeleteSelection( wxCommandEvent& WXUNUSED(event) )
1047 {
1048     RecursiveDeleteSelection( treectrl->GetRootItem() );
1049 }
1050
1051 /**********************************************************************
1052  * Playlist mode functions
1053  **********************************************************************/
1054 void Playlist::OnRandom( wxCommandEvent& event )
1055 {
1056     vlc_value_t val;
1057     val.b_bool = event.IsChecked();
1058     var_Set( p_playlist, "random", val);
1059 }
1060
1061 void Playlist::OnLoop( wxCommandEvent& event )
1062 {
1063     vlc_value_t val;
1064     val.b_bool = event.IsChecked();
1065     var_Set( p_playlist, "loop", val);
1066 }
1067
1068 void Playlist::OnRepeat( wxCommandEvent& event )
1069 {
1070     vlc_value_t val;
1071     val.b_bool = event.IsChecked();
1072     var_Set( p_playlist, "repeat", val);
1073 }
1074
1075 /********************************************************************
1076  * Event
1077  ********************************************************************/
1078 void Playlist::OnActivateItem( wxTreeEvent& event )
1079 {
1080     playlist_item_t *p_item,*p_node,*p_item2,*p_node2;
1081
1082     PlaylistItem *p_wxitem = (PlaylistItem *)treectrl->GetItemData(
1083                                                             event.GetItem() );
1084     wxTreeItemId parent = treectrl->GetItemParent( event.GetItem() );
1085
1086     PlaylistItem *p_wxparent = (PlaylistItem *)treectrl->GetItemData( parent );
1087
1088     LockPlaylist( p_intf->p_sys, p_playlist );
1089
1090     if( !( p_wxitem && p_wxparent ) )
1091     {
1092         UnlockPlaylist( p_intf->p_sys, p_playlist );
1093         return;
1094     }
1095
1096     p_item2 = playlist_ItemGetById(p_playlist, p_wxitem->i_id);
1097     p_node2 = playlist_ItemGetById(p_playlist, p_wxparent->i_id);
1098     if( p_item2 && p_item2->i_children == -1 )
1099     {
1100         p_node = p_node2;
1101         p_item = p_item2;
1102     }
1103     else
1104     {
1105         p_node = p_item2;
1106         p_item = NULL;
1107 /*        if( p_node && p_node->i_children > 0 &&
1108             p_node->pp_children[0]->i_children == -1)
1109         {
1110             p_item = p_node->pp_children[0];
1111         }
1112         else
1113         {
1114             p_item = NULL;
1115         }*/
1116     }
1117
1118     playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view,
1119                       p_node, p_item );
1120     UnlockPlaylist( p_intf->p_sys, p_playlist );
1121 }
1122
1123 void Playlist::OnKeyDown( wxTreeEvent& event )
1124 {
1125     long keycode = event.GetKeyCode();
1126     /* Delete selected items */
1127     if( keycode == WXK_BACK || keycode == WXK_DELETE )
1128     {
1129         /* We send a dummy event */
1130         OnDeleteSelection( event );
1131     }
1132     else
1133     {
1134         event.Skip();
1135     }
1136 }
1137
1138 void Playlist::OnEnDis( wxCommandEvent& event )
1139 {
1140     msg_Warn( p_intf, "not implemented" );
1141 }
1142
1143 /**********************************************************************
1144  * Menu
1145  **********************************************************************/
1146
1147 void Playlist::OnMenuOpen( wxMenuEvent& event)
1148 {
1149 #if defined( __WXMSW__ )
1150 #   define GetEventObject GetMenu
1151 #endif
1152
1153     if( event.GetEventObject() == p_view_menu )
1154     {
1155         p_view_menu = ViewMenu();
1156     }
1157 #if defined( __WXMSW__ )
1158 #   undef GetEventObject
1159 #endif
1160 }
1161
1162 void Playlist::OnMenuEvent( wxCommandEvent& event )
1163 {
1164     if( event.GetId() < FirstView_Event )
1165     {
1166         event.Skip();
1167         return;
1168     }
1169     else if( event.GetId() < LastView_Event )
1170     {
1171
1172         int i_new_view = event.GetId() - FirstView_Event;
1173
1174         playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_new_view );
1175
1176         if( p_view != NULL )
1177         {
1178             b_changed_view = VLC_TRUE;
1179             i_current_view = i_new_view;
1180             playlist_ViewUpdate( p_playlist, i_new_view );
1181             Rebuild( VLC_TRUE );
1182             return;
1183         }
1184         else if( i_new_view >= VIEW_FIRST_SORTED &&
1185                  i_new_view <= VIEW_LAST_SORTED )
1186         {
1187             b_changed_view = VLC_TRUE;
1188             playlist_ViewInsert( p_playlist, i_new_view, "View" );
1189             playlist_ViewUpdate( p_playlist, i_new_view );
1190
1191             i_current_view = i_new_view;
1192
1193             Rebuild( VLC_TRUE );
1194         }
1195     }
1196     else if( event.GetId() >= FirstSD_Event && event.GetId() < LastSD_Event )
1197     {
1198         if( !playlist_IsServicesDiscoveryLoaded( p_playlist,
1199                                 pp_sds[event.GetId() - FirstSD_Event] ) )
1200         {
1201             playlist_ServicesDiscoveryAdd( p_playlist,
1202                             pp_sds[event.GetId() - FirstSD_Event] );
1203         }
1204         else
1205         {
1206             //wxMutexGuiLeave();
1207             playlist_ServicesDiscoveryRemove( p_playlist,
1208                             pp_sds[event.GetId() - FirstSD_Event] );
1209             //wxMutexGuiEnter();
1210         }
1211     }
1212 }
1213
1214 wxMenu * Playlist::ViewMenu()
1215 {
1216     if( !p_view_menu )
1217     {
1218         p_view_menu = new wxMenu;
1219     }
1220     else
1221     {
1222         wxMenuItemList::Node *node = p_view_menu->GetMenuItems().GetFirst();
1223         for( ; node; )
1224         {
1225             wxMenuItem *item = node->GetData();
1226             node = node->GetNext();
1227             p_view_menu->Delete( item );
1228         }
1229     }
1230
1231     /* FIXME : have a list of "should have" views */
1232     p_view_menu->Append( FirstView_Event + VIEW_CATEGORY,
1233                            wxU(_("Normal") ) );
1234     p_view_menu->Append( FirstView_Event + VIEW_S_AUTHOR,
1235                            wxU(_("Sorted by artist") ) );
1236     p_view_menu->Append( FirstView_Event + VIEW_S_ALBUM,
1237                            wxU(_("Sorted by Album") ) );
1238
1239     return p_view_menu;
1240 }
1241
1242 wxMenu *Playlist::SDMenu()
1243 {
1244     p_sd_menu = new wxMenu;
1245
1246     vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
1247                                         FIND_ANYWHERE );
1248
1249     int i_number = 0;
1250     for( int i_index = 0; i_index < p_list->i_count; i_index++ )
1251     {
1252         module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
1253
1254         if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
1255             i_number++;
1256     }
1257     if( i_number ) pp_sds = (char **)calloc( i_number, sizeof(void *) );
1258
1259     i_number = 0;
1260     for( int i_index = 0; i_index < p_list->i_count; i_index++ )
1261     {
1262         module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
1263
1264         if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
1265         {
1266             p_sd_menu->AppendCheckItem( FirstSD_Event + i_number ,
1267                 wxU( p_parser->psz_longname ? p_parser->psz_longname :
1268                      (p_parser->psz_shortname ?
1269                       p_parser->psz_shortname : p_parser->psz_object_name) ) );
1270
1271             if( playlist_IsServicesDiscoveryLoaded( p_playlist,
1272                                     p_parser->psz_object_name ) )
1273             {
1274                 p_sd_menu->Check( FirstSD_Event + i_number, TRUE );
1275             }
1276
1277             pp_sds[i_number++] = p_parser->psz_object_name;
1278         }
1279     }
1280     vlc_list_release( p_list );
1281     return p_sd_menu;
1282 }
1283
1284
1285 /*****************************************************************************
1286  * Popup management functions
1287  *****************************************************************************/
1288 void Playlist::OnPopup( wxContextMenuEvent& event )
1289 {
1290     wxPoint pt = event.GetPosition();
1291     playlist_item_t *p_item;
1292
1293     i_wx_popup_item = treectrl->HitTest( ScreenToClient( pt ) );
1294     if( i_wx_popup_item.IsOk() )
1295     {
1296         PlaylistItem *p_wxitem = (PlaylistItem *)treectrl->GetItemData(
1297                                                             i_wx_popup_item );
1298         PlaylistItem *p_wxparent= (PlaylistItem *)treectrl->GetItemData(
1299                                   treectrl->GetItemParent( i_wx_popup_item ) );
1300         i_popup_item = p_wxitem->i_id;
1301         i_popup_parent = p_wxparent->i_id;
1302         treectrl->SelectItem( i_wx_popup_item );
1303
1304         LockPlaylist( p_intf->p_sys, p_playlist );
1305         p_item = playlist_ItemGetById( p_playlist, i_popup_item );
1306
1307         if( !p_item )
1308         {
1309             UnlockPlaylist( p_intf->p_sys, p_playlist );
1310             return;
1311         }
1312         if( p_item->i_children == -1 )
1313         {
1314             UnlockPlaylist( p_intf->p_sys, p_playlist );
1315             Playlist::PopupMenu( item_popup,
1316                                  ScreenToClient( wxGetMousePosition() ) );
1317         }
1318         else
1319         {
1320             UnlockPlaylist( p_intf->p_sys, p_playlist );
1321             Playlist::PopupMenu( node_popup,
1322                                  ScreenToClient( wxGetMousePosition() ) );
1323         }
1324     }
1325 }
1326
1327 void Playlist::OnPopupPlay( wxCommandEvent& event )
1328 {
1329     playlist_item_t *p_popup_item, *p_popup_parent;
1330     LockPlaylist( p_intf->p_sys, p_playlist );
1331     p_popup_item = playlist_ItemGetById( p_playlist, i_popup_item );
1332     p_popup_parent = playlist_ItemGetById( p_playlist, i_popup_parent );
1333     if( p_popup_item != NULL )
1334     {
1335         if( p_popup_item->i_children > -1 )
1336         {
1337             if( event.GetId() == PopupPlay_Event &&
1338                 p_popup_item->i_children > 0 )
1339             {
1340                 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
1341                                   i_current_view, p_popup_item,
1342                                   p_popup_item->pp_children[0] );
1343             }
1344             else
1345             {
1346                 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
1347                                   i_current_view, p_popup_item, NULL );
1348             }
1349         }
1350         else
1351         {
1352             if( event.GetId() == PopupPlay_Event )
1353             {
1354                 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
1355                                   i_current_view, p_popup_parent,
1356                                   p_popup_item );
1357             }
1358         }
1359     }
1360     UnlockPlaylist( p_intf->p_sys, p_playlist );
1361 }
1362
1363 void Playlist::OnPopupPreparse( wxCommandEvent& event )
1364 {
1365     Preparse();
1366 }
1367
1368 void Playlist::Preparse()
1369 {
1370     playlist_item_t *p_popup_item;
1371     LockPlaylist( p_intf->p_sys, p_playlist );
1372     p_popup_item = playlist_ItemGetById( p_playlist, i_popup_item );
1373
1374     if( p_popup_item != NULL )
1375     {
1376         if( p_popup_item->i_children == -1 )
1377         {
1378             playlist_PreparseEnqueue( p_playlist, &p_popup_item->input );
1379         }
1380         else
1381         {
1382             int i = 0;
1383             playlist_item_t *p_parent = p_popup_item;
1384             for( i = 0; i< p_parent->i_children ; i++ )
1385             {
1386                 wxMenuEvent dummy;
1387                 i_wx_popup_item = FindItem( treectrl->GetRootItem(),
1388                                          p_parent->pp_children[i]->input.i_id );
1389                 i_popup_item = p_parent->pp_children[i]->input.i_id;
1390                 Preparse();
1391             }
1392         }
1393     }
1394     UnlockPlaylist( p_intf->p_sys, p_playlist );
1395 }
1396
1397 void Playlist::OnPopupDel( wxCommandEvent& event )
1398 {
1399     DeleteTreeItem( i_wx_popup_item );
1400 }
1401
1402 void Playlist::OnPopupSort( wxCommandEvent& event )
1403 {
1404     PlaylistItem *p_wxitem;
1405     playlist_item_t *p_item;
1406
1407     p_wxitem = (PlaylistItem *)treectrl->GetItemData( i_wx_popup_item );
1408     LockPlaylist( p_intf->p_sys, p_playlist );
1409
1410     p_item = playlist_ItemGetById( p_playlist, p_wxitem->i_id );
1411     if( p_item->i_children >= 0 )
1412     {
1413         playlist_RecursiveNodeSort( p_playlist, p_item,
1414                                     SORT_TITLE_NODES_FIRST, ORDER_NORMAL );
1415
1416         treectrl->DeleteChildren( i_wx_popup_item );
1417         i_saved_id = -1;
1418         UpdateNodeChildren( p_item, i_wx_popup_item );
1419
1420     }
1421     UnlockPlaylist( p_intf->p_sys, p_playlist );
1422 }
1423
1424 void Playlist::OnPopupInfo( wxCommandEvent& event )
1425 {
1426     LockPlaylist( p_intf->p_sys, p_playlist );
1427     playlist_item_t *p_popup_item = playlist_ItemGetById( p_playlist, i_popup_item );
1428     if( p_popup_item )
1429     {
1430         iteminfo_dialog = new ItemInfoDialog( p_intf, p_popup_item, this );
1431         if( iteminfo_dialog->ShowModal() == wxID_OK )
1432         {
1433             UpdateItem( i_wx_popup_item );
1434         }
1435         delete iteminfo_dialog;
1436     }
1437     UnlockPlaylist( p_intf->p_sys, p_playlist );
1438 }
1439
1440
1441 /*****************************************************************************
1442  * Custom events management
1443  *****************************************************************************/
1444 void Playlist::OnPlaylistEvent( wxCommandEvent& event )
1445 {
1446     switch( event.GetId() )
1447     {
1448         case UpdateItem_Event:
1449             UpdateItem( event.GetInt() );
1450             break;
1451         case AppendItem_Event:
1452             AppendItem( event );
1453             break;
1454         case RemoveItem_Event:
1455             RemoveItem( event.GetInt() );
1456             break;
1457     }
1458 }
1459
1460 /*****************************************************************************
1461  * PlaylistChanged: callback triggered by the intf-change playlist variable
1462  *  We don't rebuild the playlist directly here because we don't want the
1463  *  caller to block for a too long time.
1464  *****************************************************************************/
1465 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
1466                             vlc_value_t oval, vlc_value_t nval, void *param )
1467 {
1468     Playlist *p_playlist_dialog = (Playlist *)param;
1469     p_playlist_dialog->b_need_update = VLC_TRUE;
1470     return VLC_SUCCESS;
1471 }
1472
1473 /*****************************************************************************
1474  * Next: callback triggered by the playlist-current playlist variable
1475  *****************************************************************************/
1476 static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
1477                          vlc_value_t oval, vlc_value_t nval, void *param )
1478 {
1479     Playlist *p_playlist_dialog = (Playlist *)param;
1480
1481     wxCommandEvent event( wxEVT_PLAYLIST, UpdateItem_Event );
1482     event.SetInt( oval.i_int );
1483     p_playlist_dialog->AddPendingEvent( event );
1484     event.SetInt( nval.i_int );
1485     p_playlist_dialog->AddPendingEvent( event );
1486
1487     return 0;
1488 }
1489
1490 /*****************************************************************************
1491  * ItemChanged: callback triggered by the item-change playlist variable
1492  *****************************************************************************/
1493 static int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
1494                         vlc_value_t old_val, vlc_value_t new_val, void *param )
1495 {
1496     Playlist *p_playlist_dialog = (Playlist *)param;
1497
1498     wxCommandEvent event( wxEVT_PLAYLIST, UpdateItem_Event );
1499     event.SetInt( new_val.i_int );
1500     p_playlist_dialog->AddPendingEvent( event );
1501
1502     return 0;
1503 }
1504 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
1505                         vlc_value_t old_val, vlc_value_t new_val, void *param )
1506 {
1507     Playlist *p_playlist_dialog = (Playlist *)param;
1508
1509     wxCommandEvent event( wxEVT_PLAYLIST, RemoveItem_Event );
1510     event.SetInt( new_val.i_int );
1511     p_playlist_dialog->AddPendingEvent( event );
1512
1513     return 0;
1514 }
1515
1516 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
1517                          vlc_value_t oval, vlc_value_t nval, void *param )
1518 {
1519     Playlist *p_playlist_dialog = (Playlist *)param;
1520
1521     playlist_add_t *p_add = (playlist_add_t *)malloc(sizeof( playlist_add_t));
1522     memcpy( p_add, nval.p_address, sizeof( playlist_add_t ) );
1523
1524     if( p_playlist_dialog->i_items_to_append++ > 50 )
1525     {
1526         /* Too many items waiting to be added, it will be quicker to rebuild
1527          * the whole playlist */
1528         p_playlist_dialog->b_need_update = VLC_TRUE;
1529         return VLC_SUCCESS;
1530     }
1531
1532     wxCommandEvent event( wxEVT_PLAYLIST, AppendItem_Event );
1533     event.SetClientData( (void *)p_add );
1534     p_playlist_dialog->AddPendingEvent( event );
1535
1536     return VLC_SUCCESS;
1537 }
1538 }