]> git.sesse.net Git - vlc/blob - modules/gui/wxwindows/playlist.cpp
a51d09ea2814e5d3f646a354de9b2e48c653b1e6
[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  * Event Table.
65  *****************************************************************************/
66
67 /* IDs for the controls and the menu commands */
68 enum
69 {
70     /* menu items */
71     AddFile_Event = 1,
72     AddDir_Event,
73     AddMRL_Event,
74     Close_Event,
75     Open_Event,
76     Save_Event,
77
78     SortTitle_Event,
79     RSortTitle_Event,
80     Randomize_Event,
81
82     InvertSelection_Event,
83     DeleteSelection_Event,
84     Random_Event,
85     Loop_Event,
86     Repeat_Event,
87     SelectAll_Event,
88
89     PopupPlay_Event,
90     PopupPlayThis_Event,
91     PopupPreparse_Event,
92     PopupSort_Event,
93     PopupDel_Event,
94     PopupInfo_Event,
95
96     SearchText_Event,
97     Search_Event,
98
99     /* controls */
100     TreeCtrl_Event,
101
102     Browse_Event,  /* For export playlist */
103
104     /* custom events */
105     UpdateItem_Event,
106     AppendItem_Event,
107     RemoveItem_Event,
108
109     MenuDummy_Event = wxID_HIGHEST + 999,
110
111     FirstView_Event = wxID_HIGHEST + 1000,
112     LastView_Event = wxID_HIGHEST + 1100,
113
114     FirstSD_Event = wxID_HIGHEST + 2000,
115     LastSD_Event = wxID_HIGHEST + 2100,
116 };
117
118 DEFINE_LOCAL_EVENT_TYPE( wxEVT_PLAYLIST );
119
120 BEGIN_EVENT_TABLE(Playlist, wxFrame)
121     EVT_SIZE(Playlist::OnSize)
122
123     /* Menu events */
124     EVT_MENU(AddFile_Event, Playlist::OnAddFile)
125     EVT_MENU(AddDir_Event, Playlist::OnAddDir)
126     EVT_MENU(AddMRL_Event, Playlist::OnAddMRL)
127     EVT_MENU(Close_Event, Playlist::OnClose)
128     EVT_MENU(Open_Event, Playlist::OnOpen)
129     EVT_MENU(Save_Event, Playlist::OnSave)
130
131     EVT_MENU(SortTitle_Event, Playlist::OnSort)
132     EVT_MENU(RSortTitle_Event, Playlist::OnSort)
133
134     EVT_MENU(Randomize_Event, Playlist::OnSort)
135
136     EVT_MENU(InvertSelection_Event, Playlist::OnInvertSelection)
137     EVT_MENU(DeleteSelection_Event, Playlist::OnDeleteSelection)
138     EVT_MENU(SelectAll_Event, Playlist::OnSelectAll)
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
158     EVT_CONTEXT_MENU( Playlist::OnPopup )
159
160     /* Button events */
161     EVT_BUTTON( Search_Event, Playlist::OnSearch)
162     EVT_BUTTON( Save_Event, Playlist::OnSave)
163
164     EVT_TEXT(SearchText_Event, Playlist::OnSearchTextChange)
165
166     /* Custom events */
167     EVT_COMMAND(-1, wxEVT_PLAYLIST, Playlist::OnPlaylistEvent)
168
169     /* Special events : we don't want to destroy the window when the user
170      * clicks on (X) */
171     EVT_CLOSE(Playlist::OnClose)
172 END_EVENT_TABLE()
173
174 /*****************************************************************************
175  * PlaylistItem class
176  ****************************************************************************/
177 class PlaylistItem : public wxTreeItemData
178 {
179 public:
180     PlaylistItem( playlist_item_t *_p_item ) : wxTreeItemData()
181     {
182         p_item = _p_item;
183         i_id = p_item->input.i_id;
184     }
185 protected:
186     playlist_item_t *p_item;
187     int i_id;
188 friend class Playlist;
189 };
190
191 /*****************************************************************************
192  * Constructor.
193  *****************************************************************************/
194 Playlist::Playlist( intf_thread_t *_p_intf, wxWindow *p_parent ):
195     wxFrame( p_parent, -1, wxU(_("Playlist")), wxDefaultPosition,
196              wxSize(345,400), wxDEFAULT_FRAME_STYLE )
197 {
198     vlc_value_t val;
199
200     /* Initializations */
201     p_intf = _p_intf;
202     i_update_counter = 0;
203     i_sort_mode = MODE_NONE;
204     b_need_update = VLC_FALSE;
205     SetIcon( *p_intf->p_sys->p_icon );
206
207     p_view_menu = NULL;
208     p_sd_menu = SDMenu();
209
210     i_current_view = VIEW_SIMPLE;
211     b_changed_view = VLC_FALSE;
212
213     i_title_sorted = 0;
214     i_group_sorted = 0;
215     i_duration_sorted = 0;
216
217     var_Create( p_intf, "random", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
218     var_Create( p_intf, "loop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
219     var_Create( p_intf, "repeat", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );;
220
221     /* Create our "Manage" menu */
222     wxMenu *manage_menu = new wxMenu;
223     manage_menu->Append( AddFile_Event, wxU(_("&Simple Add File...")) );
224     manage_menu->Append( AddDir_Event, wxU(_("Add &Directory...")) );
225     manage_menu->Append( AddMRL_Event, wxU(_("&Add MRL...")) );
226     manage_menu->AppendSeparator();
227     manage_menu->Append( MenuDummy_Event, wxU(_("Services discovery")),
228                          p_sd_menu );
229     manage_menu->AppendSeparator();
230     manage_menu->Append( Open_Event, wxU(_("&Open Playlist...")) );
231     manage_menu->Append( Save_Event, wxU(_("&Save Playlist...")) );
232     manage_menu->AppendSeparator();
233     manage_menu->Append( Close_Event, wxU(_("&Close")) );
234
235     /* Create our "Sort" menu */
236     wxMenu *sort_menu = new wxMenu;
237     sort_menu->Append( SortTitle_Event, wxU(_("Sort by &title")) );
238     sort_menu->Append( RSortTitle_Event, wxU(_("&Reverse sort by title")) );
239     sort_menu->AppendSeparator();
240     sort_menu->Append( Randomize_Event, wxU(_("&Shuffle Playlist")) );
241
242     /* Create our "Selection" menu */
243     wxMenu *selection_menu = new wxMenu;
244     selection_menu->Append( InvertSelection_Event, wxU(_("&Invert")) );
245     selection_menu->Append( DeleteSelection_Event, wxU(_("D&elete")) );
246     selection_menu->Append( SelectAll_Event, wxU(_("&Select All")) );
247
248     /* Create our "View" menu */
249     ViewMenu();
250
251     /* Append the freshly created menus to the menu bar */
252     wxMenuBar *menubar = new wxMenuBar( wxMB_DOCKABLE );
253     menubar->Append( manage_menu, wxU(_("&Manage")) );
254     menubar->Append( sort_menu, wxU(_("S&ort")) );
255     menubar->Append( selection_menu, wxU(_("&Selection")) );
256     menubar->Append( p_view_menu, wxU(_("&View items") ) );
257
258     /* Attach the menu bar to the frame */
259     SetMenuBar( menubar );
260
261     /* Create the popup menu */
262     node_popup = new wxMenu;
263     node_popup->Append( PopupPlay_Event, wxU(_("Play")) );
264     node_popup->Append( PopupPlayThis_Event, wxU(_("Play this branch")) );
265     node_popup->Append( PopupPreparse_Event, wxU(_("Preparse")) );
266     node_popup->Append( PopupSort_Event, wxU(_("Sort this branch")) );
267     node_popup->Append( PopupDel_Event, wxU(_("Delete")) );
268     node_popup->Append( PopupInfo_Event, wxU(_("Info")) );
269
270     item_popup = new wxMenu;
271     item_popup->Append( PopupPlay_Event, wxU(_("Play")) );
272     item_popup->Append( PopupPreparse_Event, wxU(_("Preparse")) );
273     item_popup->Append( PopupDel_Event, wxU(_("Delete")) );
274     item_popup->Append( PopupInfo_Event, wxU(_("Info")) );
275
276     /* Create a panel to put everything in */
277     wxPanel *playlist_panel = new wxPanel( this, -1 );
278     playlist_panel->SetAutoLayout( TRUE );
279
280     /* Create the toolbar */
281     wxToolBar *toolbar =
282         CreateToolBar( wxTB_HORIZONTAL | wxTB_FLAT | wxTB_DOCKABLE );
283
284     /* Create the random tool */
285     toolbar->AddTool( Random_Event, wxT(""), wxBitmap(shuffle_on_xpm),
286                        wxBitmap(shuffle_on_xpm), wxITEM_CHECK,
287                        wxU(_(HELP_SHUFFLE) ) );
288     var_Get( p_intf, "random", &val );
289     toolbar->ToggleTool( Random_Event, val.b_bool );
290
291     /* Create the Loop tool */
292     toolbar->AddTool( Loop_Event, wxT(""), wxBitmap( loop_xpm),
293                       wxBitmap( loop_xpm), wxITEM_CHECK,
294                       wxU(_(HELP_LOOP )  ) );
295     var_Get( p_intf, "loop", &val );
296     toolbar->ToggleTool( Loop_Event, val.b_bool );
297
298     /* Create the Repeat one checkbox */
299     toolbar->AddTool( Repeat_Event, wxT(""), wxBitmap( repeat_xpm),
300                       wxBitmap( repeat_xpm), wxITEM_CHECK,
301                       wxU(_(HELP_REPEAT )  ) );
302     var_Get( p_intf, "repeat", &val );
303     toolbar->ToggleTool( Repeat_Event, val.b_bool ) ;
304
305     /* Create the Search Textbox */
306     search_text = new wxTextCtrl( toolbar, SearchText_Event, wxT(""),
307                                   wxDefaultPosition, wxSize(100, -1),
308                                   wxTE_PROCESS_ENTER);
309
310     /* Create the search button */
311     search_button = new wxButton( toolbar , Search_Event, wxU(_("Search")) );
312
313     toolbar->AddControl( new wxControl( toolbar, -1, wxDefaultPosition,
314                          wxSize(16, 16), wxBORDER_NONE ) );
315     toolbar->AddControl( search_text );
316     toolbar->AddControl( new wxControl( toolbar, -1, wxDefaultPosition,
317                          wxSize(5, 5), wxBORDER_NONE ) );
318     toolbar->AddControl( search_button );
319     search_button->SetDefault();
320     toolbar->Realize();
321
322     /* Create the tree */
323     treectrl = new wxTreeCtrl( playlist_panel, TreeCtrl_Event,
324                                wxDefaultPosition, wxDefaultSize,
325                                wxTR_HIDE_ROOT | wxTR_LINES_AT_ROOT|
326                                wxTR_NO_LINES |
327                                wxTR_HAS_BUTTONS | wxTR_TWIST_BUTTONS |
328                                wxTR_MULTIPLE | wxTR_EXTENDED );
329
330     /* Create image list */
331     wxImageList *p_images = new wxImageList( 16 , 16, TRUE );
332
333     /* FIXME: absolutely needs to be in the right order FIXME */
334     p_images->Add( wxIcon( type_unknown_xpm ) );
335     p_images->Add( wxIcon( type_unknown_xpm ) );
336     p_images->Add( wxIcon( type_directory_xpm ) );
337     p_images->Add( wxIcon( type_disc_xpm ) );
338     p_images->Add( wxIcon( type_card_xpm ) );
339     p_images->Add( wxIcon( type_net_xpm ) );
340     p_images->Add( wxIcon( type_playlist_xpm ) );
341     treectrl->AssignImageList( p_images );
342
343     treectrl->AddRoot( wxU(_("root" )), -1, -1, NULL );
344
345     /* Reduce font size */
346     wxFont font= treectrl->GetFont();
347     font.SetPointSize(8);
348     treectrl->SetFont( font );
349
350     wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
351     panel_sizer->Add( treectrl, 1, wxEXPAND | wxALL, 5 );
352     panel_sizer->Layout();
353
354     playlist_panel->SetSizerAndFit( panel_sizer );
355
356     int pi_widths[1] =  { -1 };
357     statusbar = CreateStatusBar( 1 );
358     statusbar->SetStatusWidths( 1, pi_widths );
359
360 #if wxUSE_DRAG_AND_DROP
361     /* Associate drop targets with the playlist */
362     SetDropTarget( new DragAndDrop( p_intf, VLC_TRUE ) );
363 #endif
364
365     playlist_t *p_playlist =
366         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
367                                        FIND_ANYWHERE );
368     if( p_playlist == NULL )
369     {
370         return;
371     }
372
373     /* We want to be noticed of playlist changes */
374
375     /* Some global changes happened -> Rebuild all */
376     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
377
378     /* We went to the next item */
379     var_AddCallback( p_playlist, "playlist-current", PlaylistNext, this );
380
381     /* One item has been updated */
382     var_AddCallback( p_playlist, "item-change", ItemChanged, this );
383
384     var_AddCallback( p_playlist, "item-append", ItemAppended, this );
385     var_AddCallback( p_playlist, "item-deleted", ItemDeleted, this );
386
387     vlc_object_release( p_playlist );
388
389
390     /* Update the playlist */
391     Rebuild();
392 }
393
394 Playlist::~Playlist()
395 {
396     playlist_t *p_playlist =
397         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
398                                        FIND_ANYWHERE );
399     if( p_playlist == NULL )
400     {
401         return;
402     }
403
404     var_DelCallback( p_playlist, "item-change", ItemChanged, this );
405     var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this );
406     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
407     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_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                             p_parser->psz_shortname :p_parser->psz_object_name)) );
1388
1389             if( playlist_IsServicesDiscoveryLoaded( p_playlist,
1390                                     p_parser->psz_object_name ) )
1391             {
1392                 p_sd_menu->Check( FirstSD_Event + i_number, TRUE );
1393             }
1394
1395             INSERT_ELEM( (void**)pp_sds, i_number, i_number,
1396                          (void*)p_parser->psz_object_name );
1397         }
1398     }
1399     vlc_list_release( p_list );
1400     vlc_object_release( p_playlist );
1401     return p_sd_menu;
1402 }
1403
1404
1405 /*****************************************************************************
1406  * Popup management functions
1407  *****************************************************************************/
1408 void Playlist::OnPopup( wxContextMenuEvent& event )
1409 {
1410     wxPoint pt = event.GetPosition();
1411
1412     i_popup_item = treectrl->HitTest( ScreenToClient( pt ) );
1413     if( i_popup_item.IsOk() )
1414     {
1415         PlaylistItem *p_wxitem = (PlaylistItem *)treectrl->GetItemData(
1416                                                             i_popup_item );
1417         PlaylistItem *p_wxparent= (PlaylistItem *) treectrl->GetItemData(
1418                                    treectrl->GetItemParent( i_popup_item ) );
1419         p_popup_item = p_wxitem->p_item;
1420         p_popup_parent = p_wxparent->p_item;
1421         treectrl->SelectItem( i_popup_item );
1422         if( p_popup_item->i_children == -1 )
1423             Playlist::PopupMenu( item_popup,
1424                                  ScreenToClient( wxGetMousePosition() ) );
1425         else
1426             Playlist::PopupMenu( node_popup,
1427                                  ScreenToClient( wxGetMousePosition() ) );
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     Preparse( p_playlist );
1480     vlc_object_release( p_playlist );
1481 }
1482
1483 void Playlist::Preparse( playlist_t *p_playlist )
1484 {
1485     if( p_popup_item != NULL )
1486     {
1487         if( p_popup_item->i_children == -1 )
1488         {
1489             playlist_PreparseEnqueue( p_playlist, &p_popup_item->input );
1490         }
1491         else
1492         {
1493             int i = 0;
1494             playlist_item_t *p_parent = p_popup_item;
1495             for( i = 0; i< p_parent->i_children ; i++ )
1496             {
1497                 wxMenuEvent dummy;
1498                 i_popup_item = FindItem( treectrl->GetRootItem(),
1499                                          p_parent->pp_children[i] );
1500                 p_popup_item = p_parent->pp_children[i];
1501                 Preparse( p_playlist );
1502             }
1503         }
1504     }
1505 }
1506
1507 void Playlist::OnPopupDel( wxMenuEvent& event )
1508 {
1509     PlaylistItem *p_wxitem;
1510
1511     p_wxitem = (PlaylistItem *)treectrl->GetItemData( i_popup_item );
1512
1513     if( p_wxitem->p_item->i_children == -1 )
1514     {
1515         DeleteItem( p_wxitem->p_item->input.i_id );
1516     }
1517     else
1518     {
1519         DeleteNode( p_wxitem->p_item );
1520     }
1521 }
1522
1523 void Playlist::OnPopupSort( wxMenuEvent& event )
1524 {
1525     PlaylistItem *p_wxitem;
1526
1527     p_wxitem = (PlaylistItem *)treectrl->GetItemData( i_popup_item );
1528
1529     if( p_wxitem->p_item->i_children >= 0 )
1530     {
1531         playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf,
1532                                         VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
1533
1534         if( p_playlist )
1535         {
1536             vlc_mutex_lock( &p_playlist->object_lock );
1537             playlist_RecursiveNodeSort( p_playlist, p_wxitem->p_item,
1538                                         SORT_TITLE_NODES_FIRST, ORDER_NORMAL );
1539             vlc_mutex_unlock( &p_playlist->object_lock );
1540
1541             treectrl->DeleteChildren( i_popup_item );
1542             UpdateNodeChildren( p_playlist, p_wxitem->p_item, i_popup_item );
1543
1544             vlc_object_release( p_playlist );
1545         }
1546     }
1547 }
1548
1549 void Playlist::OnPopupInfo( wxMenuEvent& event )
1550 {
1551     if( p_popup_item )
1552     {
1553         iteminfo_dialog = new ItemInfoDialog( p_intf, p_popup_item, this );
1554         if( iteminfo_dialog->ShowModal() == wxID_OK )
1555         {
1556             UpdateItem( i_popup_item );
1557         }
1558         delete iteminfo_dialog;
1559     }
1560 }
1561
1562
1563 /*****************************************************************************
1564  * Custom events management
1565  *****************************************************************************/
1566 void Playlist::OnPlaylistEvent( wxCommandEvent& event )
1567 {
1568     switch( event.GetId() )
1569     {
1570         case UpdateItem_Event:
1571             UpdateItem( event.GetInt() );
1572             break;
1573         case AppendItem_Event:
1574             AppendItem( event );
1575             break;
1576         case RemoveItem_Event:
1577             RemoveItem( event.GetInt() );
1578             break;
1579     }
1580 }
1581
1582 /*****************************************************************************
1583  * PlaylistChanged: callback triggered by the intf-change playlist variable
1584  *  We don't rebuild the playlist directly here because we don't want the
1585  *  caller to block for a too long time.
1586  *****************************************************************************/
1587 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
1588                             vlc_value_t oval, vlc_value_t nval, void *param )
1589 {
1590     Playlist *p_playlist_dialog = (Playlist *)param;
1591     p_playlist_dialog->b_need_update = VLC_TRUE;
1592     return VLC_SUCCESS;
1593 }
1594
1595 /*****************************************************************************
1596  * Next: callback triggered by the playlist-current playlist variable
1597  *****************************************************************************/
1598 static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
1599                          vlc_value_t oval, vlc_value_t nval, void *param )
1600 {
1601     Playlist *p_playlist_dialog = (Playlist *)param;
1602
1603     wxCommandEvent event( wxEVT_PLAYLIST, UpdateItem_Event );
1604     event.SetInt( oval.i_int );
1605     p_playlist_dialog->AddPendingEvent( event );
1606     event.SetInt( nval.i_int );
1607     p_playlist_dialog->AddPendingEvent( event );
1608
1609     return 0;
1610 }
1611
1612 /*****************************************************************************
1613  * ItemChanged: callback triggered by the item-change playlist variable
1614  *****************************************************************************/
1615 static int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
1616                         vlc_value_t old_val, vlc_value_t new_val, void *param )
1617 {
1618     Playlist *p_playlist_dialog = (Playlist *)param;
1619
1620     wxCommandEvent event( wxEVT_PLAYLIST, UpdateItem_Event );
1621     event.SetInt( new_val.i_int );
1622     p_playlist_dialog->AddPendingEvent( event );
1623
1624     return 0;
1625 }
1626 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
1627                         vlc_value_t old_val, vlc_value_t new_val, void *param )
1628 {
1629     Playlist *p_playlist_dialog = (Playlist *)param;
1630
1631     wxCommandEvent event( wxEVT_PLAYLIST, RemoveItem_Event );
1632     event.SetInt( new_val.i_int );
1633     p_playlist_dialog->AddPendingEvent( event );
1634
1635     return 0;
1636 }
1637
1638 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
1639                          vlc_value_t oval, vlc_value_t nval, void *param )
1640 {
1641     Playlist *p_playlist_dialog = (Playlist *)param;
1642
1643     playlist_add_t *p_add = (playlist_add_t *)malloc(sizeof( playlist_add_t));
1644     memcpy( p_add, nval.p_address, sizeof( playlist_add_t ) );
1645
1646     wxCommandEvent event( wxEVT_PLAYLIST, AppendItem_Event );
1647     event.SetClientData( (void *)p_add );
1648     p_playlist_dialog->AddPendingEvent( event );
1649
1650     return VLC_SUCCESS;
1651 }