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