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