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