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