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