1 /*****************************************************************************
2 * playlist.cpp : wxWindows plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2001 VideoLAN
5 * $Id: playlist.cpp,v 1.22 2003/10/06 16:23:30 zorglub Exp $
7 * Authors: Olivier Teulière <ipkiss@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
28 #include <errno.h> /* ENOMEM */
29 #include <string.h> /* strerror() */
35 #include "wxwindows.h"
36 #include <wx/listctrl.h>
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 );
42 /*****************************************************************************
44 *****************************************************************************/
46 /* IDs for the controls and the menu commands */
58 EnableSelection_Event,
59 DisableSelection_Event,
61 InvertSelection_Event,
62 DeleteSelection_Event,
79 BEGIN_EVENT_TABLE(Playlist, wxFrame)
81 EVT_MENU(AddFile_Event, Playlist::OnAddFile)
82 EVT_MENU(AddMRL_Event, Playlist::OnAddMRL)
83 EVT_MENU(Sort_Event, Playlist::OnSort)
84 EVT_MENU(RSort_Event, Playlist::OnRSort)
85 EVT_MENU(Close_Event, Playlist::OnClose)
86 EVT_MENU(Open_Event, Playlist::OnOpen)
87 EVT_MENU(Save_Event, Playlist::OnSave)
88 EVT_MENU(EnableSelection_Event, Playlist::OnEnableSelection)
89 EVT_MENU(DisableSelection_Event, Playlist::OnDisableSelection)
90 EVT_MENU(InvertSelection_Event, Playlist::OnInvertSelection)
91 EVT_MENU(DeleteSelection_Event, Playlist::OnDeleteSelection)
92 EVT_MENU(SelectAll_Event, Playlist::OnSelectAll)
93 EVT_MENU(Infos_Event, Playlist::OnInfos)
94 EVT_CHECKBOX(Random_Event, Playlist::OnRandom)
95 EVT_CHECKBOX(Repeat_Event, Playlist::OnRepeat)
96 EVT_CHECKBOX(Loop_Event, Playlist::OnLoop)
99 EVT_LIST_ITEM_ACTIVATED(ListView_Event, Playlist::OnActivateItem)
100 EVT_LIST_KEY_DOWN(ListView_Event, Playlist::OnKeyDown)
103 EVT_BUTTON( Search_Event, Playlist::OnSearch)
104 EVT_BUTTON( En_Dis_Event, Playlist::OnEnDis)
105 EVT_BUTTON( Save_Event, Playlist::OnSave)
106 EVT_BUTTON( Infos_Event, Playlist::OnInfos)
108 EVT_TEXT(SearchText_Event, Playlist::OnSearchTextChange)
110 /* Special events : we don't want to destroy the window when the user
112 EVT_CLOSE(Playlist::OnClose)
115 /*****************************************************************************
117 *****************************************************************************/
118 Playlist::Playlist( intf_thread_t *_p_intf, wxWindow *p_parent ):
119 wxFrame( p_parent, -1, wxU(_("Playlist")), wxDefaultPosition,
120 wxDefaultSize, wxDEFAULT_FRAME_STYLE )
122 /* Initializations */
123 iteminfo_dialog = NULL;
126 i_update_counter = 0;
127 b_need_update = VLC_FALSE;
128 vlc_mutex_init( p_intf, &lock );
129 SetIcon( *p_intf->p_sys->p_icon );
131 var_Create( p_intf, "random", VLC_VAR_BOOL );
132 var_Change( p_intf, "random", VLC_VAR_INHERITVALUE, & val, NULL );
133 var_Create( p_intf, "loop", VLC_VAR_BOOL );
134 var_Create( p_intf, "loop", VLC_VAR_BOOL );
135 var_Change( p_intf, "repeat", VLC_VAR_INHERITVALUE, & val, NULL );
136 var_Change( p_intf, "repeat", VLC_VAR_INHERITVALUE, & val, NULL );
138 /* Create our "Manage" menu */
139 wxMenu *manage_menu = new wxMenu;
140 manage_menu->Append( AddFile_Event, wxU(_("&Simple Add...")) );
141 manage_menu->Append( AddMRL_Event, wxU(_("&Add MRL...")) );
142 manage_menu->Append( Sort_Event, wxU(_("&Sort")) );
143 manage_menu->Append( RSort_Event, wxU(_("&Reverse Sort")) );
144 manage_menu->Append( Open_Event, wxU(_("&Open Playlist...")) );
145 manage_menu->Append( Save_Event, wxU(_("&Save Playlist...")) );
146 manage_menu->AppendSeparator();
147 manage_menu->Append( Close_Event, wxU(_("&Close")) );
149 /* Create our "Selection" menu */
150 wxMenu *selection_menu = new wxMenu;
151 selection_menu->Append( EnableSelection_Event, wxU(_("&Enable")) );
152 selection_menu->Append( DisableSelection_Event, wxU(_("&Disable")) );
153 selection_menu->AppendSeparator();
154 selection_menu->Append( InvertSelection_Event, wxU(_("&Invert")) );
155 selection_menu->Append( DeleteSelection_Event, wxU(_("&Delete")) );
156 selection_menu->Append( SelectAll_Event, wxU(_("&Select All")) );
158 /* Append the freshly created menus to the menu bar */
159 wxMenuBar *menubar = new wxMenuBar( wxMB_DOCKABLE );
160 menubar->Append( manage_menu, wxU(_("&Manage")) );
161 menubar->Append( selection_menu, wxU(_("&Selection")) );
163 /* Attach the menu bar to the frame */
164 SetMenuBar( menubar );
166 /* Create a panel to put everything in */
167 wxPanel *playlist_panel = new wxPanel( this, -1 );
168 playlist_panel->SetAutoLayout( TRUE );
170 /* Create the listview */
171 /* FIXME: the given size is arbitrary, and prevents us from resizing
172 * the window to smaller dimensions. But the sizers don't seem to adjust
173 * themselves to the size of a listview, and with a wxDefaultSize the
174 * playlist window is ridiculously small */
175 listview = new wxListView( playlist_panel, ListView_Event,
176 wxDefaultPosition, wxSize( 305, 300 ),
177 wxLC_REPORT | wxSUNKEN_BORDER );
178 listview->InsertColumn( 0, wxU(_("Url")) );
180 listview->InsertColumn( 1, wxU(_("Duration")) );
182 listview->SetColumnWidth( 0, 300 );
184 listview->SetColumnWidth( 1, 100 );
187 /* Create the Random checkbox */
188 wxCheckBox *random_checkbox =
189 new wxCheckBox( playlist_panel, Random_Event, wxU(_("Random")) );
191 var_Get( p_intf, "random", &val);
192 vlc_bool_t b_random = val.b_bool;
193 random_checkbox->SetValue( b_random == VLC_FALSE ? 0 : 1);
195 /* Create the Loop Checkbox */
196 wxCheckBox *loop_checkbox =
197 new wxCheckBox( playlist_panel, Loop_Event, wxU(_("Loop")) );
199 var_Get( p_intf, "loop", &val );
200 int b_loop = val.b_bool ;
201 loop_checkbox->SetValue( b_loop );
203 /* Create the Repeat one checkbox */
204 wxCheckBox *repeat_checkbox =
205 new wxCheckBox( playlist_panel, Repeat_Event, wxU(_("Repeat one")) );
207 var_Get( p_intf, "repeat", &val );
208 int b_repeat = val.b_bool ;
209 repeat_checkbox->SetValue( b_repeat );
211 /* Create the Search Textbox */
213 new wxTextCtrl( playlist_panel, SearchText_Event, wxT(""),
214 wxDefaultPosition, wxSize( 140, -1),
217 /* Create the search button */
219 new wxButton( playlist_panel, Search_Event, wxU(_("Search")) );
221 wxButton *en_dis_button =
222 new wxButton( playlist_panel, En_Dis_Event, wxU(_("Enable/Disable Group") ) );
224 wxButton *iteminfo_button =
225 new wxButton( playlist_panel, Infos_Event, wxU(_("Item Infos") ) );
226 /* Place everything in sizers */
227 wxBoxSizer *button_sizer = new wxBoxSizer( wxHORIZONTAL );
228 button_sizer->Add( en_dis_button, 0, wxALIGN_CENTER|wxRIGHT, 5);
229 button_sizer->Add( iteminfo_button, 0, wxALIGN_CENTER|wxLEFT , 5);
230 button_sizer->Layout();
233 wxBoxSizer *checkbox_sizer = new wxBoxSizer( wxHORIZONTAL );
234 checkbox_sizer->Add( random_checkbox, 0,
235 wxEXPAND|wxALIGN_RIGHT, 5);
236 checkbox_sizer->Add( loop_checkbox, 0,
237 wxEXPAND|wxALIGN_RIGHT, 5);
238 checkbox_sizer->Add( repeat_checkbox, 0,
239 wxEXPAND|wxALIGN_RIGHT, 5);
241 checkbox_sizer->Layout();
243 wxBoxSizer *search_sizer = new wxBoxSizer( wxHORIZONTAL );
244 search_sizer->Add( search_text, 0, wxALL|wxALIGN_CENTER, 5);
245 search_sizer->Add( search_button, 0, wxALL|wxALIGN_CENTER, 5);
247 search_sizer->Layout();
249 wxBoxSizer *bottom_sizer = new wxBoxSizer( wxVERTICAL );
250 bottom_sizer->Add( checkbox_sizer, 0, wxALL|wxALIGN_CENTER, 5 );
251 bottom_sizer->Add( button_sizer , 0, wxALL|wxALIGN_CENTER, 5 );
253 bottom_sizer->Layout();
255 wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
257 wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
258 panel_sizer->Add( listview, 1, wxEXPAND | wxALL, 5 );
260 panel_sizer->Add( search_sizer, 0, wxALIGN_CENTRE );
261 panel_sizer->Add( bottom_sizer, 0 , wxALIGN_CENTRE);
262 panel_sizer->Layout();
264 playlist_panel->SetSizerAndFit( panel_sizer );
265 main_sizer->Add( playlist_panel, 1, wxGROW, 0 );
266 main_sizer->Layout();
267 SetSizerAndFit( main_sizer );
269 #if !defined(__WXX11__)
270 /* Associate drop targets with the playlist */
271 SetDropTarget( new DragAndDrop( p_intf ) );
274 playlist_t *p_playlist =
275 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
277 if( p_playlist == NULL )
282 /* We want to be noticed of playlit changes */
283 var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
284 vlc_object_release( p_playlist );
286 /* Update the playlist */
290 Playlist::~Playlist()
292 playlist_t *p_playlist =
293 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
295 if( p_playlist == NULL )
300 delete iteminfo_dialog;
302 var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
303 vlc_object_release( p_playlist );
306 void Playlist::Rebuild()
308 playlist_t *p_playlist =
309 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
311 if( p_playlist == NULL )
316 /* Clear the list... */
317 listview->DeleteAllItems();
319 /* ...and rebuild it */
320 vlc_mutex_lock( &p_playlist->object_lock );
321 for( int i = 0; i < p_playlist->i_size; i++ )
323 wxString filename = wxU(p_playlist->pp_items[i]->psz_name);
324 listview->InsertItem( i, filename );
325 if( p_playlist->pp_items[i]->b_enabled == VLC_FALSE )
328 listitem.m_itemId = i;
329 listitem.SetTextColour( *wxLIGHT_GREY);
330 listview->SetItem(listitem);
332 /* FIXME: we should try to find the actual duration... */
333 /* While we don't use it, hide it, it's ugly */
335 listview->SetItem( i, 1, wxU(_("no info")) );
338 vlc_mutex_unlock( &p_playlist->object_lock );
340 /* Change the colour for the currenty played stream */
342 listitem.m_itemId = p_playlist->i_index;
343 listitem.SetTextColour( *wxRED );
344 listview->SetItem( listitem );
346 listview->Focus( p_playlist->i_index );
348 vlc_object_release( p_playlist );
351 void Playlist::ShowPlaylist( bool show )
353 if( show ) Rebuild();
357 void Playlist::UpdatePlaylist()
359 vlc_bool_t b_need_update = VLC_FALSE;
362 /* If the playlist isn't show there's no need to update it */
363 if( !IsShown() ) return;
365 vlc_mutex_lock( &lock );
366 if( this->b_need_update )
368 b_need_update = VLC_TRUE;
369 this->b_need_update = VLC_FALSE;
371 vlc_mutex_unlock( &lock );
373 if( b_need_update ) Rebuild();
375 /* Updating the playing status every 0.5s is enough */
376 if( i_update_counter % 5 ) return;
378 playlist_t *p_playlist =
379 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
381 if( p_playlist == NULL )
386 /* Update the colour of items */
387 vlc_mutex_lock( &p_playlist->object_lock );
388 if( p_intf->p_sys->i_playing != p_playlist->i_index )
391 listitem.m_itemId = p_playlist->i_index;
392 listitem.SetTextColour( *wxRED );
393 listview->SetItem( listitem );
395 if( p_intf->p_sys->i_playing != -1 )
397 listitem.m_itemId = p_intf->p_sys->i_playing;
398 listitem.SetTextColour( *wxBLACK );
399 listview->SetItem( listitem );
401 p_intf->p_sys->i_playing = p_playlist->i_index;
403 vlc_mutex_unlock( &p_playlist->object_lock );
405 vlc_object_release( p_playlist );
408 /*****************************************************************************
410 *****************************************************************************/
411 void Playlist::DeleteItem( int item )
413 playlist_t *p_playlist =
414 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
416 if( p_playlist == NULL )
421 playlist_Delete( p_playlist, item );
422 listview->DeleteItem( item );
424 vlc_object_release( p_playlist );
427 void Playlist::OnClose( wxCommandEvent& WXUNUSED(event) )
432 void Playlist::OnSave( wxCommandEvent& WXUNUSED(event) )
434 playlist_t *p_playlist =
435 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
437 if( p_playlist == NULL )
442 wxFileDialog dialog( this, wxU(_("Save playlist")),
443 wxT(""), wxT(""), wxT("*"), wxSAVE );
445 if( dialog.ShowModal() == wxID_OK )
447 playlist_SaveFile( p_playlist, dialog.GetPath().mb_str() );
450 vlc_object_release( p_playlist );
453 void Playlist::OnOpen( wxCommandEvent& WXUNUSED(event) )
455 playlist_t *p_playlist =
456 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
458 if( p_playlist == NULL )
463 wxFileDialog dialog( this, wxU(_("Open playlist")),
464 wxT(""), wxT(""), wxT("*"), wxOPEN );
466 if( dialog.ShowModal() == wxID_OK )
468 playlist_LoadFile( p_playlist, dialog.GetPath().mb_str() );
471 vlc_object_release( p_playlist );
474 void Playlist::OnAddFile( wxCommandEvent& WXUNUSED(event) )
476 p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE_SIMPLE, 0, 0 );
483 void Playlist::OnAddMRL( wxCommandEvent& WXUNUSED(event) )
485 p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE, 0, 0 );
492 void Playlist::OnSort( wxCommandEvent& WXUNUSED(event) )
494 playlist_t *p_playlist =
495 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
497 if( p_playlist == NULL )
502 playlist_Sort( p_playlist , 0 );
504 vlc_object_release( p_playlist );
511 void Playlist::OnRSort( wxCommandEvent& WXUNUSED(event) )
513 playlist_t *p_playlist =
514 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
516 if( p_playlist == NULL )
521 playlist_Sort( p_playlist , 1 );
523 vlc_object_release( p_playlist );
530 void Playlist::OnSearchTextChange( wxCommandEvent& WXUNUSED(event) )
532 search_button->SetDefault();
535 void Playlist::OnSearch( wxCommandEvent& WXUNUSED(event) )
537 wxString search_string= search_text->GetValue();
543 for( i_current = 0 ; i_current <= listview->GetItemCount() ; i_current++ )
545 if( listview->GetItemState( i_current, wxLIST_STATE_SELECTED)
546 == wxLIST_STATE_SELECTED )
553 for ( i_current = i_first + 1; i_current <= listview->GetItemCount() ;
557 listitem.SetId( i_current );
558 listview->GetItem( listitem );
559 if( listitem.m_text.Lower().Contains( search_string.Lower() ) )
565 for( long item = 0; item < listview->GetItemCount(); item++ )
567 listview->Select( item, FALSE );
571 listitem.SetId(i_item);
572 listitem.m_state = wxLIST_STATE_SELECTED;
573 listview->Select( i_item, TRUE );
574 listview->Focus( i_item );
578 void Playlist::OnInvertSelection( wxCommandEvent& WXUNUSED(event) )
580 for( long item = 0; item < listview->GetItemCount(); item++ )
582 listview->Select( item, ! listview->IsSelected( item ) );
586 void Playlist::OnDeleteSelection( wxCommandEvent& WXUNUSED(event) )
588 /* Delete from the end to the beginning, to avoid a shift of indices */
589 for( long item = listview->GetItemCount() - 1; item >= 0; item-- )
591 if( listview->IsSelected( item ) )
600 void Playlist::OnEnableSelection( wxCommandEvent& WXUNUSED(event) )
602 playlist_t *p_playlist =
603 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
605 if( p_playlist == NULL )
610 for( long item = listview->GetItemCount() - 1; item >= 0; item-- )
612 if( listview->IsSelected( item ) )
614 playlist_Enable( p_playlist, item );
617 vlc_object_release( p_playlist);
621 void Playlist::OnDisableSelection( wxCommandEvent& WXUNUSED(event) )
623 playlist_t *p_playlist =
624 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
626 if( p_playlist == NULL )
631 for( long item = listview->GetItemCount() - 1; item >= 0; item-- )
633 if( listview->IsSelected( item ) )
635 playlist_Disable( p_playlist, item );
638 vlc_object_release( p_playlist);
642 void Playlist::OnRandom( wxCommandEvent& event )
645 val.b_bool = event.IsChecked();
646 playlist_t *p_playlist =
647 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
649 if( p_playlist == NULL )
653 var_Set( p_playlist , "random", val);
654 vlc_object_release( p_playlist );
656 void Playlist::OnLoop ( wxCommandEvent& event )
659 val.b_bool = event.IsChecked();
660 playlist_t *p_playlist =
661 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
663 if( p_playlist == NULL )
667 var_Set( p_playlist , "loop", val);
668 vlc_object_release( p_playlist );
671 void Playlist::OnRepeat ( wxCommandEvent& event )
674 val.b_bool = event.IsChecked();
675 playlist_t *p_playlist =
676 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
678 if( p_playlist == NULL )
682 var_Set( p_playlist , "repeat", val);
683 vlc_object_release( p_playlist );
686 void Playlist::OnSelectAll( wxCommandEvent& WXUNUSED(event) )
688 for( long item = 0; item < listview->GetItemCount(); item++ )
690 listview->Select( item, TRUE );
694 void Playlist::OnActivateItem( wxListEvent& event )
696 playlist_t *p_playlist =
697 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
699 if( p_playlist == NULL )
703 playlist_Goto( p_playlist, event.GetIndex() );
705 vlc_object_release( p_playlist );
707 void Playlist::OnKeyDown( wxListEvent& event )
709 long keycode = event.GetKeyCode();
710 /* Delete selected items */
711 if( keycode == WXK_BACK || keycode == WXK_DELETE )
713 /* We send a dummy event */
714 OnDeleteSelection( event );
718 void Playlist::OnInfos( wxCommandEvent& WXUNUSED(event) )
720 playlist_t *p_playlist =
721 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
723 if( p_playlist == NULL )
728 if( iteminfo_dialog == NULL )
730 /* We use the first selected item, so find it */
732 i_item = listview->GetNextItem(i_item,
734 wxLIST_STATE_SELECTED);
735 if( i_item >= 0 && i_item < p_playlist->i_size )
737 iteminfo_dialog = new ItemInfoDialog(
738 p_intf, p_playlist->pp_items[i_item], this );
739 if( iteminfo_dialog->ShowModal() == wxID_OK )
741 delete iteminfo_dialog;
742 iteminfo_dialog = NULL;
745 vlc_object_release( p_playlist );
749 void Playlist::OnEnDis( wxCommandEvent& WXUNUSED(event) )
751 playlist_t *p_playlist =
752 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
754 if( p_playlist == NULL )
760 i_item = listview->GetNextItem(i_item,
762 wxLIST_STATE_SELECTED);
764 if( i_item >= 0 && i_item < p_playlist->i_size )
766 if( p_playlist->pp_items[i_item]->b_enabled == VLC_TRUE)
767 playlist_DisableGroup( p_playlist ,
768 p_playlist->pp_items[i_item]->i_group );
770 playlist_EnableGroup( p_playlist ,
771 p_playlist->pp_items[i_item]->i_group );
775 vlc_object_release( p_playlist );
777 /*****************************************************************************
778 * PlaylistChanged: callback triggered by the intf-change playlist variable
779 * We don't rebuild the playlist directly here because we don't want the
780 * caller to block for a too long time.
781 *****************************************************************************/
782 int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
783 vlc_value_t old_val, vlc_value_t new_val, void *param )
785 Playlist *p_playlist_dialog = (Playlist *)param;
786 vlc_mutex_lock( &p_playlist_dialog->lock );
787 p_playlist_dialog->b_need_update = VLC_TRUE;
788 vlc_mutex_unlock( &p_playlist_dialog->lock );