]> git.sesse.net Git - vlc/blob - modules/gui/wxwindows/playlist.cpp
* modules/gui/wxwindows/preferences.cpp:
[vlc] / modules / gui / wxwindows / playlist.cpp
1 /*****************************************************************************
2  * playlist.cpp : wxWindows plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2001 VideoLAN
5  * $Id: playlist.cpp,v 1.21 2003/09/22 14:40:10 zorglub 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 <stdlib.h>                                      /* malloc(), free() */
28 #include <errno.h>                                                 /* ENOMEM */
29 #include <string.h>                                            /* strerror() */
30 #include <stdio.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/intf.h>
34
35 #include "wxwindows.h"
36 #include <wx/listctrl.h>
37
38 /* Callback prototype */
39 int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
40                      vlc_value_t old_val, vlc_value_t new_val, void *param );
41
42 /*****************************************************************************
43  * Event Table.
44  *****************************************************************************/
45
46 /* IDs for the controls and the menu commands */
47 enum
48 {
49     /* menu items */
50     AddFile_Event = 1,
51     AddMRL_Event,
52     Sort_Event,
53     RSort_Event,
54     Close_Event,
55     Open_Event,
56     Save_Event,
57
58     InvertSelection_Event,
59     DeleteSelection_Event,
60     Random_Event,
61     Loop_Event,
62     Repeat_Event,
63     SelectAll_Event,
64
65     SearchText_Event,
66     Search_Event,
67
68     /* controls */
69     ListView_Event
70 };
71
72 BEGIN_EVENT_TABLE(Playlist, wxFrame)
73     /* Menu events */
74     EVT_MENU(AddFile_Event, Playlist::OnAddFile)
75     EVT_MENU(AddMRL_Event, Playlist::OnAddMRL)
76     EVT_MENU(Sort_Event, Playlist::OnSort)
77     EVT_MENU(RSort_Event, Playlist::OnRSort)
78     EVT_MENU(Close_Event, Playlist::OnClose)
79     EVT_MENU(Open_Event, Playlist::OnOpen)
80     EVT_MENU(Save_Event, Playlist::OnSave)
81     EVT_MENU(InvertSelection_Event, Playlist::OnInvertSelection)
82     EVT_MENU(DeleteSelection_Event, Playlist::OnDeleteSelection)
83     EVT_MENU(SelectAll_Event, Playlist::OnSelectAll)
84     EVT_CHECKBOX(Random_Event, Playlist::OnRandom)
85     EVT_CHECKBOX(Repeat_Event, Playlist::OnRepeat)
86     EVT_CHECKBOX(Loop_Event, Playlist::OnLoop)
87
88     /* Listview events */
89     EVT_LIST_ITEM_ACTIVATED(ListView_Event, Playlist::OnActivateItem)
90     EVT_LIST_KEY_DOWN(ListView_Event, Playlist::OnKeyDown)
91
92     /* Button events */
93     EVT_BUTTON( Search_Event, Playlist::OnSearch)
94     EVT_BUTTON( Save_Event, Playlist::OnSave)
95
96     EVT_TEXT(SearchText_Event, Playlist::OnSearchTextChange)
97
98     /* Special events : we don't want to destroy the window when the user
99      * clicks on (X) */
100     EVT_CLOSE(Playlist::OnClose)
101 END_EVENT_TABLE()
102
103 /*****************************************************************************
104  * Constructor.
105  *****************************************************************************/
106 Playlist::Playlist( intf_thread_t *_p_intf, wxWindow *p_parent ):
107     wxFrame( p_parent, -1, wxU(_("Playlist")), wxDefaultPosition,
108              wxDefaultSize, wxDEFAULT_FRAME_STYLE )
109 {
110     /* Initializations */
111     p_intf = _p_intf;
112     vlc_value_t  val;
113     i_update_counter = 0;
114     b_need_update = VLC_FALSE;
115     vlc_mutex_init( p_intf, &lock );
116     SetIcon( *p_intf->p_sys->p_icon );
117
118     var_Create( p_intf, "random", VLC_VAR_BOOL );
119     var_Change( p_intf, "random", VLC_VAR_INHERITVALUE, & val, NULL );
120     var_Create( p_intf, "loop", VLC_VAR_BOOL );
121     var_Create( p_intf, "loop", VLC_VAR_BOOL );
122     var_Change( p_intf, "repeat", VLC_VAR_INHERITVALUE, & val, NULL );
123     var_Change( p_intf, "repeat", VLC_VAR_INHERITVALUE, & val, NULL );
124
125     /* Create our "Manage" menu */
126     wxMenu *manage_menu = new wxMenu;
127     manage_menu->Append( AddFile_Event, wxU(_("&Simple Add...")) );
128     manage_menu->Append( AddMRL_Event, wxU(_("&Add MRL...")) );
129     manage_menu->Append( Sort_Event, wxU(_("&Sort")) );
130     manage_menu->Append( RSort_Event, wxU(_("&Reverse Sort")) );
131     manage_menu->Append( Open_Event, wxU(_("&Open Playlist...")) );
132     manage_menu->Append( Save_Event, wxU(_("&Save Playlist...")) );
133     manage_menu->AppendSeparator();
134     manage_menu->Append( Close_Event, wxU(_("&Close")) );
135
136     /* Create our "Selection" menu */
137     wxMenu *selection_menu = new wxMenu;
138     selection_menu->Append( InvertSelection_Event, wxU(_("&Invert")) );
139     selection_menu->Append( DeleteSelection_Event, wxU(_("&Delete")) );
140     selection_menu->Append( SelectAll_Event, wxU(_("&Select All")) );
141
142     /* Append the freshly created menus to the menu bar */
143     wxMenuBar *menubar = new wxMenuBar( wxMB_DOCKABLE );
144     menubar->Append( manage_menu, wxU(_("&Manage")) );
145     menubar->Append( selection_menu, wxU(_("&Selection")) );
146
147     /* Attach the menu bar to the frame */
148     SetMenuBar( menubar );
149
150     /* Create a panel to put everything in */
151     wxPanel *playlist_panel = new wxPanel( this, -1 );
152     playlist_panel->SetAutoLayout( TRUE );
153
154     /* Create the listview */
155     /* FIXME: the given size is arbitrary, and prevents us from resizing
156      * the window to smaller dimensions. But the sizers don't seem to adjust
157      * themselves to the size of a listview, and with a wxDefaultSize the
158      * playlist window is ridiculously small */
159     listview = new wxListView( playlist_panel, ListView_Event,
160                                wxDefaultPosition, wxSize( 305, 300 ),
161                                wxLC_REPORT | wxSUNKEN_BORDER );
162     listview->InsertColumn( 0, wxU(_("Url")) );
163     #if 0
164         listview->InsertColumn( 1, wxU(_("Duration")) );
165     #endif
166     listview->SetColumnWidth( 0, 300 );
167     #if 0
168         listview->SetColumnWidth( 1, 100 );
169     #endif
170
171     /* Create the Random checkbox */
172     wxCheckBox *random_checkbox = 
173         new wxCheckBox( playlist_panel, Random_Event, wxU(_("Random")) );
174         
175     var_Get( p_intf, "random", &val);
176     vlc_bool_t b_random = val.b_bool;
177     random_checkbox->SetValue( b_random == VLC_FALSE ? 0 : 1);
178
179     /* Create the Loop Checkbox */
180     wxCheckBox *loop_checkbox = 
181         new wxCheckBox( playlist_panel, Loop_Event, wxU(_("Loop")) );
182
183     var_Get( p_intf, "loop", &val );
184     int b_loop = val.b_bool ; 
185     loop_checkbox->SetValue( b_loop );
186
187     /* Create the Repeat one checkbox */
188     wxCheckBox *repeat_checkbox = 
189         new wxCheckBox( playlist_panel, Repeat_Event, wxU(_("Repeat one")) );
190
191     var_Get( p_intf, "repeat", &val );
192     int b_repeat = val.b_bool ; 
193     repeat_checkbox->SetValue( b_repeat );
194
195     /* Create the Search Textbox */
196     search_text =
197         new wxTextCtrl( playlist_panel, SearchText_Event, wxT(""), 
198                         wxDefaultPosition, wxSize( 140, -1), 
199                         wxTE_PROCESS_ENTER);    
200
201     /* Create the search button */
202     search_button = 
203         new wxButton( playlist_panel, Search_Event, wxU(_("Search")) );
204
205  
206     /* Place everything in sizers */
207     wxBoxSizer *button_sizer = new wxBoxSizer( wxVERTICAL );
208     button_sizer->Add( random_checkbox, 0, 
209                              wxEXPAND|wxALIGN_RIGHT, 5);
210     button_sizer->Add( loop_checkbox, 0, 
211                               wxEXPAND|wxALIGN_RIGHT, 5);
212     button_sizer->Add( repeat_checkbox, 0, 
213                               wxEXPAND|wxALIGN_RIGHT, 5);
214
215     button_sizer->Layout();
216
217     wxBoxSizer *search_sizer = new wxBoxSizer( wxVERTICAL );
218     search_sizer->Add( search_text, 0, wxALL|wxALIGN_CENTER, 5);
219     search_sizer->Add( search_button, 0, wxALL|wxALIGN_CENTER, 5);
220
221     search_sizer->Layout();
222
223     wxBoxSizer *bottom_sizer = new wxBoxSizer( wxHORIZONTAL );
224     bottom_sizer->Add( search_sizer , 0, wxALL|wxALIGN_CENTER, 5 );
225     bottom_sizer->Add( button_sizer , 0, wxALL|wxALIGN_CENTER, 5 );
226     
227     bottom_sizer->Layout();
228
229     wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
230
231     wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
232     panel_sizer->Add( listview, 1, wxEXPAND | wxALL, 5 );
233     
234     panel_sizer->Add( bottom_sizer, 0 , wxALIGN_CENTRE);
235     panel_sizer->Layout();
236
237     playlist_panel->SetSizerAndFit( panel_sizer );
238     main_sizer->Add( playlist_panel, 1, wxGROW, 0 );
239     main_sizer->Layout();
240     SetSizerAndFit( main_sizer );
241
242 #if !defined(__WXX11__)
243     /* Associate drop targets with the playlist */
244     SetDropTarget( new DragAndDrop( p_intf ) );
245 #endif
246
247     playlist_t *p_playlist =
248         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
249                                        FIND_ANYWHERE );
250     if( p_playlist == NULL )
251     {
252         return;
253     }
254
255     /* We want to be noticed of playlit changes */
256     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
257     vlc_object_release( p_playlist );
258
259     /* Update the playlist */
260     Rebuild();
261 }
262
263 Playlist::~Playlist()
264 {
265     playlist_t *p_playlist =
266         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
267                                        FIND_ANYWHERE );
268     if( p_playlist == NULL )
269     {
270         return;
271     }
272
273     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
274     vlc_object_release( p_playlist );
275 }
276
277 void Playlist::Rebuild()
278 {
279     playlist_t *p_playlist =
280         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
281                                        FIND_ANYWHERE );
282     if( p_playlist == NULL )
283     {
284         return;
285     }
286
287     /* Clear the list... */
288     listview->DeleteAllItems();
289
290     /* ...and rebuild it */
291     vlc_mutex_lock( &p_playlist->object_lock );
292     for( int i = 0; i < p_playlist->i_size; i++ )
293     {
294         wxString filename = wxU(p_playlist->pp_items[i]->psz_name);
295         listview->InsertItem( i, filename );
296         /* FIXME: we should try to find the actual duration... */
297         /* While we don't use it, hide it, it's ugly */
298         #if 0
299                 listview->SetItem( i, 1, wxU(_("no info")) );
300         #endif
301     }
302     vlc_mutex_unlock( &p_playlist->object_lock );
303
304     /* Change the colour for the currenty played stream */
305     wxListItem listitem;
306     listitem.m_itemId = p_playlist->i_index;
307     listitem.SetTextColour( *wxRED );
308     listview->SetItem( listitem );
309
310 //    listview->Select( p_playlist->i_index, TRUE );
311     listview->Focus( p_playlist->i_index );
312
313     vlc_object_release( p_playlist );
314 }
315
316 void Playlist::ShowPlaylist( bool show )
317 {
318     if( show ) Rebuild();
319     Show( show );
320 }
321
322 void Playlist::UpdatePlaylist()
323 {
324     vlc_bool_t b_need_update = VLC_FALSE;
325     i_update_counter++;
326
327     /* If the playlist isn't show there's no need to update it */
328     if( !IsShown() ) return;
329
330     vlc_mutex_lock( &lock );
331     if( this->b_need_update )
332     {
333         b_need_update = VLC_TRUE;
334         this->b_need_update = VLC_FALSE;
335     }
336     vlc_mutex_unlock( &lock );
337
338     if( b_need_update ) Rebuild();
339
340     /* Updating the playing status every 0.5s is enough */
341     if( i_update_counter % 5 ) return;
342
343     playlist_t *p_playlist =
344         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
345                                        FIND_ANYWHERE );
346     if( p_playlist == NULL )
347     {
348         return;
349     }
350
351     /* Update the colour of items */
352     vlc_mutex_lock( &p_playlist->object_lock );
353     if( p_intf->p_sys->i_playing != p_playlist->i_index )
354     {
355         wxListItem listitem;
356         listitem.m_itemId = p_playlist->i_index;
357         listitem.SetTextColour( *wxRED );
358         listview->SetItem( listitem );
359
360         if( p_intf->p_sys->i_playing != -1 )
361         {
362             listitem.m_itemId = p_intf->p_sys->i_playing;
363             listitem.SetTextColour( *wxBLACK );
364             listview->SetItem( listitem );
365         }
366         p_intf->p_sys->i_playing = p_playlist->i_index;
367     }
368     vlc_mutex_unlock( &p_playlist->object_lock );
369
370     vlc_object_release( p_playlist );
371 }
372
373 /*****************************************************************************
374  * Private methods.
375  *****************************************************************************/
376 void Playlist::DeleteItem( int item )
377 {
378     playlist_t *p_playlist =
379         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
380                                        FIND_ANYWHERE );
381     if( p_playlist == NULL )
382     {
383         return;
384     }
385
386     playlist_Delete( p_playlist, item );
387     listview->DeleteItem( item );
388
389     vlc_object_release( p_playlist );
390 }
391
392 void Playlist::OnClose( wxCommandEvent& WXUNUSED(event) )
393 {
394     Hide();
395 }
396
397 void Playlist::OnSave( wxCommandEvent& WXUNUSED(event) )
398 {
399     playlist_t *p_playlist =
400         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
401                                        FIND_ANYWHERE );
402     if( p_playlist == NULL )
403     {
404         return;
405     }
406
407     wxFileDialog dialog( this, wxU(_("Save playlist")),
408                          wxT(""), wxT(""), wxT("*"), wxSAVE );
409
410     if( dialog.ShowModal() == wxID_OK )
411     {
412         playlist_SaveFile( p_playlist, dialog.GetPath().mb_str() );
413     }
414
415     vlc_object_release( p_playlist );
416 }
417
418 void Playlist::OnOpen( wxCommandEvent& WXUNUSED(event) )
419 {
420     playlist_t *p_playlist =
421         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
422                                        FIND_ANYWHERE );
423     if( p_playlist == NULL )
424     {
425         return;
426     }
427
428     wxFileDialog dialog( this, wxU(_("Open playlist")),
429                          wxT(""), wxT(""), wxT("*"), wxOPEN );
430
431     if( dialog.ShowModal() == wxID_OK )
432     {
433         playlist_LoadFile( p_playlist, dialog.GetPath().mb_str() );
434     }
435
436     vlc_object_release( p_playlist );
437 }
438
439 void Playlist::OnAddFile( wxCommandEvent& WXUNUSED(event) )
440 {
441     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE_SIMPLE, 0, 0 );
442
443 #if 0
444     Rebuild();
445 #endif
446 }
447
448 void Playlist::OnAddMRL( wxCommandEvent& WXUNUSED(event) )
449 {
450     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE, 0, 0 );
451
452 #if 0
453     Rebuild();
454 #endif
455 }
456
457 void Playlist::OnSort( wxCommandEvent& WXUNUSED(event) )
458 {
459     playlist_t *p_playlist =
460         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
461                                        FIND_ANYWHERE );
462     if( p_playlist == NULL )
463     {
464         return;
465     }
466
467     playlist_Sort( p_playlist , 0  );
468    
469     vlc_object_release( p_playlist );
470
471     Rebuild();
472
473     return;
474 }
475
476 void Playlist::OnRSort( wxCommandEvent& WXUNUSED(event) )
477 {
478     playlist_t *p_playlist =
479         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
480                                        FIND_ANYWHERE );
481     if( p_playlist == NULL )
482     {
483         return;
484     }
485
486     playlist_Sort( p_playlist , 1 );
487    
488     vlc_object_release( p_playlist );
489
490     Rebuild();
491
492     return;
493 }
494
495 void Playlist::OnSearchTextChange( wxCommandEvent& WXUNUSED(event) )
496 {
497    search_button->SetDefault(); 
498 }
499
500 void Playlist::OnSearch( wxCommandEvent& WXUNUSED(event) )
501 {
502     wxString search_string= search_text->GetValue();
503
504     int i_current;
505     int i_first = 0 ; 
506     int i_item = -1;
507
508     for( i_current = 0 ; i_current <= listview->GetItemCount() ; i_current++ ) 
509     {
510         if( listview->GetItemState( i_current, wxLIST_STATE_SELECTED)
511                 == wxLIST_STATE_SELECTED )
512         {
513             i_first = i_current;
514             break;
515         }
516     }
517
518     for ( i_current = i_first + 1; i_current <= listview->GetItemCount()
519                  ; i_current++ )
520     {
521         wxListItem listitem;
522         listitem.SetId( i_current );
523         listview->GetItem( listitem );
524         if( listitem.m_text.Lower().Contains( search_string.Lower() ) )
525         {
526            i_item = i_current;
527            break;
528         }
529     }
530     for( long item = 0; item < listview->GetItemCount(); item++ )
531     {
532         listview->Select( item, FALSE );
533     }
534  
535     wxListItem listitem;
536     listitem.SetId(i_item);
537     listitem.m_state = wxLIST_STATE_SELECTED;
538     listview->Select( i_item, TRUE );
539     listview->Focus( i_item );
540
541 }
542
543 void Playlist::OnInvertSelection( wxCommandEvent& WXUNUSED(event) )
544 {
545     for( long item = 0; item < listview->GetItemCount(); item++ )
546     {
547         listview->Select( item, ! listview->IsSelected( item ) );
548     }
549 }
550
551 void Playlist::OnDeleteSelection( wxCommandEvent& WXUNUSED(event) )
552 {
553     /* Delete from the end to the beginning, to avoid a shift of indices */
554     for( long item = listview->GetItemCount() - 1; item >= 0; item-- )
555     {
556         if( listview->IsSelected( item ) )
557         {
558             DeleteItem( item );
559         }
560     }
561
562     Rebuild();
563 }
564
565 void Playlist::OnRandom( wxCommandEvent& event )
566 {
567     vlc_value_t val;
568     val.b_bool = event.IsChecked();
569 // ? VLC_TRUE : VLC_FALSE ;
570     playlist_t *p_playlist =
571         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
572                                        FIND_ANYWHERE );
573     if( p_playlist == NULL )
574     {
575         return;
576     }
577     var_Set( p_playlist , "random", val);
578     vlc_object_release( p_playlist );
579 }
580 void Playlist::OnLoop ( wxCommandEvent& event )
581 {
582     vlc_value_t val;
583     val.b_bool = event.IsChecked();
584 // ? VLC_TRUE : VLC_FALSE ;
585     playlist_t *p_playlist =
586         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
587                                        FIND_ANYWHERE );
588     if( p_playlist == NULL )
589     {
590         return;
591     }
592     var_Set( p_playlist , "loop", val);
593     vlc_object_release( p_playlist );
594 }
595
596 void Playlist::OnRepeat ( wxCommandEvent& event )
597 {
598     vlc_value_t val;
599     val.b_bool = event.IsChecked();
600 // ? VLC_TRUE : VLC_FALSE ;
601     playlist_t *p_playlist =
602         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
603                                        FIND_ANYWHERE );
604     if( p_playlist == NULL )
605     {
606         return;
607     }
608     var_Set( p_playlist , "repeat", val);
609     vlc_object_release( p_playlist );
610 }
611
612 void Playlist::OnSelectAll( wxCommandEvent& WXUNUSED(event) )
613 {
614     for( long item = 0; item < listview->GetItemCount(); item++ )
615     {
616         listview->Select( item, TRUE );
617     }
618 }
619
620 void Playlist::OnActivateItem( wxListEvent& 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     playlist_Goto( p_playlist, event.GetIndex() );
631
632     vlc_object_release( p_playlist );
633 }
634 void Playlist::OnKeyDown( wxListEvent& event )
635 {
636     long keycode = event.GetKeyCode();
637     /* Delete selected items */
638     if( keycode == WXK_BACK || keycode == WXK_DELETE )
639     {
640         /* We send a dummy event */
641         OnDeleteSelection( event );
642     }
643 }
644
645 /*****************************************************************************
646  * PlaylistChanged: callback triggered by the intf-change playlist variable
647  *  We don't rebuild the playlist directly here because we don't want the
648  *  caller to block for a too long time.
649  *****************************************************************************/
650 int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
651                      vlc_value_t old_val, vlc_value_t new_val, void *param )
652 {
653     Playlist *p_playlist_dialog = (Playlist *)param;
654     vlc_mutex_lock( &p_playlist_dialog->lock );
655     p_playlist_dialog->b_need_update = VLC_TRUE;
656     vlc_mutex_unlock( &p_playlist_dialog->lock );
657     return VLC_SUCCESS;
658 }