1 /*****************************************************************************
2 * main_interface.cpp : Main interface
3 ****************************************************************************
4 * Copyright (C) 2006-2011 VideoLAN and AUTHORS
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Jean-Baptiste Kempf <jb@videolan.org>
9 * Ilkka Ollakka <ileoo@videolan.org>
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.
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.
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 *****************************************************************************/
32 #include "main_interface.hpp"
33 #include "input_manager.hpp" // Creation
34 #include "actions_manager.hpp" // killInstance
36 #include "util/customwidgets.hpp" // qtEventToVLCKey, QVLCStackedWidget
37 #include "util/qt_dirs.hpp" // toNativeSeparators
39 #include "components/interface_widgets.hpp" // bgWidget, videoWidget
40 #include "components/controller.hpp" // controllers
41 #include "components/playlist/playlist.hpp" // plWidget
42 #include "dialogs/firstrun.hpp" // First Run
43 #include "dialogs/playlist.hpp" // PlaylistDialog
45 #include "menus.hpp" // Menu creation
46 #include "recents.hpp" // RecentItems when DnD
48 #include <QCloseEvent>
60 #include <QStackedWidget>
63 #include <vlc_keys.h> /* Wheel event */
64 #include <vlc_vout_display.h> /* vout_thread_t and VOUT_ events */
67 #if defined(_WIN32) && HAS_QT5
69 #include <qpa/qplatformnativeinterface.h>
74 /* Callback prototypes */
75 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
76 vlc_value_t old_val, vlc_value_t new_val, void *param );
77 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
78 vlc_value_t old_val, vlc_value_t new_val, void *param );
79 static int IntfBossCB( vlc_object_t *p_this, const char *psz_variable,
80 vlc_value_t old_val, vlc_value_t new_val, void *param );
81 static int IntfRaiseMainCB( vlc_object_t *p_this, const char *psz_variable,
82 vlc_value_t old_val, vlc_value_t new_val,
85 const QEvent::Type MainInterface::ToolbarsNeedRebuild =
86 (QEvent::Type)QEvent::registerEventType();
88 MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
90 /* Variables initialisation */
93 playlistWidget = NULL;
94 stackCentralOldWidget= NULL;
96 fullscreenControls = NULL;
101 b_hideAfterCreation = false; // --qt-start-minimized
102 playlistVisible = false;
104 b_interfaceFullScreen= false;
105 b_hasPausedWhenMinimized = false;
108 /* Ask for Privacy */
109 FirstRun::CheckAndRun( this, p_intf );
112 * Configuration and settings
113 * Pre-building of interface
116 setFocusPolicy( Qt::StrongFocus );
117 setAcceptDrops( true );
118 setWindowRole( "vlc-main" );
119 setWindowIcon( QApplication::windowIcon() );
120 setWindowOpacity( var_InheritFloat( p_intf, "qt-opacity" ) );
122 setAttribute( Qt::WA_MacBrushedMetal );
125 /* Is video in embedded in the UI or not */
126 b_videoEmbedded = var_InheritBool( p_intf, "embedded-video" );
128 /* Does the interface resize to video size or the opposite */
129 b_autoresize = var_InheritBool( p_intf, "qt-video-autoresize" );
131 /* Are we in the enhanced always-video mode or not ? */
132 b_minimalView = var_InheritBool( p_intf, "qt-minimal-view" );
134 /* Do we want anoying popups or not */
135 i_notificationSetting = var_InheritInteger( p_intf, "qt-notification" );
138 b_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );
140 /* Set the other interface settings */
141 settings = getSettings();
145 p_intf->p_sys->disable_volume_keys = var_InheritBool( p_intf, "qt-disable-volume-keys" );
149 b_plDocked = getSettings()->value( "MainWindow/pl-dock-status", true ).toBool();
152 /**************************
153 * UI and Widgets design
154 **************************/
155 setVLCWindowsTitle();
160 VLCMenuBar::createMenuBar( this, p_intf );
161 CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ),
162 THEDP, destroyPopupMenu() );
164 createMainWidget( settings );
170 setStatusBarVisibility( getSettings()->value( "MainWindow/status-bar-visible", false ).toBool() );
175 taskbar_wmsg = RegisterWindowMessage(TEXT("TaskbarButtonCreated"));
178 /*********************************
179 * Create the Systray Management *
180 *********************************/
183 /*************************************************************
184 * Connect the input manager to the GUI elements it manages *
185 * Beware initSystray did some connects on input manager too *
186 *************************************************************/
188 * Connects on nameChanged()
189 * Those connects are different because options can impeach them to trigger.
191 /* Main Interface statusbar */
192 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
193 this, setName( const QString& ) );
194 /* and title of the Main Interface*/
195 if( var_InheritBool( p_intf, "qt-name-in-title" ) )
197 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
198 this, setVLCWindowsTitle( const QString& ) );
200 /* END CONNECTS ON IM */
202 /* VideoWidget connects for asynchronous calls */
203 b_videoFullScreen = false;
204 connect( this, SIGNAL(askGetVideo(WId*,int*,int*,unsigned*,unsigned *)),
205 this, SLOT(getVideoSlot(WId*,int*,int*,unsigned*,unsigned*)),
206 Qt::BlockingQueuedConnection );
207 connect( this, SIGNAL(askReleaseVideo( void )),
208 this, SLOT(releaseVideoSlot( void )),
209 Qt::BlockingQueuedConnection );
210 CONNECT( this, askVideoOnTop(bool), this, setVideoOnTop(bool));
216 CONNECT( this, askVideoToResize( unsigned int, unsigned int ),
217 this, setVideoSize( unsigned int, unsigned int ) );
218 CONNECT( videoWidget, sizeChanged( int, int ),
219 this, videoSizeChanged( int, int ) );
221 CONNECT( this, askVideoSetFullScreen( bool ),
222 this, setVideoFullScreen( bool ) );
225 CONNECT( THEDP, toolBarConfUpdated(), this, toolBarConfUpdated() );
226 installEventFilter( this );
228 CONNECT( this, askToQuit(), THEDP, quit() );
230 CONNECT( this, askBoss(), this, setBoss() );
231 CONNECT( this, askRaise(), this, setRaise() );
233 /** END of CONNECTS**/
239 var_AddCallback( p_intf->p_libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
240 var_AddCallback( p_intf->p_libvlc, "intf-boss", IntfBossCB, p_intf );
241 var_AddCallback( p_intf->p_libvlc, "intf-show", IntfRaiseMainCB, p_intf );
243 /* Register callback for the intf-popupmenu variable */
244 var_AddCallback( p_intf->p_libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
247 /* Final Sizing, restoration and placement of the interface */
248 if( settings->value( "MainWindow/playlist-visible", false ).toBool() )
251 QVLCTools::restoreWidgetPosition( settings, this, QSize(600, 420) );
253 b_interfaceFullScreen = isFullScreen();
255 setVisible( !b_hideAfterCreation );
257 /* Switch to minimal view if needed, must be called after the show() */
259 toggleMinimalView( true );
261 computeMinimumSize();
264 MainInterface::~MainInterface()
266 /* Unsure we hide the videoWidget before destroying it */
267 if( stackCentralOldWidget == videoWidget )
275 ImageList_Destroy( himl );
281 /* Be sure to kill the actionsManager... Only used in the MI and control */
282 ActionsManager::killInstance();
284 /* Delete the FSC controller */
285 delete fullscreenControls;
289 settings->beginGroup("MainWindow");
290 settings->setValue( "pl-dock-status", b_plDocked );
292 /* Save playlist state */
293 settings->setValue( "playlist-visible", playlistVisible );
295 settings->setValue( "adv-controls",
296 getControlsVisibilityStatus() & CONTROLS_ADVANCED );
297 settings->setValue( "status-bar-visible", b_statusbarVisible );
299 /* Save the stackCentralW sizes */
300 settings->setValue( "bgSize", stackWidgetsSizes[bgWidget] );
301 settings->setValue( "playlistSize", stackWidgetsSizes[playlistWidget] );
302 settings->endGroup();
305 QVLCTools::saveWidgetPosition(settings, this);
307 /* Unregister callbacks */
308 var_DelCallback( p_intf->p_libvlc, "intf-boss", IntfBossCB, p_intf );
309 var_DelCallback( p_intf->p_libvlc, "intf-show", IntfRaiseMainCB, p_intf );
310 var_DelCallback( p_intf->p_libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
311 var_DelCallback( p_intf->p_libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
313 p_intf->p_sys->p_mi = NULL;
316 void MainInterface::computeMinimumSize()
319 if( menuBar()->isVisible() )
320 minWidth += controls->sizeHint().width();
322 setMinimumWidth( minWidth );
325 /*****************************
327 *****************************/
328 void MainInterface::recreateToolbars()
330 bool b_adv = getControlsVisibilityStatus() & CONTROLS_ADVANCED;
335 controls = new ControlsWidget( p_intf, b_adv, this );
336 inputC = new InputControlsWidget( p_intf, this );
337 mainLayout->insertWidget( 2, inputC );
338 mainLayout->insertWidget( settings->value( "MainWindow/ToolbarPos", 0 ).toInt() ? 0: 3,
341 if( fullscreenControls )
343 delete fullscreenControls;
344 fullscreenControls = new FullscreenControllerWidget( p_intf, this );
345 CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
346 this, handleKeyPress( QKeyEvent * ) );
347 THEMIM->requestVoutUpdate();
350 setMinimalView( b_minimalView );
353 void MainInterface::reloadPrefs()
355 i_notificationSetting = var_InheritInteger( p_intf, "qt-notification" );
356 b_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );
358 p_intf->p_sys->disable_volume_keys = var_InheritBool( p_intf, "qt-disable-volume-keys" );
360 if( !var_InheritBool( p_intf, "qt-fs-controller" ) && fullscreenControls )
362 delete fullscreenControls;
363 fullscreenControls = NULL;
367 void MainInterface::createMainWidget( QSettings *creationSettings )
369 /* Create the main Widget and the mainLayout */
370 QWidget *main = new QWidget;
371 setCentralWidget( main );
372 mainLayout = new QVBoxLayout( main );
373 main->setContentsMargins( 0, 0, 0, 0 );
374 mainLayout->setSpacing( 0 ); mainLayout->setMargin( 0 );
377 stackCentralW = new QVLCStackedWidget( main );
380 if ( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY
381 && var_InheritBool( p_intf, "qt-icon-change" ) )
383 bgWidget = new EasterEggBackgroundWidget( p_intf );
384 CONNECT( this, kc_pressed(), bgWidget, animate() );
387 bgWidget = new BackgroundWidget( p_intf );
389 stackCentralW->addWidget( bgWidget );
390 if ( !var_InheritBool( p_intf, "qt-bgcone" ) )
391 bgWidget->setWithArt( false );
393 if ( var_InheritBool( p_intf, "qt-bgcone-expands" ) )
394 bgWidget->setExpandstoHeight( true );
396 /* And video Outputs */
397 if( b_videoEmbedded )
399 videoWidget = new VideoWidget( p_intf );
400 stackCentralW->addWidget( videoWidget );
402 mainLayout->insertWidget( 1, stackCentralW );
404 stackWidgetsSizes[bgWidget] =
405 creationSettings->value( "MainWindow/bgSize", QSize( 600, 0 ) ).toSize();
406 /* Resize even if no-auto-resize, because we are at creation */
407 resizeStack( stackWidgetsSizes[bgWidget].width(), stackWidgetsSizes[bgWidget].height() );
409 /* Create the CONTROLS Widget */
410 controls = new ControlsWidget( p_intf,
411 creationSettings->value( "MainWindow/adv-controls", false ).toBool(), this );
412 inputC = new InputControlsWidget( p_intf, this );
414 mainLayout->insertWidget( 2, inputC );
415 mainLayout->insertWidget(
416 creationSettings->value( "MainWindow/ToolbarPos", 0 ).toInt() ? 0: 3,
419 /* Visualisation, disabled for now, they SUCK */
421 visualSelector = new VisualSelector( p_intf );
422 mainLayout->insertWidget( 0, visualSelector );
423 visualSelector->hide();
427 /* Enable the popup menu in the MI */
428 main->setContextMenuPolicy( Qt::CustomContextMenu );
429 CONNECT( main, customContextMenuRequested( const QPoint& ),
430 THEDP, setPopupMenu() );
432 if ( depth() > 8 ) /* 8bit depth has too many issues with opacity */
433 /* Create the FULLSCREEN CONTROLS Widget */
434 if( var_InheritBool( p_intf, "qt-fs-controller" ) )
436 fullscreenControls = new FullscreenControllerWidget( p_intf, this );
437 CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
438 this, handleKeyPress( QKeyEvent * ) );
442 inline void MainInterface::initSystray()
444 bool b_systrayAvailable = QSystemTrayIcon::isSystemTrayAvailable();
445 bool b_systrayWanted = var_InheritBool( p_intf, "qt-system-tray" );
447 if( var_InheritBool( p_intf, "qt-start-minimized") )
449 if( b_systrayAvailable )
451 b_systrayWanted = true;
452 b_hideAfterCreation = true;
455 msg_Err( p_intf, "cannot start minimized without system tray bar" );
458 if( b_systrayAvailable && b_systrayWanted )
462 inline void MainInterface::createStatusBar()
467 /* Widgets Creation*/
468 QStatusBar *statusBarr = statusBar();
470 TimeLabel *timeLabel = new TimeLabel( p_intf );
471 nameLabel = new ClickableQLabel();
472 nameLabel->setTextInteractionFlags( Qt::TextSelectableByMouse
473 | Qt::TextSelectableByKeyboard );
474 SpeedLabel *speedLabel = new SpeedLabel( p_intf, this );
476 /* Styling those labels */
477 timeLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
478 speedLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
479 nameLabel->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel);
480 timeLabel->setStyleSheet(
481 "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
482 speedLabel->setStyleSheet(
483 "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
484 /* pad both label and its tooltip */
485 nameLabel->setStyleSheet( "padding-left: 5px; padding-right: 5px;" );
487 /* and adding those */
488 statusBarr->addWidget( nameLabel, 8 );
489 statusBarr->addPermanentWidget( speedLabel, 0 );
490 statusBarr->addPermanentWidget( timeLabel, 0 );
492 CONNECT( nameLabel, doubleClicked(), THEDP, epgDialog() );
493 /* timeLabel behaviour:
494 - double clicking opens the goto time dialog
495 - right-clicking and clicking just toggle between remaining and
497 CONNECT( timeLabel, doubleClicked(), THEDP, gotoTimeDialog() );
499 CONNECT( THEMIM->getIM(), encryptionChanged( bool ),
500 this, showCryptedLabel( bool ) );
502 CONNECT( THEMIM->getIM(), seekRequested( float ),
503 timeLabel, setDisplayPosition( float ) );
505 /* This shouldn't be necessary, but for somehow reason, the statusBarr
506 starts at height of 20px and when a text is shown it needs more space.
507 But, as the QMainWindow policy doesn't allow statusBar to change QMW's
508 geometry, we need to force a height. If you have a better idea, please
511 statusBarr->setFixedHeight( statusBarr->sizeHint().height() + 2 );
514 /**********************************************************************
515 * Handling of sizing of the components
516 **********************************************************************/
518 void MainInterface::debug()
522 msg_Dbg( p_intf, "Controls size: %i - %i", controls->size().height(), controls->size().width() );
523 msg_Dbg( p_intf, "Controls minimumsize: %i - %i", controls->minimumSize().height(), controls->minimumSize().width() );
524 msg_Dbg( p_intf, "Controls sizeHint: %i - %i", controls->sizeHint().height(), controls->sizeHint().width() );
527 msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
528 msg_Dbg( p_intf, "sizeHint: %i - %i", sizeHint().height(), sizeHint().width() );
529 msg_Dbg( p_intf, "minimumsize: %i - %i", minimumSize().height(), minimumSize().width() );
531 msg_Dbg( p_intf, "Stack size: %i - %i", stackCentralW->size().height(), stackCentralW->size().width() );
532 msg_Dbg( p_intf, "Stack sizeHint: %i - %i", stackCentralW->sizeHint().height(), stackCentralW->sizeHint().width() );
533 msg_Dbg( p_intf, "Central size: %i - %i", centralWidget()->size().height(), centralWidget()->size().width() );
537 inline void MainInterface::showVideo() { showTab( videoWidget ); setRaise(); }
538 inline void MainInterface::restoreStackOldWidget()
539 { showTab( stackCentralOldWidget ); }
541 inline void MainInterface::showTab( QWidget *widget )
543 if ( !widget ) widget = bgWidget; /* trying to restore a null oldwidget */
545 if ( stackCentralOldWidget )
546 msg_Dbg( p_intf, "Old stackCentralOldWidget %s at index %i",
547 stackCentralOldWidget->metaObject()->className(),
548 stackCentralW->indexOf( stackCentralOldWidget ) );
549 msg_Dbg( p_intf, "ShowTab request for %s", widget->metaObject()->className() );
551 /* fixing when the playlist has been undocked after being hidden.
552 restoreStackOldWidget() is called when video stops but
553 stackCentralOldWidget would still be pointing to playlist */
554 if ( widget == playlistWidget && !isPlDocked() )
557 stackCentralOldWidget = stackCentralW->currentWidget();
558 stackWidgetsSizes[stackCentralOldWidget] = stackCentralW->size();
560 /* If we are playing video, embedded */
561 if( videoWidget && THEMIM->getIM()->hasVideo() )
563 /* Video -> Playlist */
564 if( videoWidget == stackCentralOldWidget && widget == playlistWidget )
566 stackCentralW->removeWidget( videoWidget );
567 videoWidget->show(); videoWidget->raise();
570 /* Playlist -> Video */
571 if( playlistWidget == stackCentralOldWidget && widget == videoWidget )
573 playlistWidget->artContainer->removeWidget( videoWidget );
574 videoWidget->show(); videoWidget->raise();
575 stackCentralW->addWidget( videoWidget );
578 /* Embedded playlist -> Non-embedded playlist */
579 if( bgWidget == stackCentralOldWidget && widget == videoWidget )
581 /* In rare case when video is started before the interface */
582 if( playlistWidget != NULL )
583 playlistWidget->artContainer->removeWidget( videoWidget );
584 videoWidget->show(); videoWidget->raise();
585 stackCentralW->addWidget( videoWidget );
586 stackCentralW->setCurrentWidget( videoWidget );
590 stackCentralW->setCurrentWidget( widget );
592 resizeStack( stackWidgetsSizes[widget].width(), stackWidgetsSizes[widget].height() );
595 msg_Dbg( p_intf, "Stack state changed to %s, index %i",
596 stackCentralW->currentWidget()->metaObject()->className(),
597 stackCentralW->currentIndex() );
598 msg_Dbg( p_intf, "New stackCentralOldWidget %s at index %i",
599 stackCentralOldWidget->metaObject()->className(),
600 stackCentralW->indexOf( stackCentralOldWidget ) );
603 /* This part is done later, to account for the new pl size */
604 if( videoWidget && THEMIM->getIM()->hasVideo() &&
605 videoWidget == stackCentralOldWidget && widget == playlistWidget )
607 playlistWidget->artContainer->addWidget( videoWidget );
608 playlistWidget->artContainer->setCurrentWidget( videoWidget );
612 void MainInterface::toggleFSC()
614 if( !fullscreenControls ) return;
616 IMEvent *eShow = new IMEvent( IMEvent::FullscreenControlToggle );
617 QApplication::postEvent( fullscreenControls, eShow );
620 /****************************************************************************
622 ****************************************************************************/
626 * You must not change the state of this object or other Qt UI objects,
627 * from the video output thread - only from the Qt UI main loop thread.
628 * All window provider queries must be handled through signals or events.
629 * That's why we have all those emit statements...
631 WId MainInterface::getVideo( int *pi_x, int *pi_y,
632 unsigned int *pi_width, unsigned int *pi_height )
637 /* This is a blocking call signal. Results are returned through pointers.
638 * Beware of deadlocks! */
640 emit askGetVideo( &id, pi_x, pi_y, pi_width, pi_height );
644 void MainInterface::getVideoSlot( WId *p_id, int *pi_x, int *pi_y,
645 unsigned *pi_width, unsigned *pi_height )
647 /* Hidden or minimized, activate */
648 if( isHidden() || isMinimized() )
649 toggleUpdateSystrayMenu();
651 /* Request the videoWidget */
652 WId ret = videoWidget->request( pi_x, pi_y,
653 pi_width, pi_height, !b_autoresize );
655 if( ret ) /* The videoWidget is available */
657 /* Consider the video active now */
660 /* Ask videoWidget to resize correctly, if we are in normal mode */
661 if( !isFullScreen() && !isMaximized() && b_autoresize )
662 videoWidget->SetSizing( *pi_width, *pi_height );
666 /* Asynchronous call from the WindowClose function */
667 void MainInterface::releaseVideo( void )
669 emit askReleaseVideo();
672 /* Function that is CONNECTED to the previous emit */
673 void MainInterface::releaseVideoSlot( void )
675 /* This function is called when the embedded video window is destroyed,
676 * or in the rare case that the embedded window is still here but the
677 * Qt interface exits. */
678 assert( videoWidget );
679 videoWidget->release();
680 setVideoOnTop( false );
681 setVideoFullScreen( false );
683 if( stackCentralW->currentWidget() == videoWidget )
684 restoreStackOldWidget();
685 else if( playlistWidget &&
686 playlistWidget->artContainer->currentWidget() == videoWidget )
688 playlistWidget->artContainer->setCurrentIndex( 0 );
689 stackCentralW->addWidget( videoWidget );
692 /* We don't want to have a blank video to popup */
693 stackCentralOldWidget = bgWidget;
696 void MainInterface::setVideoSize( unsigned int w, unsigned int h )
698 if( !isFullScreen() && !isMaximized() )
699 videoWidget->SetSizing( w, h );
702 void MainInterface::videoSizeChanged( int w, int h )
704 if( !playlistWidget || playlistWidget->artContainer->currentWidget() != videoWidget )
708 void MainInterface::setVideoFullScreen( bool fs )
710 b_videoFullScreen = fs;
713 int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
714 /* if user hasn't defined screennumber, or screennumber that is bigger
715 * than current number of screens, take screennumber where current interface
718 if( numscreen == -1 || numscreen > QApplication::desktop()->numScreens() )
719 numscreen = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
721 QRect screenres = QApplication::desktop()->screenGeometry( numscreen );
723 /* To be sure window is on proper-screen in xinerama */
724 if( !screenres.contains( pos() ) )
726 msg_Dbg( p_intf, "Moving video to correct screen");
727 move( QPoint( screenres.x(), screenres.y() ) );
731 if( playlistWidget != NULL && playlistWidget->artContainer->currentWidget() == videoWidget )
733 showTab( videoWidget );
737 setMinimalView( true );
738 setInterfaceFullScreen( true );
742 /* TODO do we want to restore screen and position ? (when
743 * qt-fullscreen-screennumber is forced) */
744 setMinimalView( b_minimalView );
745 setInterfaceFullScreen( b_interfaceFullScreen );
747 changeThumbbarButtons( THEMIM->getIM()->playingStatus() );
753 /* Slot to change the video always-on-top flag.
754 * Emit askVideoOnTop() to invoke this from other thread. */
755 void MainInterface::setVideoOnTop( bool on_top )
757 Qt::WindowFlags oldflags = windowFlags(), newflags;
760 newflags = oldflags | Qt::WindowStaysOnTopHint;
762 newflags = oldflags & ~Qt::WindowStaysOnTopHint;
763 if( newflags != oldflags && !b_videoFullScreen )
766 setWindowFlags( newflags );
767 show(); /* necessary to apply window flags */
771 /* Asynchronous call from WindowControl function */
772 int MainInterface::controlVideo( int i_query, va_list args )
776 case VOUT_WINDOW_SET_SIZE:
778 unsigned int i_width = va_arg( args, unsigned int );
779 unsigned int i_height = va_arg( args, unsigned int );
781 emit askVideoToResize( i_width, i_height );
784 case VOUT_WINDOW_SET_STATE:
786 unsigned i_arg = va_arg( args, unsigned );
787 unsigned on_top = i_arg & VOUT_WINDOW_STATE_ABOVE;
789 emit askVideoOnTop( on_top != 0 );
792 case VOUT_WINDOW_SET_FULLSCREEN:
794 bool b_fs = va_arg( args, int );
796 emit askVideoSetFullScreen( b_fs );
800 msg_Warn( p_intf, "unsupported control query" );
805 /*****************************************************************************
806 * Playlist, Visualisation and Menus handling
807 *****************************************************************************/
809 * Toggle the playlist widget or dialog
811 void MainInterface::createPlaylist()
813 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
817 playlistWidget = dialog->exportPlaylistWidget();
818 stackCentralW->addWidget( playlistWidget );
819 stackWidgetsSizes[playlistWidget] = settings->value( "playlistSize", QSize( 600, 300 ) ).toSize();
821 CONNECT( dialog, visibilityChanged(bool), this, setPlaylistVisibility(bool) );
824 void MainInterface::togglePlaylist()
826 if( !playlistWidget ) createPlaylist();
828 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
831 if ( dialog->hasPlaylistWidget() )
832 playlistWidget = dialog->exportPlaylistWidget();
833 /* Playlist is not visible, show it */
834 if( stackCentralW->currentWidget() != playlistWidget )
836 if( stackCentralW->indexOf( playlistWidget ) == -1 )
837 stackCentralW->addWidget( playlistWidget );
838 showTab( playlistWidget );
842 restoreStackOldWidget();
844 playlistVisible = ( stackCentralW->currentWidget() == playlistWidget );
848 playlistVisible = !playlistVisible;
849 if ( ! dialog->hasPlaylistWidget() )
850 dialog->importPlaylistWidget( playlistWidget );
851 if ( playlistVisible )
859 const Qt::Key MainInterface::kc[10] =
861 Qt::Key_Up, Qt::Key_Up,
862 Qt::Key_Down, Qt::Key_Down,
863 Qt::Key_Left, Qt::Key_Right, Qt::Key_Left, Qt::Key_Right,
867 void MainInterface::dockPlaylist( bool p_docked )
869 if( b_plDocked == p_docked ) return;
870 /* some extra check */
871 if ( b_plDocked && !playlistWidget ) createPlaylist();
873 b_plDocked = p_docked;
874 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
876 if( !p_docked ) /* Previously docked */
878 playlistVisible = playlistWidget->isVisible();
879 stackCentralW->removeWidget( playlistWidget );
880 dialog->importPlaylistWidget( playlistWidget );
881 if ( playlistVisible ) dialog->show();
882 restoreStackOldWidget();
884 else /* Previously undocked */
886 playlistVisible = dialog->isVisible();
888 playlistWidget = dialog->exportPlaylistWidget();
889 stackCentralW->addWidget( playlistWidget );
891 /* If playlist is invisible don't show it */
892 if( playlistVisible ) showTab( playlistWidget );
897 * setMinimalView is the private function used by
898 * the SLOT toggleMinimalView and setVideoFullScreen
900 void MainInterface::setMinimalView( bool b_minimal )
902 menuBar()->setVisible( !b_minimal );
903 controls->setVisible( !b_minimal );
904 statusBar()->setVisible( !b_minimal && b_statusbarVisible );
905 inputC->setVisible( !b_minimal );
909 * This public SLOT is used for moving to minimal View Mode
911 * If b_minimal is false, then we are normalView
913 void MainInterface::toggleMinimalView( bool b_minimal )
915 if( !b_minimalView && b_autoresize ) /* Normal mode */
917 if( stackCentralW->currentWidget() == bgWidget )
919 if( stackCentralW->height() < 16 )
921 resizeStack( stackCentralW->width(), 100 );
925 b_minimalView = b_minimal;
926 if( !b_videoFullScreen )
928 setMinimalView( b_minimalView );
929 computeMinimumSize();
932 emit minimalViewToggled( b_minimalView );
935 /* toggling advanced controls buttons */
936 void MainInterface::toggleAdvancedButtons()
938 controls->toggleAdvanced();
939 // if( fullscreenControls ) fullscreenControls->toggleAdvanced();
942 /* Get the visibility status of the controls (hidden or not, advanced or not) */
943 int MainInterface::getControlsVisibilityStatus()
945 if( !controls ) return 0;
946 return( (controls->isVisible() ? CONTROLS_VISIBLE : CONTROLS_HIDDEN )
947 + CONTROLS_ADVANCED * controls->b_advancedVisible );
950 StandardPLPanel *MainInterface::getPlaylistView()
952 if( !playlistWidget ) return NULL;
953 else return playlistWidget->mainView;
956 void MainInterface::setStatusBarVisibility( bool b_visible )
958 statusBar()->setVisible( b_visible );
959 b_statusbarVisible = b_visible;
960 if( controls ) controls->setGripVisible( !b_statusbarVisible );
964 void MainInterface::setPlaylistVisibility( bool b_visible )
966 if( isPlDocked() || THEDP->isDying() || (playlistWidget && playlistWidget->isMinimized() ) )
969 playlistVisible = b_visible;
973 void MainInterface::visual()
975 if( !VISIBLE( visualSelector) )
977 visualSelector->show();
978 if( !THEMIM->getIM()->hasVideo() )
980 /* Show the background widget */
982 visualSelectorEnabled = true;
986 /* Stop any currently running visualization */
987 visualSelector->hide();
988 visualSelectorEnabled = false;
993 /************************************************************************
995 ************************************************************************/
996 void MainInterface::setName( const QString& name )
998 input_name = name; /* store it for the QSystray use */
999 /* Display it in the status bar, but also as a Tooltip in case it doesn't
1001 nameLabel->setText( name );
1002 nameLabel->setToolTip( name );
1006 * Give the decorations of the Main Window a correct Name.
1007 * If nothing is given, set it to VLC...
1009 void MainInterface::setVLCWindowsTitle( const QString& aTitle )
1011 if( aTitle.isEmpty() )
1013 setWindowTitle( qtr( "VLC media player" ) );
1017 setWindowTitle( aTitle + " - " + qtr( "VLC media player" ) );
1021 void MainInterface::showCryptedLabel( bool b_show )
1023 if( cryptedLabel == NULL )
1025 cryptedLabel = new QLabel;
1026 // The lock icon is not the right one for DRM protection/scrambled.
1027 //cryptedLabel->setPixmap( QPixmap( ":/lock" ) );
1028 cryptedLabel->setText( "DRM" );
1029 statusBar()->addWidget( cryptedLabel );
1032 cryptedLabel->setVisible( b_show );
1035 void MainInterface::showBuffering( float f_cache )
1037 QString amount = QString("Buffering: %1%").arg( (int)(100*f_cache) );
1038 statusBar()->showMessage( amount, 1000 );
1041 /*****************************************************************************
1042 * Systray Icon and Systray Menu
1043 *****************************************************************************/
1045 * Create a SystemTray icon and a menu that would go with it.
1046 * Connects to a click handler on the icon.
1048 void MainInterface::createSystray()
1051 if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
1052 iconVLC = QIcon::fromTheme( "vlc-xmas", QIcon( ":/logo/vlc128-xmas.png" ) );
1054 iconVLC = QIcon::fromTheme( "vlc", QIcon( ":/logo/vlc256.png" ) );
1055 sysTray = new QSystemTrayIcon( iconVLC, this );
1056 sysTray->setToolTip( qtr( "VLC media player" ));
1058 systrayMenu = new QMenu( qtr( "VLC media player" ), this );
1059 systrayMenu->setIcon( iconVLC );
1061 VLCMenuBar::updateSystrayMenu( this, p_intf, true );
1064 CONNECT( sysTray, activated( QSystemTrayIcon::ActivationReason ),
1065 this, handleSystrayClick( QSystemTrayIcon::ActivationReason ) );
1067 /* Connects on nameChanged() */
1068 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
1069 this, updateSystrayTooltipName( const QString& ) );
1070 /* Connect PLAY_STATUS on the systray */
1071 CONNECT( THEMIM->getIM(), playingStatusChanged( int ),
1072 this, updateSystrayTooltipStatus( int ) );
1076 * Updates the Systray Icon's menu and toggle the main interface
1078 void MainInterface::toggleUpdateSystrayMenu()
1080 /* If hidden, show it */
1086 else if( isMinimized() )
1094 /* Visible (possibly under other windows) */
1096 /* check if any visible window is above vlc in the z-order,
1097 * but ignore the ones always on top
1098 * and the ones which can't be activated */
1101 QWindow *window = windowHandle();
1102 winId = static_cast<HWND>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("handle", window));
1104 winId = internalWinId();
1109 wi.cbSize = sizeof( WINDOWINFO );
1110 for( hwnd = GetNextWindow( winId, GW_HWNDPREV );
1111 hwnd && ( !IsWindowVisible( hwnd ) ||
1112 ( GetWindowInfo( hwnd, &wi ) &&
1113 (wi.dwExStyle&WS_EX_NOACTIVATE) ) );
1114 hwnd = GetNextWindow( hwnd, GW_HWNDPREV ) );
1115 if( !hwnd || !GetWindowInfo( hwnd, &wi ) ||
1116 (wi.dwExStyle&WS_EX_TOPMOST) )
1129 VLCMenuBar::updateSystrayMenu( this, p_intf );
1132 /* First Item of the systray menu */
1133 void MainInterface::showUpdateSystrayMenu()
1141 VLCMenuBar::updateSystrayMenu( this, p_intf );
1144 /* First Item of the systray menu */
1145 void MainInterface::hideUpdateSystrayMenu()
1148 VLCMenuBar::updateSystrayMenu( this, p_intf );
1151 /* Click on systray Icon */
1152 void MainInterface::handleSystrayClick(
1153 QSystemTrayIcon::ActivationReason reason )
1157 case QSystemTrayIcon::Trigger:
1158 case QSystemTrayIcon::DoubleClick:
1160 VLCMenuBar::updateSystrayMenu( this, p_intf );
1162 toggleUpdateSystrayMenu();
1165 case QSystemTrayIcon::MiddleClick:
1166 sysTray->showMessage( qtr( "VLC media player" ),
1167 qtr( "Control menu for the player" ),
1168 QSystemTrayIcon::Information, 3000 );
1176 * Updates the name of the systray Icon tooltip.
1177 * Doesn't check if the systray exists, check before you call it.
1179 void MainInterface::updateSystrayTooltipName( const QString& name )
1181 if( name.isEmpty() )
1183 sysTray->setToolTip( qtr( "VLC media player" ) );
1187 sysTray->setToolTip( name );
1188 if( ( i_notificationSetting == NOTIFICATION_ALWAYS ) ||
1189 ( i_notificationSetting == NOTIFICATION_MINIMIZED && (isMinimized() || isHidden()) ) )
1191 sysTray->showMessage( qtr( "VLC media player" ), name,
1192 QSystemTrayIcon::NoIcon, 3000 );
1196 VLCMenuBar::updateSystrayMenu( this, p_intf );
1200 * Updates the status of the systray Icon tooltip.
1201 * Doesn't check if the systray exists, check before you call it.
1203 void MainInterface::updateSystrayTooltipStatus( int i_status )
1208 sysTray->setToolTip( input_name );
1211 sysTray->setToolTip( input_name + " - " + qtr( "Paused") );
1214 sysTray->setToolTip( qtr( "VLC media player" ) );
1217 VLCMenuBar::updateSystrayMenu( this, p_intf );
1220 void MainInterface::changeEvent(QEvent *event)
1222 if( event->type() == QEvent::WindowStateChange )
1224 QWindowStateChangeEvent *windowStateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);
1225 Qt::WindowStates newState = windowState();
1226 Qt::WindowStates oldState = windowStateChangeEvent->oldState();
1228 if( newState & Qt::WindowMinimized )
1230 b_hasPausedWhenMinimized = false;
1232 if( THEMIM->getIM()->playingStatus() == PLAYING_S &&
1233 THEMIM->getIM()->hasVideo() && !THEMIM->getIM()->hasVisualisation() &&
1236 b_hasPausedWhenMinimized = true;
1240 else if( oldState & Qt::WindowMinimized && !( newState & Qt::WindowMinimized ) )
1242 if( b_hasPausedWhenMinimized )
1249 QWidget::changeEvent(event);
1252 /************************************************************************
1254 ************************************************************************/
1255 void MainInterface::dropEvent(QDropEvent *event)
1257 dropEventPlay( event, true );
1263 * Event called if something is dropped onto a VLC window
1264 * \param event the event in question
1265 * \param b_play whether to play the file immediately
1266 * \param b_playlist true to add to playlist, false to add to media library
1269 void MainInterface::dropEventPlay( QDropEvent *event, bool b_play, bool b_playlist )
1271 if( event->possibleActions() & ( Qt::CopyAction | Qt::MoveAction | Qt::LinkAction ) )
1272 event->setDropAction( Qt::CopyAction );
1276 const QMimeData *mimeData = event->mimeData();
1278 /* D&D of a subtitles file, add it on the fly */
1279 if( mimeData->urls().count() == 1 && THEMIM->getIM()->hasInput() )
1281 if( !input_AddSubtitleOSD( THEMIM->getInput(),
1282 qtu( toNativeSeparators( mimeData->urls()[0].toLocalFile() ) ),
1290 bool first = b_play;
1291 foreach( const QUrl &url, mimeData->urls() )
1295 QString mrl = toURI( url.toEncoded().constData() );
1296 QFileInfo info( url.toLocalFile() );
1297 if( info.exists() && info.isSymLink() )
1299 QString target = info.symLinkTarget();
1301 if( QFile::exists( target ) )
1303 url = QUrl::fromLocalFile( target );
1307 url.setUrl( target );
1309 mrl = toURI( url.toEncoded().constData() );
1311 if( mrl.length() > 0 )
1313 Open::openMRL( p_intf, mrl, first, b_playlist );
1319 /* Browsers give content as text if you dnd the addressbar,
1320 so check if mimedata has valid url in text and use it
1321 if we didn't get any normal Urls()*/
1322 if( !mimeData->hasUrls() && mimeData->hasText() &&
1323 QUrl(mimeData->text()).isValid() )
1325 QString mrl = toURI( mimeData->text() );
1326 Open::openMRL( p_intf, mrl, first, b_playlist );
1330 void MainInterface::dragEnterEvent(QDragEnterEvent *event)
1332 event->acceptProposedAction();
1334 void MainInterface::dragMoveEvent(QDragMoveEvent *event)
1336 event->acceptProposedAction();
1338 void MainInterface::dragLeaveEvent(QDragLeaveEvent *event)
1343 /************************************************************************
1345 ************************************************************************/
1346 void MainInterface::keyPressEvent( QKeyEvent *e )
1348 handleKeyPress( e );
1350 /* easter eggs sequence handling */
1351 if ( e->key() == kc[ i_kc_offset ] )
1356 if ( i_kc_offset == (sizeof( kc ) / sizeof( Qt::Key )) )
1363 void MainInterface::handleKeyPress( QKeyEvent *e )
1365 if( ( ( e->modifiers() & Qt::ControlModifier ) && ( e->key() == Qt::Key_H ) ) ||
1366 ( b_minimalView && !b_videoFullScreen && e->key() == Qt::Key_Escape ) )
1368 toggleMinimalView( !b_minimalView );
1372 int i_vlck = qtEventToVLCKey( e );
1375 var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
1382 void MainInterface::wheelEvent( QWheelEvent *e )
1384 int i_vlckey = qtWheelEventToVLCKey( e );
1385 var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlckey );
1389 void MainInterface::closeEvent( QCloseEvent *e )
1392 emit askToQuit(); /* ask THEDP to quit, so we have a unique method */
1393 /* Accept session quit. Otherwise we break the desktop mamager. */
1397 bool MainInterface::eventFilter( QObject *obj, QEvent *event )
1399 if ( event->type() == MainInterface::ToolbarsNeedRebuild ) {
1404 return QObject::eventFilter( obj, event );
1408 void MainInterface::toolBarConfUpdated()
1410 QApplication::postEvent( this, new QEvent( MainInterface::ToolbarsNeedRebuild ) );
1413 void MainInterface::setInterfaceFullScreen( bool fs )
1416 setWindowState( windowState() | Qt::WindowFullScreen );
1418 setWindowState( windowState() & ~Qt::WindowFullScreen );
1420 void MainInterface::toggleInterfaceFullScreen()
1422 b_interfaceFullScreen = !b_interfaceFullScreen;
1423 if( !b_videoFullScreen )
1424 setInterfaceFullScreen( b_interfaceFullScreen );
1425 emit fullscreenInterfaceToggled( b_interfaceFullScreen );
1428 void MainInterface::emitBoss()
1432 void MainInterface::setBoss()
1445 void MainInterface::emitRaise()
1449 void MainInterface::setRaise()
1455 /*****************************************************************************
1456 * PopupMenuCB: callback triggered by the intf-popupmenu playlist variable.
1457 * We don't show the menu directly here because we don't want the
1458 * caller to block for a too long time.
1459 *****************************************************************************/
1460 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
1461 vlc_value_t old_val, vlc_value_t new_val, void *param )
1463 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1465 intf_thread_t *p_intf = (intf_thread_t *)param;
1467 if( p_intf->pf_show_dialog )
1469 p_intf->pf_show_dialog( p_intf, INTF_DIALOG_POPUPMENU,
1470 new_val.b_bool, NULL );
1476 /*****************************************************************************
1477 * IntfShowCB: callback triggered by the intf-toggle-fscontrol libvlc variable.
1478 *****************************************************************************/
1479 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
1480 vlc_value_t old_val, vlc_value_t new_val, void *param )
1482 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1483 VLC_UNUSED( new_val );
1485 intf_thread_t *p_intf = (intf_thread_t *)param;
1486 p_intf->p_sys->p_mi->toggleFSC();
1492 /*****************************************************************************
1493 * IntfRaiseMainCB: callback triggered by the intf-show-main libvlc variable.
1494 *****************************************************************************/
1495 static int IntfRaiseMainCB( vlc_object_t *p_this, const char *psz_variable,
1496 vlc_value_t old_val, vlc_value_t new_val, void *param )
1498 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1499 VLC_UNUSED( new_val );
1501 intf_thread_t *p_intf = (intf_thread_t *)param;
1502 p_intf->p_sys->p_mi->emitRaise();
1507 /*****************************************************************************
1508 * IntfBossCB: callback triggered by the intf-boss libvlc variable.
1509 *****************************************************************************/
1510 static int IntfBossCB( vlc_object_t *p_this, const char *psz_variable,
1511 vlc_value_t old_val, vlc_value_t new_val, void *param )
1513 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1514 VLC_UNUSED( new_val );
1516 intf_thread_t *p_intf = (intf_thread_t *)param;
1517 p_intf->p_sys->p_mi->emitBoss();