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 );
750 /* Slot to change the video always-on-top flag.
751 * Emit askVideoOnTop() to invoke this from other thread. */
752 void MainInterface::setVideoOnTop( bool on_top )
754 Qt::WindowFlags oldflags = windowFlags(), newflags;
757 newflags = oldflags | Qt::WindowStaysOnTopHint;
759 newflags = oldflags & ~Qt::WindowStaysOnTopHint;
760 if( newflags != oldflags && !b_videoFullScreen )
763 setWindowFlags( newflags );
764 show(); /* necessary to apply window flags */
768 /* Asynchronous call from WindowControl function */
769 int MainInterface::controlVideo( int i_query, va_list args )
773 case VOUT_WINDOW_SET_SIZE:
775 unsigned int i_width = va_arg( args, unsigned int );
776 unsigned int i_height = va_arg( args, unsigned int );
778 emit askVideoToResize( i_width, i_height );
781 case VOUT_WINDOW_SET_STATE:
783 unsigned i_arg = va_arg( args, unsigned );
784 unsigned on_top = i_arg & VOUT_WINDOW_STATE_ABOVE;
786 emit askVideoOnTop( on_top != 0 );
789 case VOUT_WINDOW_SET_FULLSCREEN:
791 bool b_fs = va_arg( args, int );
793 emit askVideoSetFullScreen( b_fs );
797 msg_Warn( p_intf, "unsupported control query" );
802 /*****************************************************************************
803 * Playlist, Visualisation and Menus handling
804 *****************************************************************************/
806 * Toggle the playlist widget or dialog
808 void MainInterface::createPlaylist()
810 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
814 playlistWidget = dialog->exportPlaylistWidget();
815 stackCentralW->addWidget( playlistWidget );
816 stackWidgetsSizes[playlistWidget] = settings->value( "playlistSize", QSize( 600, 300 ) ).toSize();
818 CONNECT( dialog, visibilityChanged(bool), this, setPlaylistVisibility(bool) );
821 void MainInterface::togglePlaylist()
823 if( !playlistWidget ) createPlaylist();
825 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
828 if ( dialog->hasPlaylistWidget() )
829 playlistWidget = dialog->exportPlaylistWidget();
830 /* Playlist is not visible, show it */
831 if( stackCentralW->currentWidget() != playlistWidget )
833 if( stackCentralW->indexOf( playlistWidget ) == -1 )
834 stackCentralW->addWidget( playlistWidget );
835 showTab( playlistWidget );
839 restoreStackOldWidget();
841 playlistVisible = ( stackCentralW->currentWidget() == playlistWidget );
845 playlistVisible = !playlistVisible;
846 if ( ! dialog->hasPlaylistWidget() )
847 dialog->importPlaylistWidget( playlistWidget );
848 if ( playlistVisible )
856 const Qt::Key MainInterface::kc[10] =
858 Qt::Key_Up, Qt::Key_Up,
859 Qt::Key_Down, Qt::Key_Down,
860 Qt::Key_Left, Qt::Key_Right, Qt::Key_Left, Qt::Key_Right,
864 void MainInterface::dockPlaylist( bool p_docked )
866 if( b_plDocked == p_docked ) return;
867 /* some extra check */
868 if ( b_plDocked && !playlistWidget ) createPlaylist();
870 b_plDocked = p_docked;
871 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
873 if( !p_docked ) /* Previously docked */
875 playlistVisible = playlistWidget->isVisible();
876 stackCentralW->removeWidget( playlistWidget );
877 dialog->importPlaylistWidget( playlistWidget );
878 if ( playlistVisible ) dialog->show();
879 restoreStackOldWidget();
881 else /* Previously undocked */
883 playlistVisible = dialog->isVisible();
885 playlistWidget = dialog->exportPlaylistWidget();
886 stackCentralW->addWidget( playlistWidget );
888 /* If playlist is invisible don't show it */
889 if( playlistVisible ) showTab( playlistWidget );
894 * setMinimalView is the private function used by
895 * the SLOT toggleMinimalView and setVideoFullScreen
897 void MainInterface::setMinimalView( bool b_minimal )
899 menuBar()->setVisible( !b_minimal );
900 controls->setVisible( !b_minimal );
901 statusBar()->setVisible( !b_minimal && b_statusbarVisible );
902 inputC->setVisible( !b_minimal );
906 * This public SLOT is used for moving to minimal View Mode
908 * If b_minimal is false, then we are normalView
910 void MainInterface::toggleMinimalView( bool b_minimal )
912 if( !b_minimalView && b_autoresize ) /* Normal mode */
914 if( stackCentralW->currentWidget() == bgWidget )
916 if( stackCentralW->height() < 16 )
918 resizeStack( stackCentralW->width(), 100 );
922 b_minimalView = b_minimal;
923 if( !b_videoFullScreen )
925 setMinimalView( b_minimalView );
926 computeMinimumSize();
929 emit minimalViewToggled( b_minimalView );
932 /* toggling advanced controls buttons */
933 void MainInterface::toggleAdvancedButtons()
935 controls->toggleAdvanced();
936 // if( fullscreenControls ) fullscreenControls->toggleAdvanced();
939 /* Get the visibility status of the controls (hidden or not, advanced or not) */
940 int MainInterface::getControlsVisibilityStatus()
942 if( !controls ) return 0;
943 return( (controls->isVisible() ? CONTROLS_VISIBLE : CONTROLS_HIDDEN )
944 + CONTROLS_ADVANCED * controls->b_advancedVisible );
947 StandardPLPanel *MainInterface::getPlaylistView()
949 if( !playlistWidget ) return NULL;
950 else return playlistWidget->mainView;
953 void MainInterface::setStatusBarVisibility( bool b_visible )
955 statusBar()->setVisible( b_visible );
956 b_statusbarVisible = b_visible;
957 if( controls ) controls->setGripVisible( !b_statusbarVisible );
961 void MainInterface::setPlaylistVisibility( bool b_visible )
963 if( isPlDocked() || THEDP->isDying() || (playlistWidget && playlistWidget->isMinimized() ) )
966 playlistVisible = b_visible;
970 void MainInterface::visual()
972 if( !VISIBLE( visualSelector) )
974 visualSelector->show();
975 if( !THEMIM->getIM()->hasVideo() )
977 /* Show the background widget */
979 visualSelectorEnabled = true;
983 /* Stop any currently running visualization */
984 visualSelector->hide();
985 visualSelectorEnabled = false;
990 /************************************************************************
992 ************************************************************************/
993 void MainInterface::setName( const QString& name )
995 input_name = name; /* store it for the QSystray use */
996 /* Display it in the status bar, but also as a Tooltip in case it doesn't
998 nameLabel->setText( name );
999 nameLabel->setToolTip( name );
1003 * Give the decorations of the Main Window a correct Name.
1004 * If nothing is given, set it to VLC...
1006 void MainInterface::setVLCWindowsTitle( const QString& aTitle )
1008 if( aTitle.isEmpty() )
1010 setWindowTitle( qtr( "VLC media player" ) );
1014 setWindowTitle( aTitle + " - " + qtr( "VLC media player" ) );
1018 void MainInterface::showCryptedLabel( bool b_show )
1020 if( cryptedLabel == NULL )
1022 cryptedLabel = new QLabel;
1023 // The lock icon is not the right one for DRM protection/scrambled.
1024 //cryptedLabel->setPixmap( QPixmap( ":/lock" ) );
1025 cryptedLabel->setText( "DRM" );
1026 statusBar()->addWidget( cryptedLabel );
1029 cryptedLabel->setVisible( b_show );
1032 void MainInterface::showBuffering( float f_cache )
1034 QString amount = QString("Buffering: %1%").arg( (int)(100*f_cache) );
1035 statusBar()->showMessage( amount, 1000 );
1038 /*****************************************************************************
1039 * Systray Icon and Systray Menu
1040 *****************************************************************************/
1042 * Create a SystemTray icon and a menu that would go with it.
1043 * Connects to a click handler on the icon.
1045 void MainInterface::createSystray()
1048 if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
1049 iconVLC = QIcon::fromTheme( "vlc-xmas", QIcon( ":/logo/vlc128-xmas.png" ) );
1051 iconVLC = QIcon::fromTheme( "vlc", QIcon( ":/logo/vlc256.png" ) );
1052 sysTray = new QSystemTrayIcon( iconVLC, this );
1053 sysTray->setToolTip( qtr( "VLC media player" ));
1055 systrayMenu = new QMenu( qtr( "VLC media player" ), this );
1056 systrayMenu->setIcon( iconVLC );
1058 VLCMenuBar::updateSystrayMenu( this, p_intf, true );
1061 CONNECT( sysTray, activated( QSystemTrayIcon::ActivationReason ),
1062 this, handleSystrayClick( QSystemTrayIcon::ActivationReason ) );
1064 /* Connects on nameChanged() */
1065 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
1066 this, updateSystrayTooltipName( const QString& ) );
1067 /* Connect PLAY_STATUS on the systray */
1068 CONNECT( THEMIM->getIM(), playingStatusChanged( int ),
1069 this, updateSystrayTooltipStatus( int ) );
1073 * Updates the Systray Icon's menu and toggle the main interface
1075 void MainInterface::toggleUpdateSystrayMenu()
1077 /* If hidden, show it */
1083 else if( isMinimized() )
1091 /* Visible (possibly under other windows) */
1093 /* check if any visible window is above vlc in the z-order,
1094 * but ignore the ones always on top
1095 * and the ones which can't be activated */
1098 QWindow *window = windowHandle();
1099 winId = static_cast<HWND>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("handle", window));
1101 winId = internalWinId();
1106 wi.cbSize = sizeof( WINDOWINFO );
1107 for( hwnd = GetNextWindow( winId, GW_HWNDPREV );
1108 hwnd && ( !IsWindowVisible( hwnd ) ||
1109 ( GetWindowInfo( hwnd, &wi ) &&
1110 (wi.dwExStyle&WS_EX_NOACTIVATE) ) );
1111 hwnd = GetNextWindow( hwnd, GW_HWNDPREV ) );
1112 if( !hwnd || !GetWindowInfo( hwnd, &wi ) ||
1113 (wi.dwExStyle&WS_EX_TOPMOST) )
1126 VLCMenuBar::updateSystrayMenu( this, p_intf );
1129 /* First Item of the systray menu */
1130 void MainInterface::showUpdateSystrayMenu()
1138 VLCMenuBar::updateSystrayMenu( this, p_intf );
1141 /* First Item of the systray menu */
1142 void MainInterface::hideUpdateSystrayMenu()
1145 VLCMenuBar::updateSystrayMenu( this, p_intf );
1148 /* Click on systray Icon */
1149 void MainInterface::handleSystrayClick(
1150 QSystemTrayIcon::ActivationReason reason )
1154 case QSystemTrayIcon::Trigger:
1155 case QSystemTrayIcon::DoubleClick:
1157 VLCMenuBar::updateSystrayMenu( this, p_intf );
1159 toggleUpdateSystrayMenu();
1162 case QSystemTrayIcon::MiddleClick:
1163 sysTray->showMessage( qtr( "VLC media player" ),
1164 qtr( "Control menu for the player" ),
1165 QSystemTrayIcon::Information, 3000 );
1173 * Updates the name of the systray Icon tooltip.
1174 * Doesn't check if the systray exists, check before you call it.
1176 void MainInterface::updateSystrayTooltipName( const QString& name )
1178 if( name.isEmpty() )
1180 sysTray->setToolTip( qtr( "VLC media player" ) );
1184 sysTray->setToolTip( name );
1185 if( ( i_notificationSetting == NOTIFICATION_ALWAYS ) ||
1186 ( i_notificationSetting == NOTIFICATION_MINIMIZED && (isMinimized() || isHidden()) ) )
1188 sysTray->showMessage( qtr( "VLC media player" ), name,
1189 QSystemTrayIcon::NoIcon, 3000 );
1193 VLCMenuBar::updateSystrayMenu( this, p_intf );
1197 * Updates the status of the systray Icon tooltip.
1198 * Doesn't check if the systray exists, check before you call it.
1200 void MainInterface::updateSystrayTooltipStatus( int i_status )
1205 sysTray->setToolTip( input_name );
1208 sysTray->setToolTip( input_name + " - " + qtr( "Paused") );
1211 sysTray->setToolTip( qtr( "VLC media player" ) );
1214 VLCMenuBar::updateSystrayMenu( this, p_intf );
1217 void MainInterface::changeEvent(QEvent *event)
1219 if( event->type() == QEvent::WindowStateChange )
1221 QWindowStateChangeEvent *windowStateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);
1222 Qt::WindowStates newState = windowState();
1223 Qt::WindowStates oldState = windowStateChangeEvent->oldState();
1225 if( newState & Qt::WindowMinimized )
1227 b_hasPausedWhenMinimized = false;
1229 if( THEMIM->getIM()->playingStatus() == PLAYING_S &&
1230 THEMIM->getIM()->hasVideo() && !THEMIM->getIM()->hasVisualisation() &&
1233 b_hasPausedWhenMinimized = true;
1237 else if( oldState & Qt::WindowMinimized && !( newState & Qt::WindowMinimized ) )
1239 if( b_hasPausedWhenMinimized )
1246 QWidget::changeEvent(event);
1249 /************************************************************************
1251 ************************************************************************/
1252 void MainInterface::dropEvent(QDropEvent *event)
1254 dropEventPlay( event, true );
1260 * Event called if something is dropped onto a VLC window
1261 * \param event the event in question
1262 * \param b_play whether to play the file immediately
1263 * \param b_playlist true to add to playlist, false to add to media library
1266 void MainInterface::dropEventPlay( QDropEvent *event, bool b_play, bool b_playlist )
1268 if( event->possibleActions() & ( Qt::CopyAction | Qt::MoveAction | Qt::LinkAction ) )
1269 event->setDropAction( Qt::CopyAction );
1273 const QMimeData *mimeData = event->mimeData();
1275 /* D&D of a subtitles file, add it on the fly */
1276 if( mimeData->urls().count() == 1 && THEMIM->getIM()->hasInput() )
1278 if( !input_AddSubtitleOSD( THEMIM->getInput(),
1279 qtu( toNativeSeparators( mimeData->urls()[0].toLocalFile() ) ),
1287 bool first = b_play;
1288 foreach( const QUrl &url, mimeData->urls() )
1292 QString mrl = toURI( url.toEncoded().constData() );
1293 QFileInfo info( url.toLocalFile() );
1294 if( info.exists() && info.isSymLink() )
1296 QString target = info.symLinkTarget();
1298 if( QFile::exists( target ) )
1300 url = QUrl::fromLocalFile( target );
1304 url.setUrl( target );
1306 mrl = toURI( url.toEncoded().constData() );
1308 if( mrl.length() > 0 )
1310 Open::openMRL( p_intf, mrl, first, b_playlist );
1316 /* Browsers give content as text if you dnd the addressbar,
1317 so check if mimedata has valid url in text and use it
1318 if we didn't get any normal Urls()*/
1319 if( !mimeData->hasUrls() && mimeData->hasText() &&
1320 QUrl(mimeData->text()).isValid() )
1322 QString mrl = toURI( mimeData->text() );
1323 Open::openMRL( p_intf, mrl, first, b_playlist );
1327 void MainInterface::dragEnterEvent(QDragEnterEvent *event)
1329 event->acceptProposedAction();
1331 void MainInterface::dragMoveEvent(QDragMoveEvent *event)
1333 event->acceptProposedAction();
1335 void MainInterface::dragLeaveEvent(QDragLeaveEvent *event)
1340 /************************************************************************
1342 ************************************************************************/
1343 void MainInterface::keyPressEvent( QKeyEvent *e )
1345 handleKeyPress( e );
1347 /* easter eggs sequence handling */
1348 if ( e->key() == kc[ i_kc_offset ] )
1353 if ( i_kc_offset == (sizeof( kc ) / sizeof( Qt::Key )) )
1360 void MainInterface::handleKeyPress( QKeyEvent *e )
1362 if( ( ( e->modifiers() & Qt::ControlModifier ) && ( e->key() == Qt::Key_H ) ) ||
1363 ( b_minimalView && !b_videoFullScreen && e->key() == Qt::Key_Escape ) )
1365 toggleMinimalView( !b_minimalView );
1369 int i_vlck = qtEventToVLCKey( e );
1372 var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
1379 void MainInterface::wheelEvent( QWheelEvent *e )
1381 int i_vlckey = qtWheelEventToVLCKey( e );
1382 var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlckey );
1386 void MainInterface::closeEvent( QCloseEvent *e )
1389 emit askToQuit(); /* ask THEDP to quit, so we have a unique method */
1390 /* Accept session quit. Otherwise we break the desktop mamager. */
1394 bool MainInterface::eventFilter( QObject *obj, QEvent *event )
1396 if ( event->type() == MainInterface::ToolbarsNeedRebuild ) {
1401 return QObject::eventFilter( obj, event );
1405 void MainInterface::toolBarConfUpdated()
1407 QApplication::postEvent( this, new QEvent( MainInterface::ToolbarsNeedRebuild ) );
1410 void MainInterface::setInterfaceFullScreen( bool fs )
1413 setWindowState( windowState() | Qt::WindowFullScreen );
1415 setWindowState( windowState() & ~Qt::WindowFullScreen );
1417 void MainInterface::toggleInterfaceFullScreen()
1419 b_interfaceFullScreen = !b_interfaceFullScreen;
1420 if( !b_videoFullScreen )
1421 setInterfaceFullScreen( b_interfaceFullScreen );
1422 emit fullscreenInterfaceToggled( b_interfaceFullScreen );
1425 void MainInterface::emitBoss()
1429 void MainInterface::setBoss()
1442 void MainInterface::emitRaise()
1446 void MainInterface::setRaise()
1452 /*****************************************************************************
1453 * PopupMenuCB: callback triggered by the intf-popupmenu playlist variable.
1454 * We don't show the menu directly here because we don't want the
1455 * caller to block for a too long time.
1456 *****************************************************************************/
1457 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
1458 vlc_value_t old_val, vlc_value_t new_val, void *param )
1460 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1462 intf_thread_t *p_intf = (intf_thread_t *)param;
1464 if( p_intf->pf_show_dialog )
1466 p_intf->pf_show_dialog( p_intf, INTF_DIALOG_POPUPMENU,
1467 new_val.b_bool, NULL );
1473 /*****************************************************************************
1474 * IntfShowCB: callback triggered by the intf-toggle-fscontrol libvlc variable.
1475 *****************************************************************************/
1476 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
1477 vlc_value_t old_val, vlc_value_t new_val, void *param )
1479 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1480 VLC_UNUSED( new_val );
1482 intf_thread_t *p_intf = (intf_thread_t *)param;
1483 p_intf->p_sys->p_mi->toggleFSC();
1489 /*****************************************************************************
1490 * IntfRaiseMainCB: callback triggered by the intf-show-main libvlc variable.
1491 *****************************************************************************/
1492 static int IntfRaiseMainCB( vlc_object_t *p_this, const char *psz_variable,
1493 vlc_value_t old_val, vlc_value_t new_val, void *param )
1495 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1496 VLC_UNUSED( new_val );
1498 intf_thread_t *p_intf = (intf_thread_t *)param;
1499 p_intf->p_sys->p_mi->emitRaise();
1504 /*****************************************************************************
1505 * IntfBossCB: callback triggered by the intf-boss libvlc variable.
1506 *****************************************************************************/
1507 static int IntfBossCB( vlc_object_t *p_this, const char *psz_variable,
1508 vlc_value_t old_val, vlc_value_t new_val, void *param )
1510 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1511 VLC_UNUSED( new_val );
1513 intf_thread_t *p_intf = (intf_thread_t *)param;
1514 p_intf->p_sys->p_mi->emitBoss();