Qt: resume only for files
[vlc.git] / modules / gui / qt4 / input_manager.cpp
1 /*****************************************************************************
2  * input_manager.cpp : Manage an input and interact with its GUI elements
3  ****************************************************************************
4  * Copyright (C) 2006-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *          Ilkka Ollakka  <ileoo@videolan.org>
9  *          Jean-Baptiste <jb@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #define __STDC_FORMAT_MACROS 1
27 #define __STDC_CONSTANT_MACROS 1
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include "input_manager.hpp"
34 #include "recents.hpp"
35
36 #include <vlc_keys.h>           /* ACTION_ID */
37 #include <vlc_url.h>            /* decode_URI */
38 #include <vlc_strings.h>        /* str_format_meta */
39 #include <vlc_aout.h>           /* audio_output_t */
40
41 #include <QApplication>
42 #include <QFile>
43 #include <QDir>
44 #include <QSignalMapper>
45 #include <QMessageBox>
46
47 #include <assert.h>
48
49 static int ItemChanged( vlc_object_t *, const char *,
50                         vlc_value_t, vlc_value_t, void * );
51 static int LeafToParent( vlc_object_t *, const char *,
52                         vlc_value_t, vlc_value_t, void * );
53 static int PLItemChanged( vlc_object_t *, const char *,
54                         vlc_value_t, vlc_value_t, void * );
55 static int PLItemAppended( vlc_object_t *, const char *,
56                         vlc_value_t, vlc_value_t, void * );
57 static int PLItemRemoved( vlc_object_t *, const char *,
58                         vlc_value_t, vlc_value_t, void * );
59
60 static int InputEvent( vlc_object_t *, const char *,
61                        vlc_value_t, vlc_value_t, void * );
62 static int VbiEvent( vlc_object_t *, const char *,
63                      vlc_value_t, vlc_value_t, void * );
64
65 /* Ensure arbitratry (not dynamically allocated) event IDs are not in use */
66 static inline void registerAndCheckEventIds( int start, int end )
67 {
68     for ( int i=start ; i<=end ; i++ )
69         Q_ASSERT( QEvent::registerEventType( i ) == i ); /* event ID collision ! */
70 }
71
72 /**********************************************************************
73  * InputManager implementation
74  **********************************************************************
75  * The Input Manager can be the main one around the playlist
76  * But can also be used for VLM dialog or similar
77  **********************************************************************/
78
79 InputManager::InputManager( QObject *parent, intf_thread_t *_p_intf) :
80                            QObject( parent ), p_intf( _p_intf )
81 {
82     i_old_playing_status = END_S;
83     oldName      = "";
84     artUrl       = "";
85     p_input      = NULL;
86     p_input_vbi  = NULL;
87     f_rate       = 0.;
88     p_item       = NULL;
89     b_video      = false;
90     timeA        = 0;
91     timeB        = 0;
92     f_cache      = -1.; /* impossible initial value, different from all */
93     registerAndCheckEventIds( IMEvent::PositionUpdate, IMEvent::FullscreenControlPlanHide );
94     registerAndCheckEventIds( PLEvent::PLItemAppended, PLEvent::PLEmpty );
95 }
96
97 InputManager::~InputManager()
98 {
99     delInput();
100 }
101
102 void InputManager::inputChangedHandler()
103 {
104     setInput( THEMIM->getInput() );
105 }
106
107 /* Define the Input used.
108    Add the callbacks on input
109    p_input is held once here */
110 void InputManager::setInput( input_thread_t *_p_input )
111 {
112     delInput();
113     p_input = _p_input;
114     if( p_input != NULL )
115     {
116         msg_Dbg( p_intf, "IM: Setting an input" );
117         vlc_object_hold( p_input );
118         addCallbacks();
119
120         UpdateStatus();
121         UpdateName();
122         UpdateArt();
123         UpdateTeletext();
124         UpdateNavigation();
125         UpdateVout();
126
127         p_item = input_GetItem( p_input );
128         emit rateChanged( var_GetFloat( p_input, "rate" ) );
129
130         /* Get Saved Time */
131         if( p_item->i_type == ITEM_TYPE_FILE )
132         {
133             int i_time = RecentsMRL::getInstance( p_intf )->time( p_item->psz_uri );
134             if( i_time > 0 &&
135                     !var_GetFloat( p_input, "run-time" ) &&
136                     !var_GetFloat( p_input, "start-time" ) &&
137                     !var_GetFloat( p_input, "stop-time" ) )
138             {
139                 playlist_Pause( THEPL );
140
141                 if( QMessageBox::question( NULL,
142                             _("Continue playback?"),
143                             _("Do you want to restart the playback where left off?"),
144                             QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes )
145                         == QMessageBox::Yes )
146                     var_SetTime( p_input, "time", (int64_t)i_time * 1000 );
147
148                 playlist_Play( THEPL );
149             }
150         }
151     }
152     else
153     {
154         p_item = NULL;
155         assert( !p_input_vbi );
156         emit rateChanged( var_InheritFloat( p_intf, "rate" ) );
157     }
158 }
159
160 /* delete Input if it ever existed.
161    Delete the callbacls on input
162    p_input is released once here */
163 void InputManager::delInput()
164 {
165     if( !p_input ) return;
166     msg_Dbg( p_intf, "IM: Deleting the input" );
167
168     /* Save time / position */
169     float f_pos = var_GetFloat( p_input , "position" );
170     int64_t i_time = var_GetTime( p_input, "time");
171     int i_length = var_GetTime( p_input , "length" ) / CLOCK_FREQ;
172     if( f_pos < 0.05 || f_pos > 0.95 || i_length < 60) {
173         i_time = -1;
174     }
175     RecentsMRL::getInstance( p_intf )->setTime( p_item->psz_uri, i_time );
176
177     delCallbacks();
178     i_old_playing_status = END_S;
179     p_item               = NULL;
180     oldName              = "";
181     artUrl               = "";
182     b_video              = false;
183     timeA                = 0;
184     timeB                = 0;
185     f_rate               = 0. ;
186
187     if( p_input_vbi )
188     {
189         vlc_object_release( p_input_vbi );
190         p_input_vbi = NULL;
191     }
192
193     vlc_object_release( p_input );
194     p_input = NULL;
195
196     emit positionUpdated( -1.0, 0 ,0 );
197     emit rateChanged( var_InheritFloat( p_intf, "rate" ) );
198     emit nameChanged( "" );
199     emit chapterChanged( 0 );
200     emit titleChanged( 0 );
201     emit playingStatusChanged( END_S );
202
203     emit teletextPossible( false );
204     emit AtoBchanged( false, false );
205     emit voutChanged( false );
206     emit voutListChanged( NULL, 0 );
207
208     /* Reset all InfoPanels but stats */
209     emit artChanged( NULL );
210     emit artChanged( "" );
211     emit infoChanged( NULL );
212     emit currentMetaChanged( (input_item_t *)NULL );
213
214     emit encryptionChanged( false );
215     emit recordingStateChanged( false );
216
217     emit cachingChanged( 1 );
218 }
219
220 /* Convert the event from the callbacks in actions */
221 void InputManager::customEvent( QEvent *event )
222 {
223     int i_type = event->type();
224     IMEvent *ple = static_cast<IMEvent *>(event);
225
226     if( i_type == IMEvent::ItemChanged )
227         UpdateMeta( ple->item() );
228
229     if( !hasInput() )
230         return;
231
232     /* Actions */
233     switch( i_type )
234     {
235     case IMEvent::PositionUpdate:
236         UpdatePosition();
237         break;
238     case IMEvent::StatisticsUpdate:
239         UpdateStats();
240         break;
241     case IMEvent::ItemChanged:
242         /* Ignore ItemChanged_Type event that does not apply to our input */
243         if( p_item == ple->item() )
244         {
245             UpdateStatus();
246             // UpdateName();
247             UpdateArt();
248             UpdateMeta();
249             /* Update duration of file */
250         }
251         break;
252     case IMEvent::ItemStateChanged:
253         UpdateStatus();
254         break;
255     case IMEvent::NameChanged:
256         UpdateName();
257         break;
258     case IMEvent::MetaChanged:
259         UpdateMeta();
260         UpdateName(); /* Needed for NowPlaying */
261         UpdateArt(); /* Art is part of meta in the core */
262         break;
263     case IMEvent::InfoChanged:
264         UpdateInfo();
265         break;
266     case IMEvent::ItemTitleChanged:
267         UpdateNavigation();
268         UpdateName(); /* Display the name of the Chapter, if exists */
269         break;
270     case IMEvent::ItemRateChanged:
271         UpdateRate();
272         break;
273     case IMEvent::ItemEsChanged:
274         UpdateTeletext();
275         // We don't do anything ES related. Why ?
276         break;
277     case IMEvent::ItemTeletextChanged:
278         UpdateTeletext();
279         break;
280     case IMEvent::InterfaceVoutUpdate:
281         UpdateVout();
282         break;
283     case IMEvent::SynchroChanged:
284         emit synchroChanged();
285         break;
286     case IMEvent::CachingEvent:
287         UpdateCaching();
288         break;
289     case IMEvent::BookmarksChanged:
290         emit bookmarksChanged();
291         break;
292     case IMEvent::InterfaceAoutUpdate:
293         UpdateAout();
294         break;
295     case IMEvent::RecordingEvent:
296         UpdateRecord();
297         break;
298     case IMEvent::ProgramChanged:
299         UpdateProgramEvent();
300         break;
301     case IMEvent::EPGEvent:
302         UpdateEPG();
303         break;
304     default:
305         msg_Warn( p_intf, "This shouldn't happen: %i", i_type );
306         assert(0);
307     }
308 }
309
310 /* Add the callbacks on Input. Self explanatory */
311 inline void InputManager::addCallbacks()
312 {
313     var_AddCallback( p_input, "intf-event", InputEvent, this );
314 }
315
316 /* Delete the callbacks on Input. Self explanatory */
317 inline void InputManager::delCallbacks()
318 {
319     var_DelCallback( p_input, "intf-event", InputEvent, this );
320 }
321
322 /* Static callbacks for IM */
323 static int ItemChanged( vlc_object_t *p_this, const char *psz_var,
324                         vlc_value_t oldval, vlc_value_t newval, void *param )
325 {
326     VLC_UNUSED( p_this ); VLC_UNUSED( psz_var ); VLC_UNUSED( oldval );
327
328     InputManager *im = (InputManager*)param;
329     input_item_t *p_item = static_cast<input_item_t *>(newval.p_address);
330
331     IMEvent *event = new IMEvent( IMEvent::ItemChanged, p_item );
332     QApplication::postEvent( im, event );
333     return VLC_SUCCESS;
334 }
335
336 static int InputEvent( vlc_object_t *p_this, const char *,
337                        vlc_value_t, vlc_value_t newval, void *param )
338 {
339     VLC_UNUSED( p_this );
340
341     InputManager *im = (InputManager*)param;
342     IMEvent *event;
343
344     switch( newval.i_int )
345     {
346     case INPUT_EVENT_STATE:
347         event = new IMEvent( IMEvent::ItemStateChanged );
348         break;
349     case INPUT_EVENT_RATE:
350         event = new IMEvent( IMEvent::ItemRateChanged );
351         break;
352     case INPUT_EVENT_POSITION:
353     //case INPUT_EVENT_LENGTH:
354         event = new IMEvent( IMEvent::PositionUpdate );
355         break;
356
357     case INPUT_EVENT_TITLE:
358     case INPUT_EVENT_CHAPTER:
359         event = new IMEvent( IMEvent::ItemTitleChanged );
360         break;
361
362     case INPUT_EVENT_ES:
363         event = new IMEvent( IMEvent::ItemEsChanged );
364         break;
365     case INPUT_EVENT_TELETEXT:
366         event = new IMEvent( IMEvent::ItemTeletextChanged );
367         break;
368
369     case INPUT_EVENT_STATISTICS:
370         event = new IMEvent( IMEvent::StatisticsUpdate );
371         break;
372
373     case INPUT_EVENT_VOUT:
374         event = new IMEvent( IMEvent::InterfaceVoutUpdate );
375         break;
376     case INPUT_EVENT_AOUT:
377         event = new IMEvent( IMEvent::InterfaceAoutUpdate );
378         break;
379
380     case INPUT_EVENT_ITEM_META: /* Codec MetaData + Art */
381         event = new IMEvent( IMEvent::MetaChanged );
382         break;
383     case INPUT_EVENT_ITEM_INFO: /* Codec Info */
384         event = new IMEvent( IMEvent::InfoChanged );
385         break;
386     case INPUT_EVENT_ITEM_NAME:
387         event = new IMEvent( IMEvent::NameChanged );
388         break;
389
390     case INPUT_EVENT_AUDIO_DELAY:
391     case INPUT_EVENT_SUBTITLE_DELAY:
392         event = new IMEvent( IMEvent::SynchroChanged );
393         break;
394
395     case INPUT_EVENT_CACHE:
396         event = new IMEvent( IMEvent::CachingEvent );
397         break;
398
399     case INPUT_EVENT_BOOKMARK:
400         event = new IMEvent( IMEvent::BookmarksChanged );
401         break;
402
403     case INPUT_EVENT_RECORD:
404         event = new IMEvent( IMEvent::RecordingEvent );
405         break;
406
407     case INPUT_EVENT_PROGRAM:
408         /* This is for PID changes */
409         event = new IMEvent( IMEvent::ProgramChanged );
410         break;
411
412     case INPUT_EVENT_ITEM_EPG:
413         /* EPG data changed */
414         event = new IMEvent( IMEvent::EPGEvent );
415         break;
416
417     case INPUT_EVENT_SIGNAL:
418         /* This is for capture-card signals */
419         /* event = new IMEvent( SignalChanged_Type );
420         break; */
421     default:
422         event = NULL;
423         break;
424     }
425
426     if( event )
427         QApplication::postEvent( im, event );
428     return VLC_SUCCESS;
429 }
430
431 static int VbiEvent( vlc_object_t *, const char *,
432                      vlc_value_t, vlc_value_t, void *param )
433 {
434     InputManager *im = (InputManager*)param;
435     IMEvent *event = new IMEvent( IMEvent::ItemTeletextChanged );
436
437     QApplication::postEvent( im, event );
438     return VLC_SUCCESS;
439 }
440
441 void InputManager::UpdatePosition()
442 {
443     /* Update position */
444     int i_length;
445     int64_t i_time;
446     float f_pos;
447     i_length = var_GetTime(  p_input , "length" ) / CLOCK_FREQ;
448     i_time = var_GetTime(  p_input , "time");
449     f_pos = var_GetFloat(  p_input , "position" );
450     emit positionUpdated( f_pos, i_time, i_length );
451 }
452
453 void InputManager::UpdateNavigation()
454 {
455     /* Update navigation status */
456     vlc_value_t val; val.i_int = 0;
457     vlc_value_t val2; val2.i_int = 0;
458
459     if( hasInput() )
460         var_Change( p_input, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
461
462     if( val.i_int > 0 )
463     {
464         /* p_input != NULL since val.i_int != 0 */
465         var_Change( p_input, "chapter", VLC_VAR_CHOICESCOUNT, &val2, NULL );
466
467         emit titleChanged( val.i_int > 1 );
468         emit chapterChanged( val2.i_int > 1 );
469     }
470     else
471         emit chapterChanged( false );
472
473     if( hasInput() )
474         emit inputCanSeek( var_GetBool( p_input, "can-seek" ) );
475     else
476         emit inputCanSeek( false );
477 }
478
479 void InputManager::UpdateStatus()
480 {
481     /* Update playing status */
482     int state = var_GetInteger( p_input, "state" );
483     if( i_old_playing_status != state )
484     {
485         i_old_playing_status = state;
486         emit playingStatusChanged( state );
487     }
488 }
489
490 void InputManager::UpdateRate()
491 {
492     /* Update Rate */
493     float f_new_rate = var_GetFloat( p_input, "rate" );
494     if( f_new_rate != f_rate )
495     {
496         f_rate = f_new_rate;
497         /* Update rate */
498         emit rateChanged( f_rate );
499     }
500 }
501
502 void InputManager::UpdateName()
503 {
504     assert( p_input );
505
506     /* Update text, name and nowplaying */
507     QString name;
508
509     /* Try to get the nowplaying */
510     char *format = var_InheritString( p_intf, "input-title-format" );
511     char *formated = str_format_meta( p_input, format );
512     free( format );
513     name = qfu(formated);
514     free( formated );
515
516     /* If we have Nothing */
517     if( name.simplified().isEmpty() )
518     {
519         char *uri = input_item_GetURI( input_GetItem( p_input ) );
520         char *file = uri ? strrchr( uri, '/' ) : NULL;
521         if( file != NULL )
522         {
523             decode_URI( ++file );
524             name = qfu(file);
525         }
526         else
527             name = qfu(uri);
528         free( uri );
529     }
530
531     name = name.trimmed();
532
533     if( oldName != name )
534     {
535         emit nameChanged( name );
536         oldName = name;
537     }
538 }
539
540 int InputManager::playingStatus()
541 {
542     return i_old_playing_status;
543 }
544
545 bool InputManager::hasAudio()
546 {
547     if( hasInput() )
548     {
549         vlc_value_t val;
550         var_Change( p_input, "audio-es", VLC_VAR_CHOICESCOUNT, &val, NULL );
551         return val.i_int > 0;
552     }
553     return false;
554 }
555
556 bool InputManager::hasVisualisation()
557 {
558     if( !p_input )
559         return false;
560
561     audio_output_t *aout = input_GetAout( p_input );
562     if( !aout )
563         return false;
564
565     char *visual = var_InheritString( aout, "visual" );
566     vlc_object_release( aout );
567
568     if( !visual )
569         return false;
570
571     free( visual );
572     return true;
573 }
574
575 void InputManager::UpdateTeletext()
576 {
577     if( hasInput() )
578     {
579         const bool b_enabled = var_CountChoices( p_input, "teletext-es" ) > 0;
580         const int i_teletext_es = var_GetInteger( p_input, "teletext-es" );
581
582         /* Teletext is possible. Show the buttons */
583         emit teletextPossible( b_enabled );
584
585         /* If Teletext is selected */
586         if( b_enabled && i_teletext_es >= 0 )
587         {
588             /* Then, find the current page */
589             int i_page = 100;
590             bool b_transparent = false;
591
592             if( p_input_vbi )
593             {
594                 var_DelCallback( p_input_vbi, "vbi-page", VbiEvent, this );
595                 vlc_object_release( p_input_vbi );
596             }
597
598             if( input_GetEsObjects( p_input, i_teletext_es, &p_input_vbi, NULL, NULL ) )
599                 p_input_vbi = NULL;
600
601             if( p_input_vbi )
602             {
603                 /* This callback is not remove explicitly, but interfaces
604                  * are guaranted to outlive input */
605                 var_AddCallback( p_input_vbi, "vbi-page", VbiEvent, this );
606
607                 i_page = var_GetInteger( p_input_vbi, "vbi-page" );
608                 b_transparent = !var_GetBool( p_input_vbi, "vbi-opaque" );
609             }
610             emit newTelexPageSet( i_page );
611             emit teletextTransparencyActivated( b_transparent );
612
613         }
614         emit teletextActivated( b_enabled && i_teletext_es >= 0 );
615     }
616     else
617     {
618         emit teletextActivated( false );
619         emit teletextPossible( false );
620     }
621 }
622
623 void InputManager::UpdateEPG()
624 {
625     if( hasInput() )
626     {
627        emit epgChanged();
628     }
629 }
630
631 void InputManager::UpdateVout()
632 {
633     if( hasInput() )
634     {
635         /* Get current vout lists from input */
636         size_t i_vout;
637         vout_thread_t **pp_vout;
638         if( input_Control( p_input, INPUT_GET_VOUTS, &pp_vout, &i_vout ) )
639         {
640             i_vout = 0;
641             pp_vout = NULL;
642         }
643
644         /* */
645         emit voutListChanged( pp_vout, i_vout );
646
647         /* */
648         bool b_old_video = b_video;
649         b_video = i_vout > 0;
650         if( !!b_old_video != !!b_video )
651             emit voutChanged( b_video );
652
653         /* Release the vout list */
654         for( size_t i = 0; i < i_vout; i++ )
655             vlc_object_release( (vlc_object_t*)pp_vout[i] );
656         free( pp_vout );
657     }
658 }
659 void InputManager::UpdateAout()
660 {
661     if( hasInput() )
662     {
663         /* TODO */
664     }
665 }
666 void InputManager::UpdateCaching()
667 {
668     if(!hasInput()) return;
669
670     float f_newCache = var_GetFloat ( p_input, "cache" );
671     if( f_newCache != f_cache )
672     {
673         f_cache = f_newCache;
674         /* Update cache */
675         emit cachingChanged( f_cache );
676     }
677 }
678
679 void InputManager::requestArtUpdate( input_item_t *p_item, bool b_forced )
680 {
681     bool b_current_item = false;
682     if ( !p_item && hasInput() )
683     {   /* default to current item */
684         p_item = input_GetItem( p_input );
685         b_current_item = true;
686     }
687
688     if ( p_item )
689     {
690         /* check if it has already been enqueued */
691         if ( p_item->p_meta && !b_forced )
692         {
693             int status = vlc_meta_GetStatus( p_item->p_meta );
694             if ( status & ( ITEM_ART_NOTFOUND|ITEM_ART_FETCHED ) )
695                 return;
696         }
697         libvlc_ArtRequest( p_intf->p_libvlc, p_item,
698                            (b_forced) ? META_REQUEST_OPTION_SCOPE_ANY
699                                       : META_REQUEST_OPTION_NONE );
700         /* No input will signal the cover art to update,
701              * let's do it ourself */
702         if ( b_current_item )
703             UpdateArt();
704         else
705             emit artChanged( p_item );
706     }
707 }
708
709 const QString InputManager::decodeArtURL( input_item_t *p_item )
710 {
711     assert( p_item );
712
713     char *psz_art = input_item_GetArtURL( p_item );
714     if( psz_art )
715     {
716         char *psz = make_path( psz_art );
717         free( psz_art );
718         psz_art = psz;
719     }
720
721 #if 0
722     /* Taglib seems to define a attachment://, It won't work yet */
723     url = url.replace( "attachment://", "" );
724 #endif
725
726     QString path = qfu( psz_art ? psz_art : "" );
727     free( psz_art );
728     return path;
729 }
730
731 void InputManager::UpdateArt()
732 {
733     QString url;
734
735     if( hasInput() )
736         url = decodeArtURL( input_GetItem( p_input ) );
737
738     /* the art hasn't changed, no need to update */
739     if(artUrl == url)
740         return;
741
742     /* Update Art meta */
743     artUrl = url;
744     emit artChanged( artUrl );
745 }
746
747 void InputManager::setArt( input_item_t *p_item, QString fileUrl )
748 {
749     if( hasInput() )
750     {
751         char *psz_cachedir = config_GetUserDir( VLC_CACHE_DIR );
752         QString old_url = THEMIM->getIM()->decodeArtURL( p_item );
753         old_url = QDir( old_url ).canonicalPath();
754
755         if( old_url.startsWith( QString::fromUtf8( psz_cachedir ) ) )
756             QFile( old_url ).remove(); /* Purge cached artwork */
757
758         free( psz_cachedir );
759
760         input_item_SetArtURL( p_item , fileUrl.toUtf8().constData() );
761         UpdateArt();
762     }
763 }
764
765 inline void InputManager::UpdateStats()
766 {
767     assert( p_input );
768     emit statisticsUpdated( input_GetItem( p_input ) );
769 }
770
771 inline void InputManager::UpdateMeta( input_item_t *p_item_ )
772 {
773     emit metaChanged( p_item_ );
774     emit artChanged( p_item_ );
775 }
776
777 inline void InputManager::UpdateMeta()
778 {
779     assert( p_input );
780     emit currentMetaChanged( input_GetItem( p_input ) );
781 }
782
783 inline void InputManager::UpdateInfo()
784 {
785     assert( p_input );
786     emit infoChanged( input_GetItem( p_input ) );
787 }
788
789 void InputManager::UpdateRecord()
790 {
791     if( hasInput() )
792     {
793         emit recordingStateChanged( var_GetBool( p_input, "record" ) );
794     }
795 }
796
797 void InputManager::UpdateProgramEvent()
798 {
799     if( hasInput() )
800     {
801         bool b_scrambled = var_GetBool( p_input, "program-scrambled" );
802         emit encryptionChanged( b_scrambled );
803     }
804 }
805
806 /* User update of the slider */
807 void InputManager::sliderUpdate( float new_pos )
808 {
809     if( hasInput() )
810         var_SetFloat( p_input, "position", new_pos );
811     emit seekRequested( new_pos );
812 }
813
814 void InputManager::sectionPrev()
815 {
816     if( hasInput() )
817     {
818         int i_type = var_Type( p_input, "next-chapter" );
819         var_TriggerCallback( p_input, (i_type & VLC_VAR_TYPE) != 0 ?
820                              "prev-chapter":"prev-title" );
821     }
822 }
823
824 void InputManager::sectionNext()
825 {
826     if( hasInput() )
827     {
828         int i_type = var_Type( p_input, "next-chapter" );
829         var_TriggerCallback( p_input, (i_type & VLC_VAR_TYPE) != 0 ?
830                              "next-chapter":"next-title" );
831     }
832 }
833
834 void InputManager::sectionMenu()
835 {
836     if( hasInput() )
837     {
838         vlc_value_t val, text;
839
840         if( var_Change( p_input, "title  0", VLC_VAR_GETLIST, &val, &text ) < 0 )
841             return;
842
843         /* XXX is it "Root" or "Title" we want here ?" (set 0 by default) */
844         int root = 0;
845         for( int i = 0; i < val.p_list->i_count; i++ )
846         {
847             if( !strcmp( text.p_list->p_values[i].psz_string, "Title" ) )
848                 root = i;
849         }
850         var_FreeList( &val, &text );
851
852         var_SetInteger( p_input, "title  0", root );
853     }
854 }
855
856 /*
857  *  Teletext Functions
858  */
859
860 /* Set a new Teletext Page */
861 void InputManager::telexSetPage( int page )
862 {
863     if( hasInput() && p_input_vbi )
864     {
865         const int i_teletext_es = var_GetInteger( p_input, "teletext-es" );
866
867         if( i_teletext_es >= 0 )
868         {
869             var_SetInteger( p_input_vbi, "vbi-page", page );
870             emit newTelexPageSet( page );
871         }
872     }
873 }
874
875 /* Set the transparency on teletext */
876 void InputManager::telexSetTransparency( bool b_transparentTelextext )
877 {
878     if( hasInput() && p_input_vbi )
879     {
880         var_SetBool( p_input_vbi, "vbi-opaque", !b_transparentTelextext );
881         emit teletextTransparencyActivated( b_transparentTelextext );
882     }
883 }
884
885 void InputManager::activateTeletext( bool b_enable )
886 {
887     vlc_value_t list;
888     vlc_value_t text;
889     if( hasInput() && !var_Change( p_input, "teletext-es", VLC_VAR_GETLIST, &list, &text ) )
890     {
891         if( list.p_list->i_count > 0 )
892         {
893             /* Prefer the page 100 if it is present */
894             int i;
895             for( i = 0; i < text.p_list->i_count; i++ )
896             {
897                 /* The description is the page number as a string */
898                 const char *psz_page = text.p_list->p_values[i].psz_string;
899                 if( psz_page && !strcmp( psz_page, "100" ) )
900                     break;
901             }
902             if( i >= list.p_list->i_count )
903                 i = 0;
904             var_SetInteger( p_input, "spu-es", b_enable ? list.p_list->p_values[i].i_int : -1 );
905         }
906         var_FreeList( &list, &text );
907     }
908 }
909
910 void InputManager::reverse()
911 {
912     if( hasInput() )
913     {
914         float f_rate_ = var_GetFloat( p_input, "rate" );
915         var_SetFloat( p_input, "rate", -f_rate_ );
916     }
917 }
918
919 void InputManager::slower()
920 {
921     var_TriggerCallback( THEPL, "rate-slower" );
922 }
923
924 void InputManager::faster()
925 {
926     var_TriggerCallback( THEPL, "rate-faster" );
927 }
928
929 void InputManager::littlefaster()
930 {
931     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_RATE_FASTER_FINE );
932 }
933
934 void InputManager::littleslower()
935 {
936     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_RATE_SLOWER_FINE );
937 }
938
939 void InputManager::normalRate()
940 {
941     var_SetFloat( THEPL, "rate", 1. );
942 }
943
944 void InputManager::setRate( int new_rate )
945 {
946     var_SetFloat( THEPL, "rate",
947                  (float)INPUT_RATE_DEFAULT / (float)new_rate );
948 }
949
950 void InputManager::jumpFwd()
951 {
952     int i_interval = var_InheritInteger( p_input, "short-jump-size" );
953     if( i_interval > 0 && hasInput() )
954     {
955         mtime_t val = CLOCK_FREQ * i_interval;
956         var_SetTime( p_input, "time-offset", val );
957     }
958 }
959
960 void InputManager::jumpBwd()
961 {
962     int i_interval = var_InheritInteger( p_input, "short-jump-size" );
963     if( i_interval > 0 && hasInput() )
964     {
965         mtime_t val = -CLOCK_FREQ * i_interval;
966         var_SetTime( p_input, "time-offset", val );
967     }
968 }
969
970 void InputManager::setAtoB()
971 {
972     if( !timeA )
973     {
974         timeA = var_GetTime( THEMIM->getInput(), "time"  );
975     }
976     else if( !timeB )
977     {
978         timeB = var_GetTime( THEMIM->getInput(), "time"  );
979         var_SetTime( THEMIM->getInput(), "time" , timeA );
980         CONNECT( this, positionUpdated( float, int64_t, int ),
981                  this, AtoBLoop( float, int64_t, int ) );
982     }
983     else
984     {
985         timeA = 0;
986         timeB = 0;
987         disconnect( this, SIGNAL( positionUpdated( float, int64_t, int ) ),
988                     this, SLOT( AtoBLoop( float, int64_t, int ) ) );
989     }
990     emit AtoBchanged( (timeA != 0 ), (timeB != 0 ) );
991 }
992
993 /* Function called regularly when in an AtoB loop */
994 void InputManager::AtoBLoop( float, int64_t i_time, int )
995 {
996     if( timeB && i_time >= timeB )
997         var_SetTime( THEMIM->getInput(), "time" , timeA );
998 }
999
1000 /**********************************************************************
1001  * MainInputManager implementation. Wrap an input manager and
1002  * take care of updating the main playlist input.
1003  * Used in the main playlist Dialog
1004  **********************************************************************/
1005
1006 MainInputManager::MainInputManager( intf_thread_t *_p_intf )
1007     : QObject(NULL), p_intf( _p_intf ),
1008       random( VLC_OBJECT(THEPL), "random" ),
1009       repeat( VLC_OBJECT(THEPL), "repeat" ), loop( VLC_OBJECT(THEPL), "loop" ),
1010       volume( VLC_OBJECT(THEPL), "volume" ), mute( VLC_OBJECT(THEPL), "mute" )
1011 {
1012     p_input = NULL;
1013     im = new InputManager( this, p_intf );
1014
1015     var_AddCallback( THEPL, "item-change", ItemChanged, im );
1016     var_AddCallback( THEPL, "activity", PLItemChanged, this );
1017     var_AddCallback( THEPL, "leaf-to-parent", LeafToParent, this );
1018     var_AddCallback( THEPL, "playlist-item-append", PLItemAppended, this );
1019     var_AddCallback( THEPL, "playlist-item-deleted", PLItemRemoved, this );
1020     random.addCallback( this, SLOT(notifyRandom(bool)) );
1021     repeat.addCallback( this, SLOT(notifyRepeatLoop(bool)) );
1022     loop.addCallback( this, SLOT(notifyRepeatLoop(bool)) );
1023
1024     volume.addCallback( this, SLOT(notifyVolume(float)) );
1025     mute.addCallback( this, SLOT(notifyMute(bool)) );
1026
1027     /* Warn our embedded IM about input changes */
1028     DCONNECT( this, inputChanged(),
1029               im, inputChangedHandler() );
1030
1031     /* initialize p_input (an input can already be running) */
1032     p_input = playlist_CurrentInput( THEPL );
1033     if( p_input )
1034         emit inputChanged( );
1035
1036     /* Audio Menu */
1037     menusAudioMapper = new QSignalMapper();
1038     CONNECT( menusAudioMapper, mapped(QString), this, menusUpdateAudio( QString ) );
1039 }
1040
1041 MainInputManager::~MainInputManager()
1042 {
1043     if( p_input )
1044     {
1045        vlc_object_release( p_input );
1046        p_input = NULL;
1047        emit inputChanged( );
1048     }
1049
1050     var_DelCallback( THEPL, "activity", PLItemChanged, this );
1051     var_DelCallback( THEPL, "item-change", ItemChanged, im );
1052     var_DelCallback( THEPL, "leaf-to-parent", LeafToParent, this );
1053
1054     var_DelCallback( THEPL, "playlist-item-append", PLItemAppended, this );
1055     var_DelCallback( THEPL, "playlist-item-deleted", PLItemRemoved, this );
1056
1057     delete menusAudioMapper;
1058 }
1059
1060 vout_thread_t* MainInputManager::getVout()
1061 {
1062     return p_input ? input_GetVout( p_input ) : NULL;
1063 }
1064
1065 audio_output_t * MainInputManager::getAout()
1066 {
1067     return playlist_GetAout( THEPL );
1068 }
1069
1070 void MainInputManager::customEvent( QEvent *event )
1071 {
1072     int type = event->type();
1073
1074     PLEvent *plEv;
1075
1076     // msg_Dbg( p_intf, "New MainIM Event of type: %i", type );
1077     switch( type )
1078     {
1079     case PLEvent::PLItemAppended:
1080         plEv = static_cast<PLEvent*>( event );
1081         emit playlistItemAppended( plEv->getItemId(), plEv->getParentId() );
1082         return;
1083     case PLEvent::PLItemRemoved:
1084         plEv = static_cast<PLEvent*>( event );
1085         emit playlistItemRemoved( plEv->getItemId() );
1086         return;
1087     case PLEvent::PLEmpty:
1088         plEv = static_cast<PLEvent*>( event );
1089         emit playlistNotEmpty( plEv->getItemId() >= 0 );
1090         return;
1091     case PLEvent::LeafToParent:
1092         plEv = static_cast<PLEvent*>( event );
1093         emit leafBecameParent( plEv->getItemId() );
1094         return;
1095     default:
1096         if( type != IMEvent::ItemChanged ) return;
1097     }
1098
1099     if( p_input != NULL )
1100         vlc_object_release( p_input );
1101     p_input = playlist_CurrentInput( THEPL );
1102     emit inputChanged( );
1103 }
1104
1105 /* Playlist Control functions */
1106 void MainInputManager::stop()
1107 {
1108    playlist_Stop( THEPL );
1109 }
1110
1111 void MainInputManager::next()
1112 {
1113    playlist_Next( THEPL );
1114 }
1115
1116 void MainInputManager::prev()
1117 {
1118    playlist_Prev( THEPL );
1119 }
1120
1121 void MainInputManager::prevOrReset()
1122 {
1123     if( !p_input || var_GetTime(  p_input , "time") < 10000 )
1124         playlist_Prev( THEPL );
1125     else
1126         getIM()->sliderUpdate( 0.0 );
1127 }
1128
1129 void MainInputManager::togglePlayPause()
1130 {
1131     /* No input, play */
1132     if( !p_input )
1133         playlist_Play( THEPL );
1134     else
1135         playlist_Pause( THEPL );
1136 }
1137
1138 void MainInputManager::play()
1139 {
1140     /* No input, play */
1141     if( !p_input )
1142         playlist_Play( THEPL );
1143     else
1144     {
1145         if( PLAYING_S != var_GetInteger( p_input, "state" ) )
1146         {
1147             playlist_Pause( THEPL );
1148         }
1149     }
1150 }
1151
1152 void MainInputManager::pause()
1153 {
1154     if(p_input && PLAYING_S == var_GetInteger( p_input, "state" ) )
1155     {
1156         playlist_Pause( THEPL );
1157     }
1158 }
1159
1160 void MainInputManager::toggleRandom()
1161 {
1162     config_PutInt( p_intf, "random", var_ToggleBool( THEPL, "random" ) );
1163 }
1164
1165 void MainInputManager::notifyRandom(bool value)
1166 {
1167     emit randomChanged(value);
1168 }
1169
1170 void MainInputManager::notifyRepeatLoop(bool)
1171 {
1172     int i_value = var_GetBool( THEPL, "loop" ) * REPEAT_ALL
1173               + var_GetBool( THEPL, "repeat" ) * REPEAT_ONE;
1174
1175     emit repeatLoopChanged( i_value );
1176 }
1177
1178 void MainInputManager::loopRepeatLoopStatus()
1179 {
1180     /* Toggle Normal -> Loop -> Repeat -> Normal ... */
1181     bool loop = var_GetBool( THEPL, "loop" );
1182     bool repeat = var_GetBool( THEPL, "repeat" );
1183
1184     if( repeat )
1185     {
1186         loop = false;
1187         repeat = false;
1188     }
1189     else if( loop )
1190     {
1191         loop = false;
1192         repeat = true;
1193     }
1194     else
1195     {
1196         loop = true;
1197         //repeat = false;
1198     }
1199
1200     var_SetBool( THEPL, "loop", loop );
1201     var_SetBool( THEPL, "repeat", repeat );
1202     config_PutInt( p_intf, "loop", loop );
1203     config_PutInt( p_intf, "repeat", repeat );
1204 }
1205
1206 void MainInputManager::activatePlayQuit( bool b_exit )
1207 {
1208     var_SetBool( THEPL, "play-and-exit", b_exit );
1209     config_PutInt( p_intf, "play-and-exit", b_exit );
1210 }
1211
1212 bool MainInputManager::getPlayExitState()
1213 {
1214     return var_InheritBool( THEPL, "play-and-exit" );
1215 }
1216
1217 bool MainInputManager::hasEmptyPlaylist()
1218 {
1219     playlist_Lock( THEPL );
1220     bool b_empty = playlist_IsEmpty( THEPL );
1221     playlist_Unlock( THEPL );
1222     return b_empty;
1223 }
1224
1225 /****************************
1226  * Static callbacks for MIM *
1227  ****************************/
1228 static int PLItemChanged( vlc_object_t *p_this, const char *psz_var,
1229                         vlc_value_t oldval, vlc_value_t val, void *param )
1230 {
1231     VLC_UNUSED( p_this ); VLC_UNUSED( psz_var ); VLC_UNUSED( oldval );
1232     VLC_UNUSED( val );
1233
1234     MainInputManager *mim = (MainInputManager*)param;
1235
1236     IMEvent *event = new IMEvent( IMEvent::ItemChanged );
1237     QApplication::postEvent( mim, event );
1238     return VLC_SUCCESS;
1239 }
1240
1241 static int LeafToParent( vlc_object_t *p_this, const char *psz_var,
1242                         vlc_value_t oldval, vlc_value_t newval, void *param )
1243 {
1244     VLC_UNUSED( p_this ); VLC_UNUSED( psz_var ); VLC_UNUSED( oldval );
1245     MainInputManager *mim = (MainInputManager*)param;
1246
1247     PLEvent *event = new PLEvent( PLEvent::LeafToParent, newval.i_int );
1248
1249     QApplication::postEvent( mim, event );
1250     return VLC_SUCCESS;
1251 }
1252
1253 void MainInputManager::notifyVolume( float volume )
1254 {
1255     emit volumeChanged( volume );
1256 }
1257
1258 void MainInputManager::notifyMute( bool mute )
1259 {
1260     emit soundMuteChanged(mute);
1261 }
1262
1263
1264 void MainInputManager::menusUpdateAudio( const QString& data )
1265 {
1266     audio_output_t *aout = getAout();
1267     if( aout != NULL )
1268     {
1269         aout_DeviceSet( aout, qtu(data) );
1270         vlc_object_release( aout );
1271     }
1272 }
1273
1274 static int PLItemAppended
1275 ( vlc_object_t * obj, const char *var, vlc_value_t old, vlc_value_t cur, void *data )
1276 {
1277     VLC_UNUSED( obj ); VLC_UNUSED( var ); VLC_UNUSED( old );
1278     MainInputManager *mim = static_cast<MainInputManager*>(data);
1279     playlist_add_t *p_add = static_cast<playlist_add_t*>( cur.p_address );
1280
1281     PLEvent *event = new PLEvent( PLEvent::PLItemAppended, p_add->i_item, p_add->i_node  );
1282     QApplication::postEvent( mim, event );
1283     event = new PLEvent( PLEvent::PLEmpty, p_add->i_item, 0  );
1284     QApplication::postEvent( mim, event );
1285     return VLC_SUCCESS;
1286 }
1287
1288 static int PLItemRemoved
1289 ( vlc_object_t * obj, const char *var, vlc_value_t old, vlc_value_t cur, void *data )
1290 {
1291     VLC_UNUSED( var ); VLC_UNUSED( old );
1292
1293     playlist_t *pl = (playlist_t *) obj;
1294     MainInputManager *mim = static_cast<MainInputManager*>(data);
1295
1296     PLEvent *event = new PLEvent( PLEvent::PLItemRemoved, cur.i_int, 0  );
1297     QApplication::postEvent( mim, event );
1298     // can't use playlist_IsEmpty(  ) as it isn't true yet
1299     if ( pl->items.i_size == 1 ) // lock is held
1300     {
1301         event = new PLEvent( PLEvent::PLEmpty, -1, 0 );
1302         QApplication::postEvent( mim, event );
1303     }
1304     return VLC_SUCCESS;
1305 }