0bfe6b0c7bbca9336e88cd7f10e3b1ae462f8cf0
[vlc.git] / modules / gui / wxwindows / bookmarks.cpp
1 /*****************************************************************************
2  * bookmarks.cpp : wxWindows plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
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/dialog.h>
37
38 /* Callback prototype */
39 static int PlaylistChanged( vlc_object_t *, const char *,
40                             vlc_value_t, vlc_value_t, void * );
41
42 /*****************************************************************************
43  * Class declaration.
44  *****************************************************************************/
45
46 /* IDs for the controls and the menu commands */
47 enum
48 {
49     /* menu items */
50     ButtonAdd_Event = wxID_HIGHEST + 1,
51     ButtonDel_Event,
52     ButtonClear_Event,
53     ButtonExtract_Event,
54     ButtonEdit_Event
55 };
56
57 class BookmarksDialog: public wxFrame
58 {
59 public:
60     /* Constructor */
61     BookmarksDialog( intf_thread_t *p_intf, wxWindow *p_parent );
62     virtual ~BookmarksDialog();
63
64     bool Show( bool );
65
66 private:
67
68     void Update();
69
70     /* Event handlers (these functions should _not_ be virtual) */
71     void OnClose( wxCommandEvent& event );
72     void OnAdd( wxCommandEvent& event );
73     void OnDel( wxCommandEvent& event );
74     void OnClear( wxCommandEvent& event );
75     void OnActivateItem( wxListEvent& event );
76     void OnUpdate( wxCommandEvent &event );
77     void OnEdit( wxCommandEvent& event );
78     void OnExtract( wxCommandEvent& event );
79
80     DECLARE_EVENT_TABLE();
81
82     intf_thread_t *p_intf;
83     wxWindow *p_parent;
84
85     wxListView *list_ctrl;
86 };
87
88 /*****************************************************************************
89  * Event Table.
90  *****************************************************************************/
91
92 DEFINE_LOCAL_EVENT_TYPE( wxEVT_BOOKMARKS );
93
94 BEGIN_EVENT_TABLE(BookmarksDialog, wxFrame)
95     /* Hide the window when the user closes the window */
96     EVT_CLOSE(BookmarksDialog::OnClose )
97     EVT_BUTTON( ButtonAdd_Event, BookmarksDialog::OnAdd )
98     EVT_BUTTON( ButtonDel_Event, BookmarksDialog::OnDel )
99     EVT_BUTTON( ButtonClear_Event, BookmarksDialog::OnClear )
100     EVT_BUTTON( ButtonExtract_Event, BookmarksDialog::OnExtract )
101     EVT_BUTTON( ButtonEdit_Event, BookmarksDialog::OnEdit )
102
103     EVT_LIST_ITEM_ACTIVATED( -1, BookmarksDialog::OnActivateItem )
104
105     EVT_COMMAND( -1, wxEVT_BOOKMARKS, BookmarksDialog::OnUpdate )
106 END_EVENT_TABLE()
107
108 /* Declaration of class BookmarkEditDialog */
109 class BookmarkEditDialog : public wxDialog
110 {
111 public:
112     /* Constructor */
113     BookmarkEditDialog( intf_thread_t *p_intf, wxWindow *p_parent,
114                   seekpoint_t *p_seekpoint );
115     virtual ~BookmarkEditDialog();
116     seekpoint_t *p_seekpoint;
117 private:
118
119     wxTextCtrl *name_text, *time_text, *bytes_text;
120
121     void OnOK( wxCommandEvent& event);
122     void OnCancel( wxCommandEvent& event);
123
124     DECLARE_EVENT_TABLE();
125
126     intf_thread_t *p_intf;
127 };
128
129 BEGIN_EVENT_TABLE( BookmarkEditDialog, wxDialog)
130             EVT_BUTTON( wxID_OK, BookmarkEditDialog::OnOK)
131 END_EVENT_TABLE()
132 /****************************************************************************
133  * BookmarkEditDialog
134  ***************************************************************************/
135 BookmarkEditDialog::BookmarkEditDialog( intf_thread_t *_p_intf,
136            wxWindow *_p_parent, seekpoint_t *_p_seekpoint ):wxDialog(
137             _p_parent, -1, wxU(_("Edit bookmark")), wxDefaultPosition,
138             wxDefaultSize, wxDEFAULT_FRAME_STYLE )
139 {
140     /* Initializations */
141     p_intf = _p_intf;
142     p_seekpoint = _p_seekpoint;
143     SetIcon( *p_intf->p_sys->p_icon );
144
145     /* Create a panel to put everything in*/
146     wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
147
148     wxFlexGridSizer * sizer = new wxFlexGridSizer( 2 , 3 , 1 );
149     name_text = new wxTextCtrl( this, -1, wxU( p_seekpoint->psz_name ?
150                                                p_seekpoint->psz_name : "" ),
151                                 wxDefaultPosition, wxSize( 100, 20) );
152     time_text = new wxTextCtrl( this, -1, wxString::Format(wxT("%d"),
153                                 (int)(p_seekpoint->i_time_offset / 1000000) ),
154                                 wxDefaultPosition, wxSize( 100, 20) );
155     bytes_text = new wxTextCtrl( this, -1, wxString::Format(wxT("%d"),
156                                 (int)p_seekpoint->i_byte_offset ),
157                                 wxDefaultPosition, wxSize( 100, 20) );
158
159     sizer->Add( new wxStaticText( this, -1, wxU(_("Name") ) ), 0, wxLEFT, 5 );
160     sizer->Add( name_text, 0, wxEXPAND|wxRIGHT , 5 );
161     sizer->Add( new wxStaticText( this, -1, wxU(_("Time") ) ), 0, wxLEFT, 5 );
162     sizer->Add( time_text , 0, wxEXPAND|wxRIGHT , 5);
163     sizer->Add( new wxStaticText( this, -1, wxU(_("Bytes") ) ), 0, wxLEFT, 5 );
164     sizer->Add( bytes_text, 0, wxEXPAND|wxRIGHT, 5);
165
166     wxBoxSizer *button_sizer = new wxBoxSizer( wxHORIZONTAL );
167     button_sizer->Add( new wxButton( this, wxID_OK, wxU(_("OK") ) ) );
168     button_sizer->Add( new wxButton( this, wxID_CANCEL, wxU(_("Cancel") ) ) );
169
170     panel_sizer->Add( sizer, 0, wxEXPAND | wxTOP|wxBOTTOM, 5 );
171     panel_sizer->Add( button_sizer, 0, wxEXPAND | wxBOTTOM, 5 );
172     panel_sizer->Layout();
173     SetSizerAndFit( panel_sizer );
174 }
175
176 BookmarkEditDialog::~BookmarkEditDialog()
177 {
178 }
179 void BookmarkEditDialog::OnOK( wxCommandEvent &event )
180 {
181     if( p_seekpoint->psz_name ) free( p_seekpoint->psz_name );
182     p_seekpoint->psz_name = strdup( name_text->GetValue().mb_str() );
183     p_seekpoint->i_byte_offset = atoi( bytes_text->GetValue().mb_str() );
184     p_seekpoint->i_time_offset =  1000000 *
185                                   atoll( time_text->GetValue().mb_str() ) ;
186     EndModal( wxID_OK );
187 }
188
189 void BookmarkEditDialog::OnCancel( wxCommandEvent &event )
190 {
191     EndModal( wxID_CANCEL );
192 }
193
194 /*****************************************************************************
195  * Constructor.
196  *****************************************************************************/
197 BookmarksDialog::BookmarksDialog( intf_thread_t *_p_intf, wxWindow *p_parent )
198   : wxFrame( p_parent->GetParent() ? p_parent->GetParent() : p_parent,
199              -1, wxU(_("Bookmarks")),
200              !p_parent->GetParent() ? wxDefaultPosition :
201                wxPoint( p_parent->GetParent()->GetRect().GetX(),
202                         p_parent->GetParent()->GetRect().GetY() +
203                         p_parent->GetParent()->GetRect().GetHeight() + 40 ),
204              wxSize( 500, -1 ),
205              wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT )
206 {
207     /* Initializations */
208     p_intf = _p_intf;
209     SetIcon( *p_intf->p_sys->p_icon );
210
211     wxPanel *main_panel = new wxPanel( this, -1 );
212     wxBoxSizer *main_sizer = new wxBoxSizer( wxHORIZONTAL );
213
214     wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
215
216     wxPanel *panel = new wxPanel( main_panel, -1 );
217     wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
218     wxButton *button_add =
219         new wxButton( panel, ButtonAdd_Event, wxU(_("Add")) );
220     wxButton *button_del =
221         new wxButton( panel, ButtonDel_Event, wxU(_("Remove")) );
222     wxButton *button_clear =
223         new wxButton( panel, ButtonClear_Event, wxU(_("Clear")) );
224     wxButton *button_edit =
225         new wxButton( panel, ButtonEdit_Event, wxU(_("Edit")) );
226     wxButton *button_extract =
227         new wxButton( panel, ButtonExtract_Event, wxU(_("Extract")) );
228
229 #define ADD_TEXT "Adds a bookmark at the current position in the stream"
230 #define REMOVE_TEXT "Removes the selected bookmarks"
231 #define CLEAR_TEXT "Removes all the bookmarks for that stream"
232 #define EDIT_TEXT "Edit the properties of a bookmark"
233 #define EXTRACT_TEXT "If you select two or more bookmarks, this will " \
234                "launch the streaming/transcoding wizard to allow you to " \
235               "stream or save the part of the stream between these bookmarks"
236     button_add->SetToolTip(  wxU(_( ADD_TEXT ) ) );
237     button_del->SetToolTip(  wxU(_( REMOVE_TEXT ) ) );
238     button_clear->SetToolTip(  wxU(_( CLEAR_TEXT ) ) );
239     button_edit->SetToolTip(  wxU(_( EDIT_TEXT ) ) );
240     button_extract->SetToolTip( wxU(_( EXTRACT_TEXT ) ) );
241
242     panel_sizer->Add( button_add, 0, wxEXPAND );
243     panel_sizer->Add( button_del, 0, wxEXPAND );
244     panel_sizer->Add( button_clear, 0, wxEXPAND );
245
246     panel_sizer->Add( button_edit, 0, wxEXPAND );
247     panel_sizer->Add( 0, 0, 1 );
248     panel_sizer->Add( button_extract, 0, wxEXPAND );
249
250     panel->SetSizerAndFit( panel_sizer );
251
252     list_ctrl = new wxListView( main_panel, -1,
253                                 wxDefaultPosition, wxDefaultSize,
254                                 wxLC_REPORT | wxSUNKEN_BORDER );
255     list_ctrl->InsertColumn( 0, wxU(_("Description")) );
256     list_ctrl->SetColumnWidth( 0, 240 );
257     list_ctrl->InsertColumn( 1, wxU(_("Size offset")) );
258     list_ctrl->InsertColumn( 2, wxU(_("Time offset")) );
259
260     sizer->Add( panel, 0, wxEXPAND | wxALL, 1 );
261     sizer->Add( list_ctrl, 1, wxEXPAND | wxALL, 1 );
262     main_panel->SetSizer( sizer );
263
264     main_sizer->Add( main_panel, 1, wxEXPAND );
265     SetSizer( main_sizer );
266
267     playlist_t *p_playlist =
268         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
269                                        FIND_ANYWHERE );
270     if( p_playlist )
271     {
272        /* Some global changes happened -> Rebuild all */
273        var_AddCallback( p_playlist, "playlist-current",
274                         PlaylistChanged, this );
275        vlc_object_release( p_playlist );
276     }
277 }
278
279 BookmarksDialog::~BookmarksDialog()
280 {
281     playlist_t *p_playlist =
282         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
283                                        FIND_ANYWHERE );
284     if( p_playlist )
285     {
286        var_DelCallback( p_playlist, "playlist-current",
287                         PlaylistChanged, this );
288        vlc_object_release( p_playlist );
289     }
290 }
291
292 /*****************************************************************************
293  * Private methods.
294  *****************************************************************************/
295 wxWindow *BookmarksDialog( intf_thread_t *p_intf, wxWindow *p_parent )
296 {
297     return new BookmarksDialog::BookmarksDialog( p_intf, p_parent );
298 }
299
300 void BookmarksDialog::Update()
301 {
302     input_thread_t *p_input =
303         (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
304                                            FIND_ANYWHERE );
305     if( !p_input ) return;
306
307     seekpoint_t **pp_bookmarks;
308     int i_bookmarks;
309
310     list_ctrl->DeleteAllItems();
311     if( input_Control( p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks,
312                        &i_bookmarks ) != VLC_SUCCESS )
313     {
314         vlc_object_release( p_input );
315         return;
316     }
317
318     for( int i = 0; i < i_bookmarks; i++ )
319     {
320         list_ctrl->InsertItem( i, wxL2U( pp_bookmarks[i]->psz_name ) );
321         /* FIXME: see if we can use the 64 bits integer format string */
322         list_ctrl->SetItem( i, 1, wxString::Format(wxT("%d"),
323                             (int)(pp_bookmarks[i]->i_byte_offset) ) );
324         list_ctrl->SetItem( i, 2, wxString::Format(wxT("%d"),
325                             (int)(pp_bookmarks[i]->i_time_offset / 1000000) ) );
326     }
327
328     vlc_object_release( p_input );
329 }
330
331 bool BookmarksDialog::Show( bool show )
332 {
333     Update();
334     return wxFrame::Show( show );
335 }
336
337 void BookmarksDialog::OnClose( wxCommandEvent& event )
338 {
339     Hide();
340 }
341
342 void BookmarksDialog::OnAdd( wxCommandEvent& event )
343 {
344     input_thread_t *p_input =
345         (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
346                                            FIND_ANYWHERE );
347     if( !p_input ) return;
348
349     seekpoint_t bookmark;
350     vlc_value_t pos;
351     bookmark.psz_name = NULL;
352     bookmark.i_byte_offset = 0;
353     bookmark.i_time_offset = 0;
354
355     var_Get( p_input, "position", &pos );
356     bookmark.psz_name = NULL;
357     input_Control( p_input, INPUT_GET_BYTE_POSITION, &bookmark.i_byte_offset );
358     var_Get( p_input, "time", &pos );
359     bookmark.i_time_offset = pos.i_time;
360     input_Control( p_input, INPUT_ADD_BOOKMARK, &bookmark );
361     vlc_object_release( p_input );
362
363     Update();
364 }
365
366 void BookmarksDialog::OnDel( wxCommandEvent& event )
367 {
368     input_thread_t *p_input =
369         (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
370                                            FIND_ANYWHERE );
371     if( !p_input ) return;
372
373     int i_focused = list_ctrl->GetFocusedItem();
374     if( i_focused >= 0 )
375     {
376         input_Control( p_input, INPUT_DEL_BOOKMARK, i_focused );
377     }
378
379     vlc_object_release( p_input );
380
381     Update();
382 }
383
384 void BookmarksDialog::OnClear( wxCommandEvent& event )
385 {
386     input_thread_t *p_input =
387         (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
388                                            FIND_ANYWHERE );
389     if( !p_input ) return;
390
391     input_Control( p_input, INPUT_CLEAR_BOOKMARKS );
392
393     vlc_object_release( p_input );
394
395     Update();
396 }
397
398 void BookmarksDialog::OnExtract( wxCommandEvent& event )
399 {
400     long i_first = list_ctrl->GetNextItem( -1, wxLIST_NEXT_ALL,
401                                           wxLIST_STATE_SELECTED );
402     long i_second = list_ctrl->GetNextItem( i_first, wxLIST_NEXT_ALL,
403                                           wxLIST_STATE_SELECTED );
404
405     if( i_first == -1 || i_second == -1 )
406     {
407         wxMessageBox( wxU(_("You must select two bookmarks") ),
408                       wxU(_("Invalid selection") ), wxICON_WARNING | wxOK,
409                       this );
410         return;
411     }
412     input_thread_t *p_input =
413         (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
414                                            FIND_ANYWHERE );
415     if( !p_input )
416     {
417         wxMessageBox( wxU(_("The stream must be playing or paused for "
418                             "bookmarks to work" ) ), wxU(_("No input found")),
419                             wxICON_WARNING | wxOK,
420                             this );
421         return;
422     }
423
424     seekpoint_t **pp_bookmarks;
425     int i_bookmarks ;
426
427     if( input_Control( p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks,
428                        &i_bookmarks ) != VLC_SUCCESS )
429     {
430         vlc_object_release( p_input );
431         return;
432     }
433
434     if( i_first < i_bookmarks && i_second <= i_bookmarks )
435     {
436         WizardDialog *p_wizard_dialog = new WizardDialog( p_intf, this,
437                                p_input->input.p_item->psz_uri,
438                                pp_bookmarks[i_first]->i_time_offset/1000000,
439                                pp_bookmarks[i_second]->i_time_offset/1000000 );
440         vlc_object_release( p_input );
441         if( p_wizard_dialog )
442         {
443             p_wizard_dialog->Run();
444             delete p_wizard_dialog;
445         }
446     }
447     else
448     {
449         vlc_object_release( p_input );
450     }
451 }
452
453 void BookmarksDialog::OnActivateItem( wxListEvent& event )
454 {
455     input_thread_t *p_input =
456         (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
457                                            FIND_ANYWHERE );
458     if( !p_input ) return;
459
460     input_Control( p_input, INPUT_SET_BOOKMARK, event.GetIndex() );
461
462     vlc_object_release( p_input );
463 }
464
465 void BookmarksDialog::OnEdit( wxCommandEvent& event )
466 {
467     input_thread_t *p_old_input;
468     input_thread_t *p_input =
469             (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
470                                                    FIND_ANYWHERE );
471     if( !p_input ) return;
472
473
474     seekpoint_t **pp_bookmarks;
475     int i_bookmarks;
476
477     if( input_Control( p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks,
478                        &i_bookmarks ) != VLC_SUCCESS )
479     {
480         vlc_object_release( p_input );
481         return;
482     }
483     p_old_input = p_input;
484     vlc_object_release( p_input );
485
486     long i_first = list_ctrl->GetNextItem( -1, wxLIST_NEXT_ALL,
487                                                wxLIST_STATE_SELECTED );
488
489     if( i_first > -1 && i_first <= i_bookmarks )
490     {
491         BookmarkEditDialog *p_bmk_edit;
492         p_bmk_edit = new BookmarkEditDialog( p_intf, this,
493                                pp_bookmarks[i_first]);
494
495         if( p_bmk_edit->ShowModal() == wxID_OK )
496         {
497             p_input =(input_thread_t *)vlc_object_find( p_intf,
498                             VLC_OBJECT_INPUT, FIND_ANYWHERE );
499            if( !p_input )
500            {
501                 wxMessageBox( wxU( _("No input found. The stream must be "
502                                   "playing or paused for bookmarks to work") ),
503                                wxU( _("No input") ), wxICON_WARNING | wxOK,
504                                this );
505                 return;
506            }
507            if( p_old_input != p_input )
508            {
509                 wxMessageBox( wxU( _("Input has changed, unable to save "
510                                   "bookmark. Use \"pause\" while editing "
511                                   "bookmarks to keep the same input.") ),
512                                wxU( _("Input has changed ") ),
513                                wxICON_WARNING | wxOK, this );
514                 vlc_object_release( p_input );
515                 return;
516
517            }
518            fprintf(stderr,"Changing %i\n",i_first );
519            if( input_Control(  p_input, INPUT_CHANGE_BOOKMARK,
520                                 p_bmk_edit->p_seekpoint, i_first ) !=
521                VLC_SUCCESS )
522            {
523                vlc_object_release( p_input );
524                return;
525            }
526            Update();
527            vlc_object_release( p_input );
528         }
529     }
530 }
531
532
533 void BookmarksDialog::OnUpdate( wxCommandEvent &event )
534 {
535     Update();
536 }
537
538 /*****************************************************************************
539  * PlaylistChanged: callback triggered by the intf-change playlist variable
540  *  We don't rebuild the playlist directly here because we don't want the
541  *  caller to block for a too long time.
542  *****************************************************************************/
543 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
544                             vlc_value_t oval, vlc_value_t nval, void *param )
545 {
546     BookmarksDialog::BookmarksDialog *p_dialog =
547         (BookmarksDialog::BookmarksDialog *)param;
548
549     wxCommandEvent bookmarks_event( wxEVT_BOOKMARKS, 0 );
550     p_dialog->AddPendingEvent( bookmarks_event );
551
552     return VLC_SUCCESS;
553 }