]> git.sesse.net Git - vlc/blob - modules/gui/qt4/main_interface.cpp
a308ff4c73d8004093c561cfcf9552a1bdbbb4be
[vlc] / modules / gui / qt4 / main_interface.cpp
1 /*****************************************************************************
2  * main_interface.cpp : Main interface
3  ****************************************************************************
4  * Copyright (C) 2006-2010 VideoLAN and AUTHORS
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *          Jean-Baptiste Kempf <jb@videolan.org>
9  *          Ilkka Ollakka <ileoo@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 Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include "qt4.hpp"
31
32 #include "main_interface.hpp"
33 #include "input_manager.hpp"                    // Creation
34 #include "actions_manager.hpp"                  // killInstance
35 #include "extensions_manager.hpp"               // killInstance
36
37 #include "util/customwidgets.hpp"               // qtEventToVLCKey, QVLCStackedWidget
38 #include "util/qt_dirs.hpp"                     // toNativeSeparators
39
40 #include "components/interface_widgets.hpp"     // bgWidget, videoWidget
41 #include "components/controller.hpp"            // controllers
42 #include "components/playlist/playlist.hpp"     // plWidget
43 #include "dialogs/firstrun.hpp"                 // First Run
44
45 #include "menus.hpp"                            // Menu creation
46 #include "recents.hpp"                          // RecentItems when DnD
47
48 #include <QCloseEvent>
49 #include <QKeyEvent>
50
51 #include <QUrl>
52 #include <QSize>
53 #include <QDate>
54
55 #include <QMenu>
56 #include <QMenuBar>
57 #include <QStatusBar>
58 #include <QLabel>
59 #include <QStackedWidget>
60
61 #include <vlc_keys.h>                       /* Wheel event */
62 #include <vlc_vout_display.h>               /* vout_thread_t and VOUT_ events */
63
64 // #define DEBUG_INTF
65
66 /* Callback prototypes */
67 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
68                         vlc_value_t old_val, vlc_value_t new_val, void *param );
69 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
70                        vlc_value_t old_val, vlc_value_t new_val, void *param );
71
72 MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
73 {
74     /* Variables initialisation */
75     bgWidget             = NULL;
76     videoWidget          = NULL;
77     playlistWidget       = NULL;
78     stackCentralOldWidget= NULL;
79 #ifndef HAVE_MAEMO
80     sysTray              = NULL;
81 #endif
82     fullscreenControls   = NULL;
83     cryptedLabel         = NULL;
84     controls             = NULL;
85     inputC               = NULL;
86
87     b_hideAfterCreation  = false; // --qt-start-minimized
88     playlistVisible      = false;
89     input_name           = "";
90
91
92     /* Ask for Privacy */
93     FirstRun::CheckAndRun( this, p_intf );
94
95     /**
96      *  Configuration and settings
97      *  Pre-building of interface
98      **/
99     /* Main settings */
100     setFocusPolicy( Qt::StrongFocus );
101     setAcceptDrops( true );
102     setWindowRole( "vlc-main" );
103     setWindowIcon( QApplication::windowIcon() );
104     setWindowOpacity( var_InheritFloat( p_intf, "qt-opacity" ) );
105 #ifdef Q_WS_MAC
106     setAttribute( Qt::WA_MacBrushedMetal );
107 #endif
108
109     /* Is video in embedded in the UI or not */
110     b_videoEmbedded = var_InheritBool( p_intf, "embedded-video" );
111
112     /* Does the interface resize to video size or the opposite */
113     b_autoresize = var_InheritBool( p_intf, "qt-video-autoresize" );
114
115     /* Are we in the enhanced always-video mode or not ? */
116     b_minimalView = var_InheritBool( p_intf, "qt-minimal-view" );
117
118     /* Do we want anoying popups or not */
119     b_notificationEnabled = var_InheritBool( p_intf, "qt-notification" );
120
121     /* Set the other interface settings */
122     settings = getSettings();
123     settings->beginGroup( "MainWindow" );
124
125     /* */
126     b_plDocked = getSettings()->value( "pl-dock-status", true ).toBool();
127
128     settings->endGroup( );
129
130     /**************
131      * Status Bar *
132      **************/
133     createStatusBar();
134
135     /**************************
136      *  UI and Widgets design
137      **************************/
138     setVLCWindowsTitle();
139
140     /************
141      * Menu Bar *
142      ************/
143     QVLCMenu::createMenuBar( this, p_intf );
144     CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ),
145              this, destroyPopupMenu() );
146
147     createMainWidget( settings );
148     /*********************************
149      * Create the Systray Management *
150      *********************************/
151     initSystray();
152
153     /********************
154      * Input Manager    *
155      ********************/
156     MainInputManager::getInstance( p_intf );
157
158 #ifdef WIN32
159     himl = NULL;
160     p_taskbl = NULL;
161     taskbar_wmsg = RegisterWindowMessage("TaskbarButtonCreated");
162 #endif
163
164     /************************************************************
165      * Connect the input manager to the GUI elements it manages *
166      ************************************************************/
167     /**
168      * Connects on nameChanged()
169      * Those connects are different because options can impeach them to trigger.
170      **/
171     /* Main Interface statusbar */
172     CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
173              this, setName( const QString& ) );
174     /* and systray */
175 #ifndef HAVE_MAEMO
176     if( sysTray )
177     {
178         CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
179                  this, updateSystrayTooltipName( const QString& ) );
180     }
181 #endif
182     /* and title of the Main Interface*/
183     if( var_InheritBool( p_intf, "qt-name-in-title" ) )
184     {
185         CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
186                  this, setVLCWindowsTitle( const QString& ) );
187     }
188
189     /**
190      * CONNECTS on PLAY_STATUS
191      **/
192     /* Status on the systray */
193 #ifndef HAVE_MAEMO
194     if( sysTray )
195     {
196         CONNECT( THEMIM->getIM(), statusChanged( int ),
197                  this, updateSystrayTooltipStatus( int ) );
198     }
199 #endif
200
201     /* END CONNECTS ON IM */
202
203     /* VideoWidget connects for asynchronous calls */
204     b_videoFullScreen = false;
205     b_videoOnTop = false;
206     connect( this, SIGNAL(askGetVideo(WId*,int*,int*,unsigned*,unsigned *)),
207              this, SLOT(getVideoSlot(WId*,int*,int*,unsigned*,unsigned*)),
208              Qt::BlockingQueuedConnection );
209     connect( this, SIGNAL(askReleaseVideo( void )),
210              this, SLOT(releaseVideoSlot( void )),
211              Qt::BlockingQueuedConnection );
212     CONNECT( this, askVideoOnTop(bool), this, setVideoOnTop(bool));
213
214     if( videoWidget )
215     {
216         if( b_autoresize )
217         {
218             CONNECT( this, askVideoToResize( unsigned int, unsigned int ),
219                      this, setVideoSize( unsigned int, unsigned int ) );
220             CONNECT( videoWidget, sizeChanged( int, int ),
221                      this, resizeStack( int,  int ) );
222         }
223         CONNECT( this, askVideoSetFullScreen( bool ),
224                  this, setVideoFullScreen( bool ) );
225     }
226
227     CONNECT( THEDP, toolBarConfUpdated(), this, recreateToolbars() );
228
229     /** END of CONNECTS**/
230
231
232     /************
233      * Callbacks
234      ************/
235     var_AddCallback( p_intf->p_libvlc, "intf-show", IntfShowCB, p_intf );
236
237     /* Register callback for the intf-popupmenu variable */
238     var_AddCallback( p_intf->p_libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
239
240     /* Playlist */
241     int i_plVis = settings->value( "MainWindow/playlist-visible", false ).toBool();
242
243     if( i_plVis ) togglePlaylist();
244
245     /**** FINAL SIZING and placement of interface */
246     settings->beginGroup( "MainWindow" );
247     QVLCTools::restoreWidgetPosition( settings, this, QSize(400, 100) );
248     settings->endGroup();
249
250     b_interfaceFullScreen = isFullScreen();
251
252     /* Final sizing and showing */
253     setVisible( !b_hideAfterCreation );
254
255     setMinimumWidth( __MAX( controls->sizeHint().width(),
256                             menuBar()->sizeHint().width() ) + 30 );
257
258     /* Switch to minimal view if needed, must be called after the show() */
259     if( b_minimalView )
260         toggleMinimalView( true );
261 }
262
263 MainInterface::~MainInterface()
264 {
265     /* Unsure we hide the videoWidget before destroying it */
266     if( stackCentralOldWidget == videoWidget )
267         showTab( bgWidget );
268
269 #ifdef WIN32
270     if( himl )
271         ImageList_Destroy( himl );
272     if(p_taskbl)
273         p_taskbl->vt->Release(p_taskbl);
274     CoUninitialize();
275 #endif
276
277     /* Be sure to kill the actionsManager... Only used in the MI and control */
278     ActionsManager::killInstance();
279
280     /* Idem */
281     ExtensionsManager::killInstance();
282
283     /* Delete the FSC controller */
284     delete fullscreenControls;
285
286     /* Save states */
287     settings->beginGroup( "MainWindow" );
288
289     settings->setValue( "pl-dock-status", b_plDocked );
290     /* Save playlist state */
291     if( playlistWidget )
292         settings->setValue( "playlist-visible", playlistVisible );
293
294     settings->setValue( "adv-controls",
295                         getControlsVisibilityStatus() & CONTROLS_ADVANCED );
296
297     /* Save the stackCentralW sizes */
298     settings->setValue( "bgSize", stackWidgetsSizes[bgWidget] );
299     settings->setValue( "playlistSize", stackWidgetsSizes[playlistWidget] );
300
301     /* Save this size */
302     QVLCTools::saveWidgetPosition(settings, this);
303
304     settings->endGroup();
305
306     /* Save undocked playlist size */
307     if( playlistWidget && !isPlDocked() )
308         QVLCTools::saveWidgetPosition( p_intf, "Playlist", playlistWidget );
309
310     delete playlistWidget;
311
312     delete statusBar();
313
314     /* Unregister callbacks */
315     var_DelCallback( p_intf->p_libvlc, "intf-show", IntfShowCB, p_intf );
316     var_DelCallback( p_intf->p_libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
317
318     p_intf->p_sys->p_mi = NULL;
319 }
320
321 /*****************************
322  *   Main UI handling        *
323  *****************************/
324 void MainInterface::recreateToolbars()
325 {
326     bool b_adv = getControlsVisibilityStatus() & CONTROLS_ADVANCED;
327
328     settings->beginGroup( "MainWindow" );
329     delete controls;
330     delete inputC;
331
332     controls = new ControlsWidget( p_intf, b_adv, this );
333     inputC = new InputControlsWidget( p_intf, this );
334
335     if( fullscreenControls )
336     {
337         delete fullscreenControls;
338         fullscreenControls = new FullscreenControllerWidget( p_intf, this );
339         CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
340                  this, handleKeyPress( QKeyEvent * ) );
341     }
342     mainLayout->insertWidget( 2, inputC );
343     mainLayout->insertWidget( settings->value( "ToolbarPos", 0 ).toInt() ? 0: 3,
344                               controls );
345     settings->endGroup();
346 }
347
348 void MainInterface::createMainWidget( QSettings *settings )
349 {
350     /* Create the main Widget and the mainLayout */
351     QWidget *main = new QWidget;
352     setCentralWidget( main );
353     mainLayout = new QVBoxLayout( main );
354     main->setContentsMargins( 0, 0, 0, 0 );
355     mainLayout->setSpacing( 0 ); mainLayout->setMargin( 0 );
356
357     /* */
358     stackCentralW = new QVLCStackedWidget( main );
359
360     /* Bg Cone */
361     bgWidget = new BackgroundWidget( p_intf );
362     stackCentralW->addWidget( bgWidget );
363
364     /* And video Outputs */
365     if( b_videoEmbedded )
366     {
367         videoWidget = new VideoWidget( p_intf );
368         stackCentralW->addWidget( videoWidget );
369     }
370     mainLayout->insertWidget( 1, stackCentralW );
371
372     settings->beginGroup( "MainWindow" );
373     stackWidgetsSizes[bgWidget] = settings->value( "bgSize", QSize( 400, 0 ) ).toSize();
374     /* Resize even if no-auto-resize, because we are at creation */
375     resizeStack( stackWidgetsSizes[bgWidget].width(), stackWidgetsSizes[bgWidget].height() );
376
377     /* Create the CONTROLS Widget */
378     controls = new ControlsWidget( p_intf,
379                    settings->value( "adv-controls", false ).toBool(), this );
380     inputC = new InputControlsWidget( p_intf, this );
381
382     mainLayout->insertWidget( 2, inputC );
383     mainLayout->insertWidget( settings->value( "ToolbarPos", 0 ).toInt() ? 0: 3,
384                               controls );
385
386     /* Visualisation, disabled for now, they SUCK */
387     #if 0
388     visualSelector = new VisualSelector( p_intf );
389     mainLayout->insertWidget( 0, visualSelector );
390     visualSelector->hide();
391     #endif
392
393     settings->endGroup();
394
395     /* Enable the popup menu in the MI */
396     main->setContextMenuPolicy( Qt::CustomContextMenu );
397     CONNECT( main, customContextMenuRequested( const QPoint& ),
398              this, popupMenu( const QPoint& ) );
399
400     if ( depth() > 8 ) /* 8bit depth has too many issues with opacity */
401         /* Create the FULLSCREEN CONTROLS Widget */
402         if( var_InheritBool( p_intf, "qt-fs-controller" ) )
403         {
404             fullscreenControls = new FullscreenControllerWidget( p_intf, this );
405             CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
406                      this, handleKeyPress( QKeyEvent * ) );
407         }
408 }
409
410 inline void MainInterface::initSystray()
411 {
412 #ifndef HAVE_MAEMO
413     bool b_systrayAvailable = QSystemTrayIcon::isSystemTrayAvailable();
414     bool b_systrayWanted = var_InheritBool( p_intf, "qt-system-tray" );
415
416     if( var_InheritBool( p_intf, "qt-start-minimized") )
417     {
418         if( b_systrayAvailable )
419         {
420             b_systrayWanted = true;
421             b_hideAfterCreation = true;
422         }
423         else
424             msg_Err( p_intf, "cannot start minimized without system tray bar" );
425     }
426
427     if( b_systrayAvailable && b_systrayWanted )
428         createSystray();
429 #endif
430 }
431
432 inline void MainInterface::createStatusBar()
433 {
434     /****************
435      *  Status Bar  *
436      ****************/
437     /* Widgets Creation*/
438     QStatusBar *statusBarr = statusBar();
439
440     TimeLabel *timeLabel = new TimeLabel( p_intf );
441     nameLabel = new QLabel( this );
442     nameLabel->setTextInteractionFlags( Qt::TextSelectableByMouse
443                                       | Qt::TextSelectableByKeyboard );
444     SpeedLabel *speedLabel = new SpeedLabel( p_intf, this );
445
446     /* Styling those labels */
447     timeLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
448     speedLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
449     nameLabel->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel);
450     timeLabel->setStyleSheet(
451             "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
452     speedLabel->setStyleSheet(
453             "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
454
455     /* and adding those */
456     statusBarr->addWidget( nameLabel, 8 );
457     statusBarr->addPermanentWidget( speedLabel, 0 );
458     statusBarr->addPermanentWidget( timeLabel, 0 );
459
460     /* timeLabel behaviour:
461        - double clicking opens the goto time dialog
462        - right-clicking and clicking just toggle between remaining and
463          elapsed time.*/
464     CONNECT( timeLabel, timeLabelDoubleClicked(), THEDP, gotoTimeDialog() );
465
466     CONNECT( THEMIM->getIM(), encryptionChanged( bool ),
467              this, showCryptedLabel( bool ) );
468
469     CONNECT( THEMIM->getIM(), seekRequested( float ),
470              timeLabel, setDisplayPosition( float ) );
471
472     /* This shouldn't be necessary, but for somehow reason, the statusBarr
473        starts at height of 20px and when a text is shown it needs more space.
474        But, as the QMainWindow policy doesn't allow statusBar to change QMW's
475        geometry, we need to force a height. If you have a better idea, please
476        tell me -- jb
477      */
478     statusBarr->setFixedHeight( statusBarr->sizeHint().height() + 2 );
479 }
480
481 /**********************************************************************
482  * Handling of sizing of the components
483  **********************************************************************/
484
485 void MainInterface::debug()
486 {
487 #ifdef DEBUG_INTF
488     msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
489     msg_Dbg( p_intf, "sizeHint: %i - %i", sizeHint().height(), sizeHint().width() );
490     msg_Dbg( p_intf, "minimumsize: %i - %i", minimumSize().height(), minimumSize().width() );
491
492     msg_Dbg( p_intf, "Stack size: %i - %i", stackCentralW->size().height(), stackCentralW->size().width() );
493     msg_Dbg( p_intf, "Stack sizeHint: %i - %i", stackCentralW->sizeHint().height(), stackCentralW->sizeHint().width() );
494     msg_Dbg( p_intf, "Central size: %i - %i", centralWidget()->size().height(), centralWidget()->size().width() );
495 #endif
496 }
497
498 inline void MainInterface::showVideo() { showTab( videoWidget ); }
499 inline void MainInterface::restoreStackOldWidget()
500             { showTab( stackCentralOldWidget ); }
501
502 inline void MainInterface::showTab( QWidget *widget )
503 {
504 #ifdef DEBUG_INTF
505     msg_Warn( p_intf, "Old stackCentralOldWidget %i", stackCentralW->indexOf( stackCentralOldWidget ) );
506 #endif
507
508     stackCentralOldWidget = stackCentralW->currentWidget();
509     stackWidgetsSizes[stackCentralOldWidget] = stackCentralW->size();
510
511     stackCentralW->setCurrentWidget( widget );
512     if( b_autoresize )
513         resizeStack( stackWidgetsSizes[widget].width(), stackWidgetsSizes[widget].height() );
514
515 #ifdef DEBUG_INTF
516     msg_Warn( p_intf, "State change %i",  stackCentralW->currentIndex() );
517     msg_Warn( p_intf, "New stackCentralOldWidget %i", stackCentralW->indexOf( stackCentralOldWidget ) );
518 #endif
519 }
520
521 void MainInterface::destroyPopupMenu()
522 {
523     QVLCMenu::PopupMenu( p_intf, false );
524 }
525
526 void MainInterface::popupMenu( const QPoint &p )
527 {
528     QVLCMenu::PopupMenu( p_intf, true );
529 }
530
531 void MainInterface::toggleFSC()
532 {
533    if( !fullscreenControls ) return;
534
535    IMEvent *eShow = new IMEvent( FullscreenControlToggle_Type, 0 );
536    QApplication::postEvent( fullscreenControls, eShow );
537 }
538
539 /****************************************************************************
540  * Video Handling
541  ****************************************************************************/
542
543 /**
544  * NOTE:
545  * You must not change the state of this object or other Qt4 UI objects,
546  * from the video output thread - only from the Qt4 UI main loop thread.
547  * All window provider queries must be handled through signals or events.
548  * That's why we have all those emit statements...
549  */
550 WId MainInterface::getVideo( int *pi_x, int *pi_y,
551                              unsigned int *pi_width, unsigned int *pi_height )
552 {
553     if( !videoWidget )
554         return 0;
555
556     /* This is a blocking call signal. Results are returned through pointers.
557      * Beware of deadlocks! */
558     WId id;
559     emit askGetVideo( &id, pi_x, pi_y, pi_width, pi_height );
560     return id;
561 }
562
563 void MainInterface::getVideoSlot( WId *p_id, int *pi_x, int *pi_y,
564                                   unsigned *pi_width, unsigned *pi_height )
565 {
566     /* Request the videoWidget */
567     WId ret = videoWidget->request( pi_x, pi_y,
568                                     pi_width, pi_height, !b_autoresize );
569     *p_id = ret;
570     if( ret ) /* The videoWidget is available */
571     {
572         /* Consider the video active now */
573         showVideo();
574
575         /* Ask videoWidget to resize correctly, if we are in normal mode */
576         if( !isFullScreen() && !isMaximized() && b_autoresize )
577             videoWidget->SetSizing( *pi_width, *pi_height );
578     }
579 }
580
581 /* Asynchronous call from the WindowClose function */
582 void MainInterface::releaseVideo( void )
583 {
584     emit askReleaseVideo();
585 }
586
587 /* Function that is CONNECTED to the previous emit */
588 void MainInterface::releaseVideoSlot( void )
589 {
590     videoWidget->release();
591     setVideoOnTop( false );
592     setVideoFullScreen( false );
593
594     if( stackCentralW->currentWidget() == videoWidget )
595         restoreStackOldWidget();
596
597     /* We don't want to have a blank video to popup */
598     stackCentralOldWidget = bgWidget;
599 }
600
601 void MainInterface::setVideoSize( unsigned int w, unsigned int h )
602 {
603     if( !isFullScreen() && !isMaximized() )
604         videoWidget->SetSizing( w, h );
605 }
606
607 void MainInterface::setVideoFullScreen( bool fs )
608 {
609     b_videoFullScreen = fs;
610     if( fs )
611     {
612         int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
613         /* if user hasn't defined screennumber, or screennumber that is bigger
614          * than current number of screens, take screennumber where current interface
615          * is
616          */
617         if( numscreen == -1 || numscreen > QApplication::desktop()->numScreens() )
618             numscreen = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
619
620         QRect screenres = QApplication::desktop()->screenGeometry( numscreen );
621
622         /* To be sure window is on proper-screen in xinerama */
623         if( !screenres.contains( pos() ) )
624         {
625             msg_Dbg( p_intf, "Moving video to correct screen");
626             move( QPoint( screenres.x(), screenres.y() ) );
627         }
628         setMinimalView( true );
629         setInterfaceFullScreen( true );
630     }
631     else
632     {
633         /* TODO do we want to restore screen and position ? (when
634          * qt-fullscreen-screennumber is forced) */
635         setMinimalView( b_minimalView );
636         setInterfaceFullScreen( b_interfaceFullScreen );
637     }
638     videoWidget->sync();
639 }
640
641 /* Slot to change the video always-on-top flag.
642  * Emit askVideoOnTop() to invoke this from other thread. */
643 void MainInterface::setVideoOnTop( bool on_top )
644 {
645     b_videoOnTop = on_top;
646
647     Qt::WindowFlags oldflags = windowFlags(), newflags;
648
649     if( b_videoOnTop )
650         newflags = oldflags | Qt::WindowStaysOnTopHint;
651     else
652         newflags = oldflags & ~Qt::WindowStaysOnTopHint;
653
654     if( newflags != oldflags )
655     {
656         setWindowFlags( newflags );
657         show(); /* necessary to apply window flags */
658     }
659 }
660
661 /* Asynchronous call from WindowControl function */
662 int MainInterface::controlVideo( int i_query, va_list args )
663 {
664     switch( i_query )
665     {
666     case VOUT_WINDOW_SET_SIZE:
667     {
668         unsigned int i_width  = va_arg( args, unsigned int );
669         unsigned int i_height = va_arg( args, unsigned int );
670
671         emit askVideoToResize( i_width, i_height );
672         return VLC_SUCCESS;
673     }
674     case VOUT_WINDOW_SET_STATE:
675     {
676         unsigned i_arg = va_arg( args, unsigned );
677         unsigned on_top = i_arg & VOUT_WINDOW_STATE_ABOVE;
678
679         emit askVideoOnTop( on_top != 0 );
680         return VLC_SUCCESS;
681     }
682     case VOUT_WINDOW_SET_FULLSCREEN:
683     {
684         bool b_fs = va_arg( args, int );
685
686         emit askVideoSetFullScreen( b_fs );
687         return VLC_SUCCESS;
688     }
689     default:
690         msg_Warn( p_intf, "unsupported control query" );
691         return VLC_EGENERIC;
692     }
693 }
694
695 /*****************************************************************************
696  * Playlist, Visualisation and Menus handling
697  *****************************************************************************/
698 /**
699  * Toggle the playlist widget or dialog
700  **/
701 void MainInterface::createPlaylist()
702 {
703     playlistWidget = new PlaylistWidget( p_intf, this );
704
705     if( b_plDocked )
706     {
707         stackCentralW->addWidget( playlistWidget );
708         stackWidgetsSizes[playlistWidget] = settings->value( "playlistSize", QSize( 500, 250 ) ).toSize();
709     }
710     else
711     {
712 #ifdef WIN32
713         playlistWidget->setParent( NULL );
714 #endif
715         playlistWidget->setWindowFlags( Qt::Window );
716
717         /* This will restore the geometry but will not work for position,
718            because of parenting */
719         QVLCTools::restoreWidgetPosition( p_intf, "Playlist",
720                 playlistWidget, QSize( 600, 300 ) );
721     }
722 }
723
724 void MainInterface::togglePlaylist()
725 {
726     if( !playlistWidget )
727     {
728         createPlaylist();
729     }
730
731     if( b_plDocked )
732     {
733         /* Playlist is not visible, show it */
734         if( stackCentralW->currentWidget() != playlistWidget )
735         {
736             showTab( playlistWidget );
737         }
738         else /* Hide it! */
739         {
740             restoreStackOldWidget();
741         }
742         playlistVisible = ( stackCentralW->currentWidget() == playlistWidget );
743     }
744     else
745     {
746 #ifdef WIN32
747         playlistWidget->setParent( NULL );
748 #endif
749         playlistWidget->setWindowFlags( Qt::Window );
750         playlistVisible = !playlistVisible;
751         playlistWidget->setVisible( playlistVisible );
752     }
753     debug();
754 }
755
756 void MainInterface::dockPlaylist( bool p_docked )
757 {
758     if( b_plDocked == p_docked ) return;
759     b_plDocked = p_docked;
760
761     if( !playlistWidget ) return; /* Playlist wasn't created yet */
762     if( !p_docked )
763     {
764         stackCentralW->removeWidget( playlistWidget );
765 #ifdef WIN32
766         playlistWidget->setParent( NULL );
767 #endif
768         playlistWidget->setWindowFlags( Qt::Window );
769         QVLCTools::restoreWidgetPosition( p_intf, "Playlist",
770                 playlistWidget, QSize( 600, 300 ) );
771         playlistWidget->show();
772         restoreStackOldWidget();
773     }
774     else
775     {
776         QVLCTools::saveWidgetPosition( p_intf, "Playlist", playlistWidget );
777         playlistWidget->setWindowFlags( Qt::Widget ); // Probably a Qt bug here
778         // It would be logical that QStackWidget::addWidget reset the flags...
779         stackCentralW->addWidget( playlistWidget );
780         showTab( playlistWidget );
781     }
782     playlistVisible = true;
783 }
784
785 /*
786  * setMinimalView is the private function used by
787  * the SLOT toggleMinimalView and setVideoFullScreen
788  */
789 void MainInterface::setMinimalView( bool b_minimal )
790 {
791     menuBar()->setVisible( !b_minimal );
792     controls->setVisible( !b_minimal );
793     statusBar()->setVisible( !b_minimal );
794     inputC->setVisible( !b_minimal );
795 }
796
797 /*
798  * This public SLOT is used for moving to minimal View Mode
799  *
800  * If b_minimal is false, then we are normalView
801  */
802 void MainInterface::toggleMinimalView( bool b_minimal )
803 {
804     if( !b_minimalView && b_autoresize ) /* Normal mode */
805     {
806         if( stackCentralW->currentWidget() == bgWidget )
807         {
808             if( stackCentralW->height() < 16 )
809             {
810                 resizeStack( stackCentralW->width(), 100 );
811             }
812         }
813     }
814     b_minimalView = b_minimal;
815     if( !b_videoFullScreen )
816         setMinimalView( b_minimalView );
817
818     emit minimalViewToggled( b_minimalView );
819 }
820
821 /* toggling advanced controls buttons */
822 void MainInterface::toggleAdvancedButtons()
823 {
824     controls->toggleAdvanced();
825 //    if( fullscreenControls ) fullscreenControls->toggleAdvanced();
826 }
827
828 /* Get the visibility status of the controls (hidden or not, advanced or not) */
829 int MainInterface::getControlsVisibilityStatus()
830 {
831     if( !controls ) return 0;
832     return( (controls->isVisible() ? CONTROLS_VISIBLE : CONTROLS_HIDDEN )
833                 + CONTROLS_ADVANCED * controls->b_advancedVisible );
834 }
835
836 #if 0
837 void MainInterface::visual()
838 {
839     if( !VISIBLE( visualSelector) )
840     {
841         visualSelector->show();
842         if( !THEMIM->getIM()->hasVideo() )
843         {
844             /* Show the background widget */
845         }
846         visualSelectorEnabled = true;
847     }
848     else
849     {
850         /* Stop any currently running visualization */
851         visualSelector->hide();
852         visualSelectorEnabled = false;
853     }
854 }
855 #endif
856
857 /************************************************************************
858  * Other stuff
859  ************************************************************************/
860 void MainInterface::setName( const QString& name )
861 {
862     input_name = name; /* store it for the QSystray use */
863     /* Display it in the status bar, but also as a Tooltip in case it doesn't
864        fit in the label */
865     nameLabel->setText( " " + name + " " );
866     nameLabel->setToolTip( " " + name +" " );
867 }
868
869 /**
870  * Give the decorations of the Main Window a correct Name.
871  * If nothing is given, set it to VLC...
872  **/
873 void MainInterface::setVLCWindowsTitle( const QString& aTitle )
874 {
875     if( aTitle.isEmpty() )
876     {
877         setWindowTitle( qtr( "VLC media player" ) );
878     }
879     else
880     {
881         setWindowTitle( aTitle + " - " + qtr( "VLC media player" ) );
882     }
883 }
884
885 void MainInterface::showCryptedLabel( bool b_show )
886 {
887     if( cryptedLabel == NULL )
888     {
889         cryptedLabel = new QLabel;
890         // The lock icon is not the right one for DRM protection/scrambled.
891         //cryptedLabel->setPixmap( QPixmap( ":/lock" ) );
892         cryptedLabel->setText( "DRM" );
893         statusBar()->addWidget( cryptedLabel );
894     }
895
896     cryptedLabel->setVisible( b_show );
897 }
898
899 void MainInterface::showBuffering( float f_cache )
900 {
901     QString amount = QString("Buffering: %1%").arg( (int)(100*f_cache) );
902     statusBar()->showMessage( amount, 1000 );
903 }
904
905 /*****************************************************************************
906  * Systray Icon and Systray Menu
907  *****************************************************************************/
908 #ifndef HAVE_MAEMO
909 /**
910  * Create a SystemTray icon and a menu that would go with it.
911  * Connects to a click handler on the icon.
912  **/
913 void MainInterface::createSystray()
914 {
915     QIcon iconVLC;
916     if( QDate::currentDate().dayOfYear() >= 354 )
917         iconVLC =  QIcon( ":/logo/vlc128-christmas.png" );
918     else
919         iconVLC =  QIcon( ":/logo/vlc128.png" );
920     sysTray = new QSystemTrayIcon( iconVLC, this );
921     sysTray->setToolTip( qtr( "VLC media player" ));
922
923     systrayMenu = new QMenu( qtr( "VLC media player" ), this );
924     systrayMenu->setIcon( iconVLC );
925
926     QVLCMenu::updateSystrayMenu( this, p_intf, true );
927     sysTray->show();
928
929     CONNECT( sysTray, activated( QSystemTrayIcon::ActivationReason ),
930             this, handleSystrayClick( QSystemTrayIcon::ActivationReason ) );
931 }
932
933 /**
934  * Updates the Systray Icon's menu and toggle the main interface
935  */
936 void MainInterface::toggleUpdateSystrayMenu()
937 {
938     /* If hidden, show it */
939     if( isHidden() )
940     {
941         show();
942         activateWindow();
943     }
944     else if( isMinimized() )
945     {
946         /* Minimized */
947         showNormal();
948         activateWindow();
949     }
950     else
951     {
952         /* Visible (possibly under other windows) */
953 #ifdef WIN32
954         /* check if any visible window is above vlc in the z-order,
955          * but ignore the ones always on top
956          * and the ones which can't be activated */
957         WINDOWINFO wi;
958         HWND hwnd;
959         wi.cbSize = sizeof( WINDOWINFO );
960         for( hwnd = GetNextWindow( internalWinId(), GW_HWNDPREV );
961                 hwnd && ( !IsWindowVisible( hwnd ) ||
962                     ( GetWindowInfo( hwnd, &wi ) &&
963                       (wi.dwExStyle&WS_EX_NOACTIVATE) ) );
964                 hwnd = GetNextWindow( hwnd, GW_HWNDPREV ) );
965             if( !hwnd || !GetWindowInfo( hwnd, &wi ) ||
966                 (wi.dwExStyle&WS_EX_TOPMOST) )
967             {
968                 hide();
969             }
970             else
971             {
972                 activateWindow();
973             }
974 #else
975         hide();
976 #endif
977     }
978     QVLCMenu::updateSystrayMenu( this, p_intf );
979 }
980
981 void MainInterface::handleSystrayClick(
982                                     QSystemTrayIcon::ActivationReason reason )
983 {
984     switch( reason )
985     {
986         case QSystemTrayIcon::Trigger:
987         case QSystemTrayIcon::DoubleClick:
988 #ifdef Q_WS_MAC
989             QVLCMenu::updateSystrayMenu( this, p_intf );
990 #else
991             toggleUpdateSystrayMenu();
992 #endif
993             break;
994         case QSystemTrayIcon::MiddleClick:
995             sysTray->showMessage( qtr( "VLC media player" ),
996                     qtr( "Control menu for the player" ),
997                     QSystemTrayIcon::Information, 3000 );
998             break;
999         default:
1000             break;
1001     }
1002 }
1003
1004 /**
1005  * Updates the name of the systray Icon tooltip.
1006  * Doesn't check if the systray exists, check before you call it.
1007  **/
1008 void MainInterface::updateSystrayTooltipName( const QString& name )
1009 {
1010     if( name.isEmpty() )
1011     {
1012         sysTray->setToolTip( qtr( "VLC media player" ) );
1013     }
1014     else
1015     {
1016         sysTray->setToolTip( name );
1017         if( b_notificationEnabled && ( isHidden() || isMinimized() ) )
1018         {
1019             sysTray->showMessage( qtr( "VLC media player" ), name,
1020                     QSystemTrayIcon::NoIcon, 3000 );
1021         }
1022     }
1023
1024     QVLCMenu::updateSystrayMenu( this, p_intf );
1025 }
1026
1027 /**
1028  * Updates the status of the systray Icon tooltip.
1029  * Doesn't check if the systray exists, check before you call it.
1030  **/
1031 void MainInterface::updateSystrayTooltipStatus( int i_status )
1032 {
1033     switch( i_status )
1034     {
1035     case PLAYING_S:
1036         sysTray->setToolTip( input_name );
1037         break;
1038     case PAUSE_S:
1039         sysTray->setToolTip( input_name + " - " + qtr( "Paused") );
1040         break;
1041     default:
1042         sysTray->setToolTip( qtr( "VLC media player" ) );
1043         break;
1044     }
1045     QVLCMenu::updateSystrayMenu( this, p_intf );
1046 }
1047 #endif
1048
1049 /************************************************************************
1050  * D&D Events
1051  ************************************************************************/
1052 void MainInterface::dropEvent(QDropEvent *event)
1053 {
1054     dropEventPlay( event, true );
1055 }
1056
1057 void MainInterface::dropEventPlay( QDropEvent *event, bool b_play )
1058 {
1059     if( event->possibleActions() & Qt::CopyAction )
1060        event->setDropAction( Qt::CopyAction );
1061     else
1062         return;
1063
1064     const QMimeData *mimeData = event->mimeData();
1065
1066     /* D&D of a subtitles file, add it on the fly */
1067     if( mimeData->urls().size() == 1 && THEMIM->getIM()->hasInput() )
1068     {
1069         if( !input_AddSubtitle( THEMIM->getInput(),
1070                  qtu( toNativeSeparators( mimeData->urls()[0].toLocalFile() ) ),
1071                  true ) )
1072         {
1073             event->accept();
1074             return;
1075         }
1076     }
1077
1078     bool first = b_play;
1079     foreach( const QUrl &url, mimeData->urls() )
1080     {
1081         if( url.isValid() )
1082         {
1083             char* psz_uri = make_URI( url.toEncoded().constData(), NULL );
1084             playlist_Add( THEPL, psz_uri, NULL,
1085                           PLAYLIST_APPEND | (first ? PLAYLIST_GO: PLAYLIST_PREPARSE),
1086                           PLAYLIST_END, true, pl_Unlocked );
1087             free( psz_uri );
1088             first = false;
1089             RecentsMRL::getInstance( p_intf )->addRecent( url.toString() );
1090         }
1091     }
1092
1093     /* Browsers give content as text if you dnd the addressbar,
1094        so check if mimedata has valid url in text and use it
1095        if we didn't get any normal Urls()*/
1096     if( !mimeData->hasUrls() && mimeData->hasText() &&
1097         QUrl(mimeData->text()).isValid() )
1098     {
1099         char *psz_uri = make_URI( qtu( mimeData->text() ), NULL );
1100         playlist_Add( THEPL, psz_uri, NULL,
1101                       PLAYLIST_APPEND | (first ? PLAYLIST_GO: PLAYLIST_PREPARSE),
1102                       PLAYLIST_END, true, pl_Unlocked );
1103         free( psz_uri );
1104     }
1105     event->accept();
1106 }
1107 void MainInterface::dragEnterEvent(QDragEnterEvent *event)
1108 {
1109      event->acceptProposedAction();
1110 }
1111 void MainInterface::dragMoveEvent(QDragMoveEvent *event)
1112 {
1113      event->acceptProposedAction();
1114 }
1115 void MainInterface::dragLeaveEvent(QDragLeaveEvent *event)
1116 {
1117      event->accept();
1118 }
1119
1120 /************************************************************************
1121  * Events stuff
1122  ************************************************************************/
1123 void MainInterface::keyPressEvent( QKeyEvent *e )
1124 {
1125     handleKeyPress( e );
1126 }
1127
1128 void MainInterface::handleKeyPress( QKeyEvent *e )
1129 {
1130     if( ( e->modifiers() &  Qt::ControlModifier ) && ( e->key() == Qt::Key_H ) )
1131     {
1132         toggleMinimalView( !b_minimalView );
1133         e->accept();
1134     }
1135
1136     int i_vlck = qtEventToVLCKey( e );
1137     if( i_vlck > 0 )
1138     {
1139         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
1140         e->accept();
1141     }
1142     else
1143         e->ignore();
1144 }
1145
1146 void MainInterface::wheelEvent( QWheelEvent *e )
1147 {
1148     int i_vlckey = qtWheelEventToVLCKey( e );
1149     var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlckey );
1150     e->accept();
1151 }
1152
1153 void MainInterface::closeEvent( QCloseEvent *e )
1154 {
1155     e->accept();
1156     hide();
1157     THEDP->quit();
1158 }
1159
1160 void MainInterface::setInterfaceFullScreen( bool fs )
1161 {
1162     if( fs )
1163         setWindowState( windowState() | Qt::WindowFullScreen );
1164     else
1165         setWindowState( windowState() & ~Qt::WindowFullScreen );
1166 }
1167 void MainInterface::toggleInterfaceFullScreen()
1168 {
1169     b_interfaceFullScreen = !b_interfaceFullScreen;
1170     if( !b_videoFullScreen )
1171         setInterfaceFullScreen( b_interfaceFullScreen );
1172     emit fullscreenInterfaceToggled( b_interfaceFullScreen );
1173 }
1174
1175 /*****************************************************************************
1176  * PopupMenuCB: callback triggered by the intf-popupmenu playlist variable.
1177  *  We don't show the menu directly here because we don't want the
1178  *  caller to block for a too long time.
1179  *****************************************************************************/
1180 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
1181                         vlc_value_t old_val, vlc_value_t new_val, void *param )
1182 {
1183     intf_thread_t *p_intf = (intf_thread_t *)param;
1184
1185     if( p_intf->pf_show_dialog )
1186     {
1187         p_intf->pf_show_dialog( p_intf, INTF_DIALOG_POPUPMENU,
1188                                 new_val.b_bool, NULL );
1189     }
1190
1191     return VLC_SUCCESS;
1192 }
1193
1194 /*****************************************************************************
1195  * IntfShowCB: callback triggered by the intf-show libvlc variable.
1196  *****************************************************************************/
1197 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
1198                        vlc_value_t old_val, vlc_value_t new_val, void *param )
1199 {
1200     intf_thread_t *p_intf = (intf_thread_t *)param;
1201     p_intf->p_sys->p_mi->toggleFSC();
1202
1203     /* Show event */
1204      return VLC_SUCCESS;
1205 }