]> git.sesse.net Git - vlc/blob - modules/gui/wxwindows/playlist.cpp
12a626680dcc05763547a53cd1e94a1099eaeb18
[vlc] / modules / gui / wxwindows / playlist.cpp
1 /*****************************************************************************
2  * playlist.cpp : wxWindows plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2004 VideoLAN
5  * $Id: playlist.cpp,v 1.39 2004/01/25 03:29:01 hartman Exp $
6  *
7  * Authors: Olivier Teulière <ipkiss@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc/intf.h>
29
30 #include "wxwindows.h"
31
32 /* Callback prototype */
33 int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
34                      vlc_value_t old_val, vlc_value_t new_val, void *param );
35 int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
36                      vlc_value_t old_val, vlc_value_t new_val, void *param );
37 int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
38                      vlc_value_t old_val, vlc_value_t new_val, void *param );
39
40 /*****************************************************************************
41  * Event Table.
42  *****************************************************************************/
43
44 /* IDs for the controls and the menu commands */
45 enum
46 {
47     /* menu items */
48     AddFile_Event = 1,
49     AddMRL_Event,
50     Close_Event,
51     Open_Event,
52     Save_Event,
53
54     SortTitle_Event,
55     RSortTitle_Event,
56     SortAuthor_Event,
57     RSortAuthor_Event,
58     SortGroup_Event,
59     RSortGroup_Event,
60     Randomize_Event,
61
62     EnableSelection_Event,
63     DisableSelection_Event,
64
65     InvertSelection_Event,
66     DeleteSelection_Event,
67     Random_Event,
68     Loop_Event,
69     Repeat_Event,
70     SelectAll_Event,
71
72     EnableGroup_Event,
73     DisableGroup_Event,
74
75     Up_Event,
76     Down_Event,
77     Infos_Event,
78
79     PopupPlay_Event,
80     PopupDel_Event,
81     PopupEna_Event,
82     PopupInfo_Event,
83
84     SearchText_Event,
85     Search_Event,
86
87     /* controls */
88     ListView_Event,
89
90     Browse_Event,  /* For export playlist */
91 };
92
93 BEGIN_EVENT_TABLE(Playlist, wxFrame)
94     /* Menu events */
95     EVT_MENU(AddFile_Event, Playlist::OnAddFile)
96     EVT_MENU(AddMRL_Event, Playlist::OnAddMRL)
97     EVT_MENU(Close_Event, Playlist::OnClose)
98     EVT_MENU(Open_Event, Playlist::OnOpen)
99     EVT_MENU(Save_Event, Playlist::OnSave)
100
101     EVT_MENU(SortTitle_Event, Playlist::OnSort)
102     EVT_MENU(RSortTitle_Event, Playlist::OnSort)
103     EVT_MENU(SortAuthor_Event, Playlist::OnSort)
104     EVT_MENU(RSortAuthor_Event, Playlist::OnSort)
105     EVT_MENU(SortGroup_Event, Playlist::OnSort)
106     EVT_MENU(RSortGroup_Event, Playlist::OnSort)
107
108     EVT_MENU(Randomize_Event, Playlist::OnSort)
109
110     EVT_MENU(EnableSelection_Event, Playlist::OnEnableSelection)
111     EVT_MENU(DisableSelection_Event, Playlist::OnDisableSelection)
112     EVT_MENU(InvertSelection_Event, Playlist::OnInvertSelection)
113     EVT_MENU(DeleteSelection_Event, Playlist::OnDeleteSelection)
114     EVT_MENU(SelectAll_Event, Playlist::OnSelectAll)
115     EVT_MENU(Infos_Event, Playlist::OnInfos)
116     EVT_CHECKBOX(Random_Event, Playlist::OnRandom)
117     EVT_CHECKBOX(Repeat_Event, Playlist::OnRepeat)
118     EVT_CHECKBOX(Loop_Event, Playlist::OnLoop)
119
120     EVT_MENU(EnableGroup_Event, Playlist::OnEnDis)
121     EVT_MENU(DisableGroup_Event, Playlist::OnEnDis)
122
123     /* Listview events */
124     EVT_LIST_ITEM_ACTIVATED(ListView_Event, Playlist::OnActivateItem)
125     EVT_LIST_COL_CLICK(ListView_Event, Playlist::OnColSelect)
126     EVT_LIST_KEY_DOWN(ListView_Event, Playlist::OnKeyDown)
127     EVT_LIST_ITEM_RIGHT_CLICK(ListView_Event, Playlist::OnPopup)
128
129     /* Popup events */
130     EVT_MENU( PopupPlay_Event, Playlist::OnPopupPlay)
131     EVT_MENU( PopupDel_Event, Playlist::OnPopupDel)
132     EVT_MENU( PopupEna_Event, Playlist::OnPopupEna)
133     EVT_MENU( PopupInfo_Event, Playlist::OnPopupInfo)
134
135
136     /* Button events */
137     EVT_BUTTON( Search_Event, Playlist::OnSearch)
138     EVT_BUTTON( Save_Event, Playlist::OnSave)
139     EVT_BUTTON( Infos_Event, Playlist::OnInfos)
140
141     EVT_BUTTON( Up_Event, Playlist::OnUp)
142     EVT_BUTTON( Down_Event, Playlist::OnDown)
143
144     EVT_TEXT(SearchText_Event, Playlist::OnSearchTextChange)
145
146     /* Special events : we don't want to destroy the window when the user
147      * clicks on (X) */
148     EVT_CLOSE(Playlist::OnClose)
149 END_EVENT_TABLE()
150
151
152 /* Event Table for the Newgroup class */
153 BEGIN_EVENT_TABLE(NewGroup, wxDialog)
154     EVT_BUTTON( wxID_OK, NewGroup::OnOk)
155     EVT_BUTTON( wxID_CANCEL, NewGroup::OnCancel)
156 END_EVENT_TABLE()
157
158
159 /*****************************************************************************
160  * Constructor.
161  *****************************************************************************/
162 Playlist::Playlist( intf_thread_t *_p_intf, wxWindow *p_parent ):
163     wxFrame( p_parent, -1, wxU(_("Playlist")), wxDefaultPosition,
164              wxDefaultSize, wxDEFAULT_FRAME_STYLE )
165 {
166     /* Initializations */
167     iteminfo_dialog = NULL;
168     p_intf = _p_intf;
169     vlc_value_t  val;
170     i_update_counter = 0;
171     i_sort_mode = MODE_NONE;
172     b_need_update = VLC_FALSE;
173     vlc_mutex_init( p_intf, &lock );
174     SetIcon( *p_intf->p_sys->p_icon );
175
176     i_title_sorted = 0;
177     i_author_sorted = 0;
178     i_group_sorted = 0;
179
180
181     var_Create( p_intf, "random", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
182     var_Create( p_intf, "loop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
183     var_Create( p_intf, "repeat", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );;
184
185     /* Create our "Manage" menu */
186     wxMenu *manage_menu = new wxMenu;
187     manage_menu->Append( AddFile_Event, wxU(_("&Simple Add...")) );
188     manage_menu->Append( AddMRL_Event, wxU(_("&Add MRL...")) );
189     manage_menu->AppendSeparator();
190     manage_menu->Append( Open_Event, wxU(_("&Open Playlist...")) );
191     manage_menu->Append( Save_Event, wxU(_("&Save Playlist...")) );
192     manage_menu->AppendSeparator();
193     manage_menu->Append( Close_Event, wxU(_("&Close")) );
194
195     /* Create our "Sort" menu */
196     wxMenu *sort_menu = new wxMenu;
197     sort_menu->Append( SortTitle_Event, wxU(_("Sort by &title")) );
198     sort_menu->Append( RSortTitle_Event, wxU(_("&Reverse sort by title")) );
199     sort_menu->AppendSeparator();
200     sort_menu->Append( SortAuthor_Event, wxU(_("Sort by &author")) );
201     sort_menu->Append( RSortAuthor_Event, wxU(_("Reverse sort by author")) );
202     sort_menu->AppendSeparator();
203     sort_menu->Append( SortGroup_Event, wxU(_("Sort by &group")) );
204     sort_menu->Append( RSortGroup_Event, wxU(_("Reverse sort by group")) );
205     sort_menu->AppendSeparator();
206     sort_menu->Append( Randomize_Event, wxU(_("&Shuffle Playlist")) );
207
208     /* Create our "Selection" menu */
209     wxMenu *selection_menu = new wxMenu;
210     selection_menu->Append( EnableSelection_Event, wxU(_("&Enable")) );
211     selection_menu->Append( DisableSelection_Event, wxU(_("&Disable")) );
212     selection_menu->AppendSeparator();
213     selection_menu->Append( InvertSelection_Event, wxU(_("&Invert")) );
214     selection_menu->Append( DeleteSelection_Event, wxU(_("D&elete")) );
215     selection_menu->Append( SelectAll_Event, wxU(_("&Select All")) );
216
217     /* Create our "Group" menu */
218     wxMenu *group_menu = new wxMenu;
219     group_menu->Append( EnableGroup_Event, wxU(_("&Enable all group items")) );
220     group_menu->Append( DisableGroup_Event,
221                         wxU(_("&Disable all group items")) );
222
223     /* Append the freshly created menus to the menu bar */
224     wxMenuBar *menubar = new wxMenuBar( wxMB_DOCKABLE );
225     menubar->Append( manage_menu, wxU(_("&Manage")) );
226     menubar->Append( sort_menu, wxU(_("S&ort")) );
227     menubar->Append( selection_menu, wxU(_("&Selection")) );
228     menubar->Append( group_menu, wxU(_("&Groups")) );
229
230     /* Attach the menu bar to the frame */
231     SetMenuBar( menubar );
232
233     /* Create the popup menu */
234     popup_menu = new wxMenu;
235     popup_menu->Append( PopupPlay_Event, wxU(_("Play")) );
236     popup_menu->Append( PopupDel_Event, wxU(_("Delete")) );
237     popup_menu->Append( PopupEna_Event, wxU(_("Toggle enabled")) );
238     popup_menu->Append( PopupInfo_Event, wxU(_("Info")) );
239
240     /* Create a panel to put everything in */
241     wxPanel *playlist_panel = new wxPanel( this, -1 );
242     playlist_panel->SetAutoLayout( TRUE );
243
244     /* Create the Random checkbox */
245     wxCheckBox *random_checkbox =
246         new wxCheckBox( playlist_panel, Random_Event, wxU(_("Random")) );
247     var_Get( p_intf, "random", &val );
248     vlc_bool_t b_random = val.b_bool;
249     random_checkbox->SetValue( b_random == VLC_FALSE ? 0 : 1 );
250
251     /* Create the Loop Checkbox */
252     wxCheckBox *loop_checkbox =
253         new wxCheckBox( playlist_panel, Loop_Event, wxU(_("Repeat All")) );
254     var_Get( p_intf, "loop", &val );
255     int b_loop = val.b_bool ;
256     loop_checkbox->SetValue( b_loop );
257
258     /* Create the Repeat one checkbox */
259     wxCheckBox *repeat_checkbox =
260         new wxCheckBox( playlist_panel, Repeat_Event, wxU(_("Repeat One")) );
261     var_Get( p_intf, "repeat", &val );
262     int b_repeat = val.b_bool ;
263     repeat_checkbox->SetValue( b_repeat );
264
265     /* Create the Search Textbox */
266     search_text =
267         new wxTextCtrl( playlist_panel, SearchText_Event, wxT(""),
268                         wxDefaultPosition, wxSize(140, -1),
269                         wxTE_PROCESS_ENTER);
270
271     /* Create the search button */
272     search_button =
273         new wxButton( playlist_panel, Search_Event, wxU(_("Search")) );
274
275
276     /* Create the listview */
277     /* FIXME: the given size is arbitrary, and prevents us from resizing
278      * the window to smaller dimensions. But the sizers don't seem to adjust
279      * themselves to the size of a listview, and with a wxDefaultSize the
280      * playlist window is ridiculously small */
281     listview = new wxListView( playlist_panel, ListView_Event,
282                                wxDefaultPosition, wxSize( 500, 300 ),
283                                wxLC_REPORT | wxSUNKEN_BORDER );
284     listview->InsertColumn( 0, wxU(_("Name")) );
285     listview->InsertColumn( 1, wxU(_("Author")) );
286     listview->InsertColumn( 2, wxU(_("Group")) );
287     listview->InsertColumn( 3, wxU(_("Duration")) );
288     listview->SetColumnWidth( 0, 270 );
289     listview->SetColumnWidth( 1, 150 );
290     listview->SetColumnWidth( 2, 80 );
291
292     /* Create the Up-Down buttons */
293     wxButton *up_button =
294         new wxButton( playlist_panel, Up_Event, wxU(_("Up") ) );
295
296     wxButton *down_button =
297         new wxButton( playlist_panel, Down_Event, wxU(_("Down") ) );
298
299     /* Create the iteminfo button */
300     wxButton *iteminfo_button =
301         new wxButton( playlist_panel, Infos_Event, wxU(_("Item info") ) );
302
303     /* Place everything in sizers */
304     wxBoxSizer *button_sizer = new wxBoxSizer( wxHORIZONTAL );
305     button_sizer->Add( iteminfo_button, 0, wxALIGN_CENTER|wxLEFT , 5);
306     button_sizer->Layout();
307
308     wxBoxSizer *updown_sizer = new wxBoxSizer( wxHORIZONTAL );
309     updown_sizer->Add( up_button, 0, wxALIGN_LEFT|wxRIGHT, 3);
310     updown_sizer->Add( down_button, 0, wxALIGN_LEFT|wxLEFT, 3);
311     updown_sizer->Layout();
312
313     wxBoxSizer *checkbox_sizer = new wxBoxSizer( wxHORIZONTAL );
314     checkbox_sizer->Add( random_checkbox, 0,
315                          wxEXPAND | wxALIGN_RIGHT | wxALL, 5);
316     checkbox_sizer->Add( loop_checkbox, 0,
317                          wxEXPAND | wxALIGN_RIGHT | wxALL, 5);
318     checkbox_sizer->Add( repeat_checkbox, 0,
319                          wxEXPAND | wxALIGN_RIGHT | wxALL, 5);
320     checkbox_sizer->Layout();
321
322     wxBoxSizer *search_sizer = new wxBoxSizer( wxHORIZONTAL );
323     search_sizer->Add( search_text, 0, wxRIGHT|wxALIGN_CENTER, 3);
324     search_sizer->Add( search_button, 0, wxLEFT|wxALIGN_CENTER, 3);
325     search_sizer->Layout();
326
327     /* The top and bottom sizers */
328     wxBoxSizer *top_sizer = new wxBoxSizer( wxHORIZONTAL );
329     top_sizer->Add( checkbox_sizer, 1, wxLEFT|wxRIGHT|wxALIGN_LEFT, 4 );
330     top_sizer->Add( search_sizer, 1, wxLEFT|wxRIGHT|wxALIGN_RIGHT, 4 );
331     top_sizer->Layout();
332
333     wxBoxSizer *bottom_sizer = new wxBoxSizer( wxHORIZONTAL );
334     bottom_sizer->Add( updown_sizer, 0, wxEXPAND |wxRIGHT | wxLEFT | wxALIGN_LEFT, 4);
335     bottom_sizer->Add( button_sizer , 0, wxEXPAND|wxLEFT | wxRIGHT | wxALIGN_RIGHT, 4 );
336     bottom_sizer->Layout();
337
338     wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
339
340     wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
341     panel_sizer->Add( top_sizer, 0, wxEXPAND | wxALL, 5 );
342     panel_sizer->Add( listview, 1, wxEXPAND | wxALL, 5 );
343     panel_sizer->Add( bottom_sizer, 0 , wxEXPAND | wxALL, 5);
344     panel_sizer->Layout();
345
346     playlist_panel->SetSizerAndFit( panel_sizer );
347     main_sizer->Add( playlist_panel, 1, wxGROW, 0 );
348     main_sizer->Layout();
349     SetSizerAndFit( main_sizer );
350
351 #if !defined(__WXX11__)
352     /* Associate drop targets with the playlist */
353     SetDropTarget( new DragAndDrop( p_intf, VLC_TRUE ) );
354 #endif
355
356     playlist_t *p_playlist =
357         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
358                                        FIND_ANYWHERE );
359     if( p_playlist == NULL )
360     {
361         return;
362     }
363
364     /* We want to be noticed of playlist changes */
365
366     /* Some global changes happened -> Rebuild all */
367     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
368
369     /* We went to the next item */
370     var_AddCallback( p_playlist, "playlist-current", PlaylistNext, this );
371
372     /* One item has been updated */
373     var_AddCallback( p_playlist, "item-change", ItemChanged, this );
374
375
376     vlc_object_release( p_playlist );
377
378     /* Update the playlist */
379     Rebuild();
380 }
381
382 Playlist::~Playlist()
383 {
384     playlist_t *p_playlist =
385         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
386                                        FIND_ANYWHERE );
387     if( p_playlist == NULL )
388     {
389         return;
390     }
391
392     delete iteminfo_dialog;
393
394     var_DelCallback( p_playlist, "item-change", ItemChanged, this );
395     var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this );
396     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
397     vlc_object_release( p_playlist );
398 }
399
400 /**********************************************************************
401  * Update one playlist item
402  **********************************************************************/
403 void Playlist::UpdateItem( int i )
404 {
405     playlist_t *p_playlist =
406         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
407                                        FIND_ANYWHERE );
408     if( p_playlist == NULL )
409     {
410         return;
411     }
412     if( i < 0 || i >= p_playlist->i_size || !p_playlist->pp_items[i] )
413     {
414         vlc_object_release(p_playlist);
415         return;
416     }
417     listview->SetItem( i, 0, wxL2U(p_playlist->pp_items[i]->psz_name) );
418     listview->SetItem( i, 1, wxU( playlist_GetInfo( p_playlist, i,
419                                        _("General") , _("Author") ) ) );
420     char *psz_group = playlist_FindGroup(p_playlist,
421                                          p_playlist->pp_items[i]->i_group);
422     listview->SetItem( i, 2,
423              wxL2U( psz_group ? psz_group : _("Normal") ) );
424
425     if( p_playlist->pp_items[i]->b_enabled == VLC_FALSE )
426     {
427         wxListItem listitem;
428         listitem.m_itemId = i;
429         listitem.SetTextColour( *wxLIGHT_GREY);
430         listview->SetItem(listitem);
431     }
432
433     char psz_duration[MSTRTIME_MAX_SIZE];
434     mtime_t dur = p_playlist->pp_items[i]->i_duration;
435     if( dur != -1 ) secstotimestr( psz_duration, dur/1000000 );
436     else memcpy( psz_duration , "-:--:--", sizeof("-:--:--") );
437     listview->SetItem( i, 3, wxU(psz_duration) );
438
439     /* Change the colour for the currenty played stream */
440     wxListItem listitem;
441     listitem.m_itemId = i;
442     if( i == p_playlist->i_index )
443     {
444         listitem.SetTextColour( *wxRED );
445     }
446     else
447     {
448         listitem.SetTextColour( *wxBLACK );
449     }
450     listview->SetItem( listitem );
451
452     vlc_object_release(p_playlist);
453 }
454
455 /**********************************************************************
456  * Rebuild the playlist
457  **********************************************************************/
458 void Playlist::Rebuild()
459 {
460     playlist_t *p_playlist =
461         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
462                                        FIND_ANYWHERE );
463     if( p_playlist == NULL )
464     {
465         return;
466     }
467
468     int i_focused = listview->GetFocusedItem();
469
470     /* Clear the list... */
471     listview->DeleteAllItems();
472
473     /* ...and rebuild it */
474     vlc_mutex_lock( &p_playlist->object_lock );
475     for( int i = 0; i < p_playlist->i_size; i++ )
476     {
477         wxString filename = wxL2U(p_playlist->pp_items[i]->psz_name);
478         listview->InsertItem( i, filename );
479         UpdateItem( i );
480     }
481     vlc_mutex_unlock( &p_playlist->object_lock );
482
483     if( i_focused )
484     {
485         listview->Focus( i_focused );
486         listview->Select( i_focused );
487     }
488     else
489     {
490         listview->Focus( p_playlist->i_index );
491     }
492
493     vlc_object_release( p_playlist );
494 }
495
496 void Playlist::ShowPlaylist( bool show )
497 {
498     if( show ) Rebuild();
499     Show( show );
500 }
501
502 void Playlist::UpdatePlaylist()
503 {
504     vlc_bool_t b_need_update = VLC_FALSE;
505     i_update_counter++;
506
507     /* If the playlist isn't show there's no need to update it */
508     if( !IsShown() ) return;
509
510     vlc_mutex_lock( &lock );
511     if( this->b_need_update )
512     {
513         b_need_update =VLC_TRUE;
514         this->b_need_update = VLC_FALSE;
515     }
516     vlc_mutex_unlock( &lock );
517
518     if( b_need_update )
519     {
520         Rebuild();
521     }
522
523     /* Updating the playing status every 0.5s is enough */
524     if( i_update_counter % 5 ) return;
525
526     playlist_t *p_playlist =
527         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
528                                        FIND_ANYWHERE );
529     if( p_playlist == NULL )
530     {
531         return;
532     }
533
534     /* Update the colour of items */
535
536     vlc_mutex_lock( &p_playlist->object_lock );
537     if( p_intf->p_sys->i_playing != p_playlist->i_index )
538     {
539         wxListItem listitem;
540         listitem.m_itemId = p_playlist->i_index;
541         listitem.SetTextColour( *wxRED );
542         listview->SetItem( listitem );
543
544         if( p_intf->p_sys->i_playing != -1 )
545         {
546             listitem.m_itemId = p_intf->p_sys->i_playing;
547             listitem.SetTextColour( *wxBLACK );
548             listview->SetItem( listitem );
549         }
550         p_intf->p_sys->i_playing = p_playlist->i_index;
551     }
552     vlc_mutex_unlock( &p_playlist->object_lock );
553     vlc_object_release( p_playlist );
554 }
555
556 /*****************************************************************************
557  * Private methods.
558  *****************************************************************************/
559 void Playlist::DeleteItem( int item )
560 {
561     playlist_t *p_playlist =
562         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
563                                        FIND_ANYWHERE );
564     if( p_playlist == NULL )
565     {
566         return;
567     }
568
569     playlist_Delete( p_playlist, item );
570     listview->DeleteItem( item );
571
572     vlc_object_release( p_playlist );
573 }
574
575 void Playlist::OnClose( wxCommandEvent& WXUNUSED(event) )
576 {
577     Hide();
578 }
579
580 void Playlist::OnSave( wxCommandEvent& WXUNUSED(event) )
581 {
582     struct {
583         char *psz_desc;
584         char *psz_filter;
585         char *psz_module;
586     } formats[] = {{ _("M3U file"), "*.m3u", "export-m3u" },
587                    { _("PLS file"), "*.pls", "export-pls" }};
588     wxString filter = wxT("");
589     for( unsigned int i = 0; i < sizeof(formats)/sizeof(formats[0]); i++)
590     {
591         filter.Append( wxU(formats[i].psz_desc) );
592         filter.Append( wxT("|") );
593         filter.Append( wxU(formats[i].psz_filter) );
594         filter.Append( wxT("|") );
595     }
596     wxFileDialog dialog( this, wxU(_("Save playlist")),
597                          wxT(""), wxT(""), filter, wxSAVE );
598
599     if( dialog.ShowModal() == wxID_OK )
600     {
601
602         playlist_t * p_playlist =
603             (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
604                                            FIND_ANYWHERE );
605
606         if( p_playlist )
607         {
608             if( dialog.GetFilename().mb_str() )
609             {
610                 playlist_Export( p_playlist, dialog.GetFilename().mb_str(),
611                                  formats[dialog.GetFilterIndex()].psz_module );
612             }
613         }
614         
615         vlc_object_release( p_playlist );
616         
617     }
618 }
619
620 void Playlist::OnOpen( wxCommandEvent& WXUNUSED(event) )
621 {
622     playlist_t *p_playlist =
623         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
624                                        FIND_ANYWHERE );
625     if( p_playlist == NULL )
626     {
627         return;
628     }
629
630     wxFileDialog dialog( this, wxU(_("Open playlist")),
631                          wxT(""), wxT(""), wxT("All playlists|*.pls;*.m3u;*.asx;*.b4s|M3U files|*.m3u"), wxOPEN );
632
633     if( dialog.ShowModal() == wxID_OK )
634     {
635         playlist_Import( p_playlist, dialog.GetPath().mb_str() );
636     }
637
638     vlc_object_release( p_playlist );
639 }
640
641 void Playlist::OnAddFile( wxCommandEvent& WXUNUSED(event) )
642 {
643     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE_SIMPLE, 0, 0 );
644
645 #if 0
646     Rebuild();
647 #endif
648 }
649
650 void Playlist::OnAddMRL( wxCommandEvent& WXUNUSED(event) )
651 {
652     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE, 0, 0 );
653
654 #if 0
655     Rebuild();
656 #endif
657 }
658
659 /********************************************************************
660  * Move functions
661  ********************************************************************/
662 void Playlist::OnUp( wxCommandEvent& event)
663 {
664     playlist_t *p_playlist =
665         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
666                                        FIND_ANYWHERE );
667     if( p_playlist == NULL )
668     {
669         return;
670     }
671
672     /* We use the first selected item, so find it */
673     long i_item = listview->GetNextItem( i_item, wxLIST_NEXT_ALL,
674                                          wxLIST_STATE_SELECTED);
675     if( i_item > 0 && i_item < p_playlist->i_size )
676     {
677         playlist_Move( p_playlist , i_item, i_item - 1);
678         if( i_item > 1 )
679         {
680             listview->Focus( i_item - 1 );
681         }
682         else
683         {
684             listview->Focus(0);
685         }
686     }
687     vlc_object_release( p_playlist );
688     return;
689 }
690
691 void Playlist::OnDown( wxCommandEvent& event)
692 {
693     playlist_t *p_playlist =
694         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
695                                        FIND_ANYWHERE );
696     if( p_playlist == NULL )
697     {
698         return;
699     }
700
701     /* We use the first selected item, so find it */
702     long i_item = listview->GetNextItem( i_item, wxLIST_NEXT_ALL,
703                                          wxLIST_STATE_SELECTED );
704     if( i_item >= 0 && i_item < p_playlist->i_size - 1 )
705     {
706         playlist_Move( p_playlist , i_item, i_item + 2 );
707         listview->Focus( i_item + 1 );
708     }
709     vlc_object_release( p_playlist );
710     return;
711 }
712
713 /********************************************************************
714  * Sorting functions
715  ********************************************************************/
716 void Playlist::OnSort( wxCommandEvent& event )
717 {
718     playlist_t *p_playlist =
719         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
720                                        FIND_ANYWHERE );
721     if( p_playlist == NULL )
722     {
723         return;
724     }
725     switch( event.GetId() )
726     {
727         case SortTitle_Event:
728            playlist_SortTitle( p_playlist , ORDER_NORMAL );
729            break;
730         case RSortTitle_Event:
731            playlist_SortTitle( p_playlist , ORDER_REVERSE );
732            break;
733         case SortAuthor_Event:
734            playlist_SortAuthor(p_playlist , ORDER_NORMAL );
735            break;
736         case RSortAuthor_Event:
737            playlist_SortAuthor( p_playlist , ORDER_REVERSE );
738            break;
739         case SortGroup_Event:
740            playlist_SortGroup( p_playlist , ORDER_NORMAL );
741            break;
742         case RSortGroup_Event:
743            playlist_SortGroup( p_playlist , ORDER_REVERSE );
744            break;
745         case Randomize_Event:
746            playlist_Sort( p_playlist , SORT_RANDOM, ORDER_NORMAL );
747            break;
748     }
749     vlc_object_release( p_playlist );
750
751     Rebuild();
752
753     return;
754 }
755
756 void Playlist::OnColSelect( wxListEvent& event )
757 {
758     playlist_t *p_playlist =
759         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
760                                        FIND_ANYWHERE );
761     if( p_playlist == NULL )
762     {
763         return;
764     }
765     switch( event.GetColumn() )
766     {
767         case 0:
768             if( i_title_sorted != 1 )
769             {
770                 playlist_SortTitle( p_playlist, ORDER_NORMAL );
771                 i_title_sorted = 1;
772             }
773             else
774             {
775                 playlist_SortTitle( p_playlist, ORDER_REVERSE );
776                 i_title_sorted = -1;
777             }
778             break;
779         case 1:
780             if( i_author_sorted != 1 )
781             {
782                 playlist_SortAuthor( p_playlist, ORDER_NORMAL );
783                 i_author_sorted = 1;
784             }
785             else
786             {
787                 playlist_SortAuthor( p_playlist, ORDER_REVERSE );
788                 i_author_sorted = -1;
789             }
790             break;
791         case 2:
792             if( i_group_sorted != 1 )
793             {
794                 playlist_SortGroup( p_playlist, ORDER_NORMAL );
795                 i_group_sorted = 1;
796             }
797             else
798             {
799                 playlist_SortGroup( p_playlist, ORDER_REVERSE );
800                 i_group_sorted = -1;
801             }
802             break;
803         default:
804             break;
805     }
806     vlc_object_release( p_playlist );
807
808     Rebuild();
809
810     return;
811 }
812
813 /**********************************************************************
814  * Search functions
815  **********************************************************************/
816 void Playlist::OnSearchTextChange( wxCommandEvent& WXUNUSED(event) )
817 {
818    search_button->SetDefault();
819 }
820
821 void Playlist::OnSearch( wxCommandEvent& WXUNUSED(event) )
822 {
823     wxString search_string= search_text->GetValue();
824
825     int i_current;
826     int i_first = 0 ;
827     int i_item = -1;
828
829     for( i_current = 0 ; i_current <= listview->GetItemCount() ; i_current++ )
830     {
831         if( listview->GetItemState( i_current, wxLIST_STATE_SELECTED )
832                    == wxLIST_STATE_SELECTED )
833         {
834             i_first = i_current;
835             break;
836         }
837     }
838
839     for ( i_current = i_first + 1; i_current <= listview->GetItemCount() ;
840           i_current++ )
841     {
842         wxListItem listitem;
843         listitem.SetId( i_current );
844         listview->GetItem( listitem );
845         if( listitem.m_text.Lower().Contains( search_string.Lower() ) )
846         {
847             i_item = i_current;
848             break;
849         }
850     }
851     for( long item = 0; item < listview->GetItemCount(); item++ )
852     {
853         listview->Select( item, FALSE );
854     }
855
856     wxListItem listitem;
857     listitem.SetId(i_item);
858     listitem.m_state = wxLIST_STATE_SELECTED;
859     listview->Select( i_item, TRUE );
860     listview->Focus( i_item );
861
862 }
863
864 /**********************************************************************
865  * Selection functions
866  **********************************************************************/
867 void Playlist::OnInvertSelection( wxCommandEvent& WXUNUSED(event) )
868 {
869     for( long item = 0; item < listview->GetItemCount(); item++ )
870     {
871         listview->Select( item, ! listview->IsSelected( item ) );
872     }
873 }
874
875 void Playlist::OnDeleteSelection( wxCommandEvent& WXUNUSED(event) )
876 {
877     /* Delete from the end to the beginning, to avoid a shift of indices */
878     for( long item = listview->GetItemCount() - 1; item >= 0; item-- )
879     {
880         if( listview->IsSelected( item ) )
881         {
882             DeleteItem( item );
883         }
884     }
885
886     Rebuild();
887 }
888
889 void Playlist::OnEnableSelection( wxCommandEvent& WXUNUSED(event) )
890 {
891     playlist_t *p_playlist =
892         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
893                                        FIND_ANYWHERE );
894     if( p_playlist == NULL )
895     {
896         return;
897     }
898
899     for( long item = listview->GetItemCount() - 1; item >= 0; item-- )
900     {
901         if( listview->IsSelected( item ) )
902         {
903             playlist_Enable( p_playlist, item );
904             UpdateItem( item );
905         }
906     }
907     vlc_object_release( p_playlist);
908 }
909
910 void Playlist::OnDisableSelection( wxCommandEvent& WXUNUSED(event) )
911 {
912     playlist_t *p_playlist =
913         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
914                                        FIND_ANYWHERE );
915     if( p_playlist == NULL )
916     {
917         return;
918     }
919
920     for( long item = listview->GetItemCount() - 1; item >= 0; item-- )
921     {
922         if( listview->IsSelected( item ) )
923         {
924             playlist_Disable( p_playlist, item );
925             UpdateItem( item );
926         }
927     }
928     vlc_object_release( p_playlist);
929 }
930
931 void Playlist::OnSelectAll( wxCommandEvent& WXUNUSED(event) )
932 {
933     for( long item = 0; item < listview->GetItemCount(); item++ )
934     {
935         listview->Select( item, TRUE );
936     }
937 }
938
939 /**********************************************************************
940  * Playlist mode functions
941  **********************************************************************/
942 void Playlist::OnRandom( wxCommandEvent& event )
943 {
944     vlc_value_t val;
945     val.b_bool = event.IsChecked();
946     playlist_t *p_playlist =
947         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
948                                        FIND_ANYWHERE );
949     if( p_playlist == NULL )
950     {
951         return;
952     }
953     var_Set( p_playlist , "random", val);
954     vlc_object_release( p_playlist );
955 }
956 void Playlist::OnLoop ( wxCommandEvent& event )
957 {
958     vlc_value_t val;
959     val.b_bool = event.IsChecked();
960     playlist_t *p_playlist =
961         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
962                                        FIND_ANYWHERE );
963     if( p_playlist == NULL )
964     {
965         return;
966     }
967     var_Set( p_playlist , "loop", val);
968     vlc_object_release( p_playlist );
969 }
970
971 void Playlist::OnRepeat ( wxCommandEvent& event )
972 {
973     vlc_value_t val;
974     val.b_bool = event.IsChecked();
975     playlist_t *p_playlist =
976         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
977                                        FIND_ANYWHERE );
978     if( p_playlist == NULL )
979     {
980         return;
981     }
982     var_Set( p_playlist , "repeat", val);
983     vlc_object_release( p_playlist );
984 }
985
986
987
988 void Playlist::OnActivateItem( wxListEvent& event )
989 {
990     playlist_t *p_playlist =
991         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
992                                        FIND_ANYWHERE );
993     if( p_playlist == NULL )
994     {
995         return;
996     }
997     playlist_Goto( p_playlist, event.GetIndex() );
998
999     vlc_object_release( p_playlist );
1000 }
1001
1002 void Playlist::OnKeyDown( wxListEvent& event )
1003 {
1004     long keycode = event.GetKeyCode();
1005     /* Delete selected items */
1006     if( keycode == WXK_BACK || keycode == WXK_DELETE )
1007     {
1008         /* We send a dummy event */
1009         OnDeleteSelection( event );
1010     }
1011 }
1012
1013 void Playlist::ShowInfos( int i_item )
1014 {
1015     playlist_t *p_playlist =
1016         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1017                                        FIND_ANYWHERE );
1018     if( p_playlist == NULL )
1019     {
1020         return;
1021     }
1022     if( iteminfo_dialog == NULL )
1023     {
1024         if( i_item >= 0 && i_item < p_playlist->i_size )
1025         {
1026             iteminfo_dialog = new ItemInfoDialog(
1027                               p_intf, p_playlist->pp_items[i_item], this );
1028             if( iteminfo_dialog->ShowModal()  == wxID_OK )
1029                 UpdateItem( i_item );
1030             delete iteminfo_dialog;
1031             iteminfo_dialog = NULL;
1032         }
1033     }
1034     vlc_object_release( p_playlist );
1035 }
1036
1037 void Playlist::OnInfos( wxCommandEvent& WXUNUSED(event) )
1038 {
1039     /* We use the first selected item, so find it */
1040     long i_item = listview->GetNextItem( -1 , wxLIST_NEXT_ALL,
1041                                          wxLIST_STATE_SELECTED );
1042     ShowInfos( i_item );
1043 }
1044
1045 void Playlist::OnEnDis( wxCommandEvent& event )
1046 {
1047     playlist_t *p_playlist =
1048         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1049                                        FIND_ANYWHERE );
1050     if( p_playlist == NULL )
1051     {
1052         return;
1053     }
1054
1055     long i_item = listview->GetNextItem( i_item, wxLIST_NEXT_ALL,
1056                                          wxLIST_STATE_SELECTED );
1057
1058     if( i_item >= 0 && i_item < p_playlist->i_size )
1059     {
1060        switch( event.GetId() )
1061        {
1062            case EnableGroup_Event:
1063                playlist_EnableGroup( p_playlist ,
1064                                   p_playlist->pp_items[i_item]->i_group );
1065                break;
1066            case DisableGroup_Event:
1067                playlist_DisableGroup( p_playlist ,
1068                                   p_playlist->pp_items[i_item]->i_group );
1069                break;
1070        }
1071        Rebuild();
1072     }
1073
1074     vlc_object_release( p_playlist );
1075 }
1076
1077 /*****************************************************************************
1078  * Popup management functions
1079  *****************************************************************************/
1080 void Playlist::OnPopup( wxListEvent& event )
1081 {
1082     i_popup_item = event.GetIndex();
1083     Playlist::PopupMenu( popup_menu , ScreenToClient( wxGetMousePosition() ) );
1084 }
1085
1086
1087 void Playlist::OnPopupPlay( wxMenuEvent& event )
1088 {
1089     playlist_t *p_playlist =
1090         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1091                                        FIND_ANYWHERE );
1092     if( p_playlist == NULL )
1093     {
1094         return;
1095     }
1096     if( i_popup_item != -1 )
1097     {
1098         playlist_Goto( p_playlist, i_popup_item );
1099     }
1100     vlc_object_release( p_playlist );
1101 }
1102
1103 void Playlist::OnPopupDel( wxMenuEvent& event )
1104 {
1105     DeleteItem( i_popup_item );
1106 }
1107
1108 void Playlist::OnPopupEna( wxMenuEvent& event )
1109 {
1110     playlist_t *p_playlist =
1111         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1112                                        FIND_ANYWHERE );
1113     if( p_playlist == NULL )
1114     {
1115         return;
1116     }
1117
1118     if( p_playlist->pp_items[i_popup_item]->b_enabled )
1119         //playlist_IsEnabled( p_playlist, i_popup_item ) )
1120     {
1121         playlist_Disable( p_playlist, i_popup_item );
1122     }
1123     else
1124     {
1125         playlist_Enable( p_playlist, i_popup_item );
1126     }
1127     vlc_object_release( p_playlist);
1128     UpdateItem( i_popup_item );
1129 }
1130
1131 void Playlist::OnPopupInfo( wxMenuEvent& event )
1132 {
1133     ShowInfos( i_popup_item );
1134 }
1135
1136 /*****************************************************************************
1137  * PlaylistChanged: callback triggered by the intf-change playlist variable
1138  *  We don't rebuild the playlist directly here because we don't want the
1139  *  caller to block for a too long time.
1140  *****************************************************************************/
1141 int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
1142                      vlc_value_t old_val, vlc_value_t new_val, void *param )
1143 {
1144     Playlist *p_playlist_dialog = (Playlist *)param;
1145     vlc_mutex_lock( &p_playlist_dialog->lock );
1146     p_playlist_dialog->b_need_update = VLC_TRUE;
1147     vlc_mutex_unlock( &p_playlist_dialog->lock );
1148     return VLC_SUCCESS;
1149 }
1150
1151 /*****************************************************************************
1152  * Next: callback triggered by the playlist-current playlist variable
1153  *****************************************************************************/
1154 int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
1155                  vlc_value_t old_val, vlc_value_t new_val, void *param )
1156 {
1157     Playlist *p_playlist_dialog = (Playlist *)param;
1158     p_playlist_dialog->UpdateItem( old_val.i_int );
1159     p_playlist_dialog->UpdateItem( new_val.i_int );
1160     return 0;
1161 }
1162
1163
1164 /*****************************************************************************
1165  * ItemChanged: callback triggered by the item-change playlist variable
1166  *****************************************************************************/
1167 int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
1168                  vlc_value_t old_val, vlc_value_t new_val, void *param )
1169 {
1170     Playlist *p_playlist_dialog = (Playlist *)param;
1171     p_playlist_dialog->UpdateItem( new_val.i_int );
1172     return 0;
1173 }
1174
1175
1176 /***************************************************************************
1177  * NewGroup Class
1178  ***************************************************************************/
1179 NewGroup::NewGroup( intf_thread_t *_p_intf, wxWindow *_p_parent ):
1180     wxDialog( _p_parent, -1, wxU(_("New Group")), wxDefaultPosition,
1181              wxDefaultSize, wxDEFAULT_FRAME_STYLE )
1182 {
1183     /* Initializations */
1184     p_intf = _p_intf;
1185     psz_name = NULL;
1186     SetIcon( *p_intf->p_sys->p_icon );
1187
1188     /* Create a panel to put everything in*/
1189     wxPanel *panel = new wxPanel( this, -1 );
1190     panel->SetAutoLayout( TRUE );
1191
1192     wxStaticText *group_label =
1193             new wxStaticText( panel , -1,
1194                 wxU(_("Enter a name for the new group:")));
1195
1196     groupname = new wxTextCtrl(panel, -1, wxU(""),wxDefaultPosition,
1197                                wxSize(100,27),wxTE_PROCESS_ENTER);
1198
1199     wxButton *ok_button = new wxButton(panel, wxID_OK, wxU(_("OK")) );
1200     ok_button->SetDefault();
1201     wxButton *cancel_button = new wxButton( panel, wxID_CANCEL,
1202                                             wxU(_("Cancel")) );
1203
1204     wxBoxSizer *button_sizer = new wxBoxSizer( wxHORIZONTAL );
1205
1206     button_sizer->Add( ok_button, 0, wxALL, 5 );
1207     button_sizer->Add( cancel_button, 0, wxALL, 5 );
1208     button_sizer->Layout();
1209
1210     wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
1211     panel_sizer->Add( group_label, 0, wxEXPAND | wxALL, 5 );
1212     panel_sizer->Add( groupname, 0, wxEXPAND | wxALL, 5 );
1213     panel_sizer->Add( button_sizer, 0, wxEXPAND | wxALL, 5 );
1214     panel_sizer->Layout();
1215
1216     panel->SetSizerAndFit( panel_sizer );
1217
1218     wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
1219     main_sizer->Add( panel, 1, wxEXPAND, 0 );
1220     main_sizer->Layout();
1221     SetSizerAndFit( main_sizer );
1222 }
1223
1224 NewGroup::~NewGroup()
1225 {
1226 }
1227
1228 void NewGroup::OnOk( wxCommandEvent& event )
1229 {
1230     psz_name = strdup( groupname->GetLineText(0).mb_str() );
1231
1232     playlist_t * p_playlist =
1233           (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1234                                        FIND_ANYWHERE );
1235
1236     if( p_playlist )
1237     {
1238         if( !playlist_CreateGroup( p_playlist, psz_name ) )
1239         {
1240             psz_name = NULL;
1241         }
1242         vlc_object_release( p_playlist );
1243     }
1244
1245     EndModal( wxID_OK );
1246 }
1247
1248 void NewGroup::OnCancel( wxCommandEvent& WXUNUSED(event) )
1249 {
1250     EndModal( wxID_CANCEL );
1251 }