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