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