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