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