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