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