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>
65 #include <vlc_keys.h> /* Wheel event */
66 #include <vlc_vout_display.h> /* vout_thread_t and VOUT_ events */
69 #if defined(_WIN32) && HAS_QT5
71 #include <qpa/qplatformnativeinterface.h>
76 /* Callback prototypes */
77 static int PopupMenuCB( 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 IntfShowCB( 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 IntfBossCB( vlc_object_t *p_this, const char *psz_variable,
82 vlc_value_t old_val, vlc_value_t new_val, void *param );
83 static int IntfRaiseMainCB( vlc_object_t *p_this, const char *psz_variable,
84 vlc_value_t old_val, vlc_value_t new_val,
87 const QEvent::Type MainInterface::ToolbarsNeedRebuild =
88 (QEvent::Type)QEvent::registerEventType();
90 MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
92 /* Variables initialisation */
95 playlistWidget = NULL;
96 stackCentralOldWidget= NULL;
98 fullscreenControls = NULL;
103 b_hideAfterCreation = false; // --qt-start-minimized
104 playlistVisible = false;
106 b_interfaceFullScreen= false;
107 b_hasPausedWhenMinimized = false;
110 /* Ask for Privacy */
111 FirstRun::CheckAndRun( this, p_intf );
114 * Configuration and settings
115 * Pre-building of interface
118 setFocusPolicy( Qt::StrongFocus );
119 setAcceptDrops( true );
120 setWindowRole( "vlc-main" );
121 setWindowIcon( QApplication::windowIcon() );
122 setWindowOpacity( var_InheritFloat( p_intf, "qt-opacity" ) );
124 setAttribute( Qt::WA_MacBrushedMetal );
127 /* Is video in embedded in the UI or not */
128 b_videoEmbedded = var_InheritBool( p_intf, "embedded-video" );
130 /* Does the interface resize to video size or the opposite */
131 b_autoresize = var_InheritBool( p_intf, "qt-video-autoresize" );
133 /* Are we in the enhanced always-video mode or not ? */
134 b_minimalView = var_InheritBool( p_intf, "qt-minimal-view" );
136 /* Do we want anoying popups or not */
137 i_notificationSetting = var_InheritInteger( p_intf, "qt-notification" );
140 b_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );
142 /* Set the other interface settings */
143 settings = getSettings();
147 p_intf->p_sys->disable_volume_keys = var_InheritBool( p_intf, "qt-disable-volume-keys" );
151 b_plDocked = getSettings()->value( "MainWindow/pl-dock-status", true ).toBool();
154 /**************************
155 * UI and Widgets design
156 **************************/
157 setVLCWindowsTitle();
162 VLCMenuBar::createMenuBar( this, p_intf );
163 CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ),
164 THEDP, destroyPopupMenu() );
166 createMainWidget( settings );
172 setStatusBarVisibility( getSettings()->value( "MainWindow/status-bar-visible", false ).toBool() );
177 taskbar_wmsg = RegisterWindowMessage(TEXT("TaskbarButtonCreated"));
180 /*********************************
181 * Create the Systray Management *
182 *********************************/
185 /*************************************************************
186 * Connect the input manager to the GUI elements it manages *
187 * Beware initSystray did some connects on input manager too *
188 *************************************************************/
190 * Connects on nameChanged()
191 * Those connects are different because options can impeach them to trigger.
193 /* Main Interface statusbar */
194 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
195 this, setName( const QString& ) );
196 /* and title of the Main Interface*/
197 if( var_InheritBool( p_intf, "qt-name-in-title" ) )
199 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
200 this, setVLCWindowsTitle( const QString& ) );
202 /* END CONNECTS ON IM */
204 /* VideoWidget connects for asynchronous calls */
205 b_videoFullScreen = false;
206 connect( this, SIGNAL(askGetVideo(WId*,struct vout_window_t*,unsigned*,unsigned *)),
207 this, SLOT(getVideoSlot(WId*,struct vout_window_t*,unsigned*,unsigned*)),
208 Qt::BlockingQueuedConnection );
209 connect( this, SIGNAL(askReleaseVideo( void )),
210 this, SLOT(releaseVideoSlot( void )),
211 Qt::BlockingQueuedConnection );
212 CONNECT( this, askVideoOnTop(bool), this, setVideoOnTop(bool));
218 CONNECT( this, askVideoToResize( unsigned int, unsigned int ),
219 this, setVideoSize( unsigned int, unsigned int ) );
220 CONNECT( videoWidget, sizeChanged( int, int ),
221 this, videoSizeChanged( int, int ) );
223 CONNECT( this, askVideoSetFullScreen( bool ),
224 this, setVideoFullScreen( bool ) );
227 CONNECT( THEDP, toolBarConfUpdated(), this, toolBarConfUpdated() );
228 installEventFilter( this );
230 CONNECT( this, askToQuit(), THEDP, quit() );
232 CONNECT( this, askBoss(), this, setBoss() );
233 CONNECT( this, askRaise(), this, setRaise() );
235 /** END of CONNECTS**/
241 var_AddCallback( p_intf->p_libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
242 var_AddCallback( p_intf->p_libvlc, "intf-boss", IntfBossCB, p_intf );
243 var_AddCallback( p_intf->p_libvlc, "intf-show", IntfRaiseMainCB, p_intf );
245 /* Register callback for the intf-popupmenu variable */
246 var_AddCallback( p_intf->p_libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
249 /* Final Sizing, restoration and placement of the interface */
250 if( settings->value( "MainWindow/playlist-visible", false ).toBool() )
253 QVLCTools::restoreWidgetPosition( settings, this, QSize(600, 420) );
255 b_interfaceFullScreen = isFullScreen();
257 setVisible( !b_hideAfterCreation );
259 /* Switch to minimal view if needed, must be called after the show() */
261 toggleMinimalView( true );
263 computeMinimumSize();
266 MainInterface::~MainInterface()
268 /* Unsure we hide the videoWidget before destroying it */
269 if( stackCentralOldWidget == videoWidget )
277 ImageList_Destroy( himl );
283 /* Be sure to kill the actionsManager... Only used in the MI and control */
284 ActionsManager::killInstance();
286 /* Delete the FSC controller */
287 delete fullscreenControls;
291 settings->beginGroup("MainWindow");
292 settings->setValue( "pl-dock-status", b_plDocked );
294 /* Save playlist state */
295 settings->setValue( "playlist-visible", playlistVisible );
297 settings->setValue( "adv-controls",
298 getControlsVisibilityStatus() & CONTROLS_ADVANCED );
299 settings->setValue( "status-bar-visible", b_statusbarVisible );
301 /* Save the stackCentralW sizes */
302 settings->setValue( "bgSize", stackWidgetsSizes[bgWidget] );
303 settings->setValue( "playlistSize", stackWidgetsSizes[playlistWidget] );
304 settings->endGroup();
307 QVLCTools::saveWidgetPosition(settings, this);
309 /* Unregister callbacks */
310 var_DelCallback( p_intf->p_libvlc, "intf-boss", IntfBossCB, p_intf );
311 var_DelCallback( p_intf->p_libvlc, "intf-show", IntfRaiseMainCB, p_intf );
312 var_DelCallback( p_intf->p_libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
313 var_DelCallback( p_intf->p_libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
315 p_intf->p_sys->p_mi = NULL;
318 void MainInterface::computeMinimumSize()
321 if( menuBar()->isVisible() )
322 minWidth += controls->sizeHint().width();
324 setMinimumWidth( minWidth );
327 /*****************************
329 *****************************/
330 void MainInterface::recreateToolbars()
332 bool b_adv = getControlsVisibilityStatus() & CONTROLS_ADVANCED;
337 controls = new ControlsWidget( p_intf, b_adv, this );
338 inputC = new InputControlsWidget( p_intf, this );
339 mainLayout->insertWidget( 2, inputC );
340 mainLayout->insertWidget( settings->value( "MainWindow/ToolbarPos", 0 ).toInt() ? 0: 3,
343 if( fullscreenControls )
345 delete fullscreenControls;
346 fullscreenControls = new FullscreenControllerWidget( p_intf, this );
347 CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
348 this, handleKeyPress( QKeyEvent * ) );
349 THEMIM->requestVoutUpdate();
352 setMinimalView( b_minimalView );
355 void MainInterface::reloadPrefs()
357 i_notificationSetting = var_InheritInteger( p_intf, "qt-notification" );
358 b_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );
360 p_intf->p_sys->disable_volume_keys = var_InheritBool( p_intf, "qt-disable-volume-keys" );
362 if( !var_InheritBool( p_intf, "qt-fs-controller" ) && fullscreenControls )
364 delete fullscreenControls;
365 fullscreenControls = NULL;
369 void MainInterface::createResumePanel( QWidget *w )
371 resumePanel = new QWidget( w );
373 QHBoxLayout *resumePanelLayout = new QHBoxLayout( resumePanel );
374 resumePanelLayout->setSpacing( 0 ); resumePanelLayout->setMargin( 0 );
376 QLabel *continuePixmapLabel = new QLabel();
377 continuePixmapLabel->setPixmap( QPixmap( ":/menu/help" ) );
378 continuePixmapLabel->setContentsMargins( 5, 0, 5, 0 );
380 QLabel *continueLabel = new QLabel( qtr( "Do you want to restart the playback where left off?") );
382 QToolButton *cancel = new QToolButton( resumePanel );
383 cancel->setAutoRaise( true );
384 cancel->setText( "X" );
386 QPushButton *ok = new QPushButton( qtr( "&Continue" ) );
388 resumePanelLayout->addWidget( continuePixmapLabel );
389 resumePanelLayout->addWidget( continueLabel );
390 resumePanelLayout->addStretch( 1 );
391 resumePanelLayout->addWidget( ok );
392 resumePanelLayout->addWidget( cancel );
394 resumeTimer = new QTimer( resumePanel );
395 resumeTimer->setSingleShot( true );
396 resumeTimer->setInterval( 6000 );
398 CONNECT( resumeTimer, timeout(), this, hideResumePanel() );
399 CONNECT( cancel, clicked(), this, hideResumePanel() );
400 CONNECT( THEMIM->getIM(), resumePlayback(int64_t), this, showResumePanel(int64_t) );
401 BUTTONACT( ok, resumePlayback() );
403 w->layout()->addWidget( resumePanel );
406 void MainInterface::showResumePanel( int64_t _time ) {
407 int setting = var_InheritInteger( p_intf, "qt-continue" );
412 i_resumeTime = _time;
418 resumePanel->setVisible(true);
419 resumeTimer->start();
423 void MainInterface::hideResumePanel()
425 if( resumePanel->isVisible() )
427 if( !isFullScreen() && !isMaximized() )
428 resize( width(), height() - resumePanel->height() );
434 void MainInterface::resumePlayback()
436 if( THEMIM->getIM()->hasInput() ) {
437 var_SetTime( THEMIM->getInput(), "time", i_resumeTime );
442 void MainInterface::createMainWidget( QSettings *creationSettings )
444 /* Create the main Widget and the mainLayout */
445 QWidget *main = new QWidget;
446 setCentralWidget( main );
447 mainLayout = new QVBoxLayout( main );
448 main->setContentsMargins( 0, 0, 0, 0 );
449 mainLayout->setSpacing( 0 ); mainLayout->setMargin( 0 );
451 createResumePanel( main );
453 stackCentralW = new QVLCStackedWidget( main );
456 if ( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY
457 && var_InheritBool( p_intf, "qt-icon-change" ) )
459 bgWidget = new EasterEggBackgroundWidget( p_intf );
460 CONNECT( this, kc_pressed(), bgWidget, animate() );
463 bgWidget = new BackgroundWidget( p_intf );
465 stackCentralW->addWidget( bgWidget );
466 if ( !var_InheritBool( p_intf, "qt-bgcone" ) )
467 bgWidget->setWithArt( false );
469 if ( var_InheritBool( p_intf, "qt-bgcone-expands" ) )
470 bgWidget->setExpandstoHeight( true );
472 /* And video Outputs */
473 if( b_videoEmbedded )
475 videoWidget = new VideoWidget( p_intf );
476 stackCentralW->addWidget( videoWidget );
478 mainLayout->insertWidget( 1, stackCentralW );
480 stackWidgetsSizes[bgWidget] =
481 creationSettings->value( "MainWindow/bgSize", QSize( 600, 0 ) ).toSize();
482 /* Resize even if no-auto-resize, because we are at creation */
483 resizeStack( stackWidgetsSizes[bgWidget].width(), stackWidgetsSizes[bgWidget].height() );
485 /* Create the CONTROLS Widget */
486 controls = new ControlsWidget( p_intf,
487 creationSettings->value( "MainWindow/adv-controls", false ).toBool(), this );
488 inputC = new InputControlsWidget( p_intf, this );
490 mainLayout->insertWidget( 2, inputC );
491 mainLayout->insertWidget(
492 creationSettings->value( "MainWindow/ToolbarPos", 0 ).toInt() ? 0: 3,
495 /* Visualisation, disabled for now, they SUCK */
497 visualSelector = new VisualSelector( p_intf );
498 mainLayout->insertWidget( 0, visualSelector );
499 visualSelector->hide();
503 /* Enable the popup menu in the MI */
504 main->setContextMenuPolicy( Qt::CustomContextMenu );
505 CONNECT( main, customContextMenuRequested( const QPoint& ),
506 THEDP, setPopupMenu() );
508 if ( depth() > 8 ) /* 8bit depth has too many issues with opacity */
509 /* Create the FULLSCREEN CONTROLS Widget */
510 if( var_InheritBool( p_intf, "qt-fs-controller" ) )
512 fullscreenControls = new FullscreenControllerWidget( p_intf, this );
513 CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
514 this, handleKeyPress( QKeyEvent * ) );
518 inline void MainInterface::initSystray()
520 bool b_systrayAvailable = QSystemTrayIcon::isSystemTrayAvailable();
521 bool b_systrayWanted = var_InheritBool( p_intf, "qt-system-tray" );
523 if( var_InheritBool( p_intf, "qt-start-minimized") )
525 if( b_systrayAvailable )
527 b_systrayWanted = true;
528 b_hideAfterCreation = true;
531 msg_Err( p_intf, "cannot start minimized without system tray bar" );
534 if( b_systrayAvailable && b_systrayWanted )
538 inline void MainInterface::createStatusBar()
543 /* Widgets Creation*/
544 QStatusBar *statusBarr = statusBar();
546 TimeLabel *timeLabel = new TimeLabel( p_intf );
547 nameLabel = new ClickableQLabel();
548 nameLabel->setTextInteractionFlags( Qt::TextSelectableByMouse
549 | Qt::TextSelectableByKeyboard );
550 SpeedLabel *speedLabel = new SpeedLabel( p_intf, this );
552 /* Styling those labels */
553 timeLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
554 speedLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
555 nameLabel->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel);
556 timeLabel->setStyleSheet(
557 "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
558 speedLabel->setStyleSheet(
559 "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
560 /* pad both label and its tooltip */
561 nameLabel->setStyleSheet( "padding-left: 5px; padding-right: 5px;" );
563 /* and adding those */
564 statusBarr->addWidget( nameLabel, 8 );
565 statusBarr->addPermanentWidget( speedLabel, 0 );
566 statusBarr->addPermanentWidget( timeLabel, 0 );
568 CONNECT( nameLabel, doubleClicked(), THEDP, epgDialog() );
569 /* timeLabel behaviour:
570 - double clicking opens the goto time dialog
571 - right-clicking and clicking just toggle between remaining and
573 CONNECT( timeLabel, doubleClicked(), THEDP, gotoTimeDialog() );
575 CONNECT( THEMIM->getIM(), encryptionChanged( bool ),
576 this, showCryptedLabel( bool ) );
578 CONNECT( THEMIM->getIM(), seekRequested( float ),
579 timeLabel, setDisplayPosition( float ) );
581 /* This shouldn't be necessary, but for somehow reason, the statusBarr
582 starts at height of 20px and when a text is shown it needs more space.
583 But, as the QMainWindow policy doesn't allow statusBar to change QMW's
584 geometry, we need to force a height. If you have a better idea, please
587 statusBarr->setFixedHeight( statusBarr->sizeHint().height() + 2 );
590 /**********************************************************************
591 * Handling of sizing of the components
592 **********************************************************************/
594 void MainInterface::debug()
598 msg_Dbg( p_intf, "Controls size: %i - %i", controls->size().height(), controls->size().width() );
599 msg_Dbg( p_intf, "Controls minimumsize: %i - %i", controls->minimumSize().height(), controls->minimumSize().width() );
600 msg_Dbg( p_intf, "Controls sizeHint: %i - %i", controls->sizeHint().height(), controls->sizeHint().width() );
603 msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
604 msg_Dbg( p_intf, "sizeHint: %i - %i", sizeHint().height(), sizeHint().width() );
605 msg_Dbg( p_intf, "minimumsize: %i - %i", minimumSize().height(), minimumSize().width() );
607 msg_Dbg( p_intf, "Stack size: %i - %i", stackCentralW->size().height(), stackCentralW->size().width() );
608 msg_Dbg( p_intf, "Stack sizeHint: %i - %i", stackCentralW->sizeHint().height(), stackCentralW->sizeHint().width() );
609 msg_Dbg( p_intf, "Central size: %i - %i", centralWidget()->size().height(), centralWidget()->size().width() );
613 inline void MainInterface::showVideo() { showTab( videoWidget ); setRaise(); }
614 inline void MainInterface::restoreStackOldWidget()
615 { showTab( stackCentralOldWidget ); }
617 inline void MainInterface::showTab( QWidget *widget )
619 if ( !widget ) widget = bgWidget; /* trying to restore a null oldwidget */
621 if ( stackCentralOldWidget )
622 msg_Dbg( p_intf, "Old stackCentralOldWidget %s at index %i",
623 stackCentralOldWidget->metaObject()->className(),
624 stackCentralW->indexOf( stackCentralOldWidget ) );
625 msg_Dbg( p_intf, "ShowTab request for %s", widget->metaObject()->className() );
627 /* fixing when the playlist has been undocked after being hidden.
628 restoreStackOldWidget() is called when video stops but
629 stackCentralOldWidget would still be pointing to playlist */
630 if ( widget == playlistWidget && !isPlDocked() )
633 stackCentralOldWidget = stackCentralW->currentWidget();
634 stackWidgetsSizes[stackCentralOldWidget] = stackCentralW->size();
636 /* If we are playing video, embedded */
637 if( videoWidget && THEMIM->getIM()->hasVideo() )
639 /* Video -> Playlist */
640 if( videoWidget == stackCentralOldWidget && widget == playlistWidget )
642 stackCentralW->removeWidget( videoWidget );
643 videoWidget->show(); videoWidget->raise();
646 /* Playlist -> Video */
647 if( playlistWidget == stackCentralOldWidget && widget == videoWidget )
649 playlistWidget->artContainer->removeWidget( videoWidget );
650 videoWidget->show(); videoWidget->raise();
651 stackCentralW->addWidget( videoWidget );
654 /* Embedded playlist -> Non-embedded playlist */
655 if( bgWidget == stackCentralOldWidget && widget == videoWidget )
657 /* In rare case when video is started before the interface */
658 if( playlistWidget != NULL )
659 playlistWidget->artContainer->removeWidget( videoWidget );
660 videoWidget->show(); videoWidget->raise();
661 stackCentralW->addWidget( videoWidget );
662 stackCentralW->setCurrentWidget( videoWidget );
666 stackCentralW->setCurrentWidget( widget );
668 resizeStack( stackWidgetsSizes[widget].width(), stackWidgetsSizes[widget].height() );
671 msg_Dbg( p_intf, "Stack state changed to %s, index %i",
672 stackCentralW->currentWidget()->metaObject()->className(),
673 stackCentralW->currentIndex() );
674 msg_Dbg( p_intf, "New stackCentralOldWidget %s at index %i",
675 stackCentralOldWidget->metaObject()->className(),
676 stackCentralW->indexOf( stackCentralOldWidget ) );
679 /* This part is done later, to account for the new pl size */
680 if( videoWidget && THEMIM->getIM()->hasVideo() &&
681 videoWidget == stackCentralOldWidget && widget == playlistWidget )
683 playlistWidget->artContainer->addWidget( videoWidget );
684 playlistWidget->artContainer->setCurrentWidget( videoWidget );
688 void MainInterface::toggleFSC()
690 if( !fullscreenControls ) return;
692 IMEvent *eShow = new IMEvent( IMEvent::FullscreenControlToggle );
693 QApplication::postEvent( fullscreenControls, eShow );
696 /****************************************************************************
698 ****************************************************************************/
702 * You must not change the state of this object or other Qt UI objects,
703 * from the video output thread - only from the Qt UI main loop thread.
704 * All window provider queries must be handled through signals or events.
705 * That's why we have all those emit statements...
707 WId MainInterface::getVideo( struct vout_window_t *p_wnd,
708 unsigned int *pi_width, unsigned int *pi_height )
713 /* This is a blocking call signal. Results are returned through pointers.
714 * Beware of deadlocks! */
716 emit askGetVideo( &id, p_wnd, pi_width, pi_height );
720 void MainInterface::getVideoSlot( WId *p_id, struct vout_window_t *p_wnd,
721 unsigned *pi_width, unsigned *pi_height )
723 /* Hidden or minimized, activate */
724 if( isHidden() || isMinimized() )
725 toggleUpdateSystrayMenu();
727 /* Request the videoWidget */
728 WId ret = videoWidget->request( p_wnd, pi_width, pi_height, !b_autoresize );
730 if( ret ) /* The videoWidget is available */
732 /* Consider the video active now */
735 /* Ask videoWidget to resize correctly, if we are in normal mode */
736 if( !isFullScreen() && !isMaximized() && b_autoresize )
737 videoWidget->SetSizing( *pi_width, *pi_height );
741 /* Asynchronous call from the WindowClose function */
742 void MainInterface::releaseVideo( void )
744 emit askReleaseVideo();
747 /* Function that is CONNECTED to the previous emit */
748 void MainInterface::releaseVideoSlot( void )
750 /* This function is called when the embedded video window is destroyed,
751 * or in the rare case that the embedded window is still here but the
752 * Qt interface exits. */
753 assert( videoWidget );
754 videoWidget->release();
755 setVideoOnTop( false );
756 setVideoFullScreen( false );
759 if( stackCentralW->currentWidget() == videoWidget )
760 restoreStackOldWidget();
761 else if( playlistWidget &&
762 playlistWidget->artContainer->currentWidget() == videoWidget )
764 playlistWidget->artContainer->setCurrentIndex( 0 );
765 stackCentralW->addWidget( videoWidget );
768 /* We don't want to have a blank video to popup */
769 stackCentralOldWidget = bgWidget;
772 void MainInterface::setVideoSize( unsigned int w, unsigned int h )
774 if( !isFullScreen() && !isMaximized() )
775 videoWidget->SetSizing( w, h );
778 void MainInterface::videoSizeChanged( int w, int h )
780 if( !playlistWidget || playlistWidget->artContainer->currentWidget() != videoWidget )
784 void MainInterface::setVideoFullScreen( bool fs )
786 b_videoFullScreen = fs;
789 int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
790 /* if user hasn't defined screennumber, or screennumber that is bigger
791 * than current number of screens, take screennumber where current interface
794 if( numscreen == -1 || numscreen > QApplication::desktop()->numScreens() )
795 numscreen = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
797 QRect screenres = QApplication::desktop()->screenGeometry( numscreen );
799 /* To be sure window is on proper-screen in xinerama */
800 if( !screenres.contains( pos() ) )
802 msg_Dbg( p_intf, "Moving video to correct screen");
803 move( QPoint( screenres.x(), screenres.y() ) );
807 if( playlistWidget != NULL && playlistWidget->artContainer->currentWidget() == videoWidget )
809 showTab( videoWidget );
813 setMinimalView( true );
814 setInterfaceFullScreen( true );
818 /* TODO do we want to restore screen and position ? (when
819 * qt-fullscreen-screennumber is forced) */
820 setMinimalView( b_minimalView );
821 setInterfaceFullScreen( b_interfaceFullScreen );
823 changeThumbbarButtons( THEMIM->getIM()->playingStatus() );
829 /* Slot to change the video always-on-top flag.
830 * Emit askVideoOnTop() to invoke this from other thread. */
831 void MainInterface::setVideoOnTop( bool on_top )
833 Qt::WindowFlags oldflags = windowFlags(), newflags;
836 newflags = oldflags | Qt::WindowStaysOnTopHint;
838 newflags = oldflags & ~Qt::WindowStaysOnTopHint;
839 if( newflags != oldflags && !b_videoFullScreen )
842 setWindowFlags( newflags );
843 show(); /* necessary to apply window flags */
847 /* Asynchronous call from WindowControl function */
848 int MainInterface::controlVideo( int i_query, va_list args )
852 case VOUT_WINDOW_SET_SIZE:
854 unsigned int i_width = va_arg( args, unsigned int );
855 unsigned int i_height = va_arg( args, unsigned int );
857 emit askVideoToResize( i_width, i_height );
860 case VOUT_WINDOW_SET_STATE:
862 unsigned i_arg = va_arg( args, unsigned );
863 unsigned on_top = i_arg & VOUT_WINDOW_STATE_ABOVE;
865 emit askVideoOnTop( on_top != 0 );
868 case VOUT_WINDOW_SET_FULLSCREEN:
870 bool b_fs = va_arg( args, int );
872 emit askVideoSetFullScreen( b_fs );
876 msg_Warn( p_intf, "unsupported control query" );
881 /*****************************************************************************
882 * Playlist, Visualisation and Menus handling
883 *****************************************************************************/
885 * Toggle the playlist widget or dialog
887 void MainInterface::createPlaylist()
889 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
893 playlistWidget = dialog->exportPlaylistWidget();
894 stackCentralW->addWidget( playlistWidget );
895 stackWidgetsSizes[playlistWidget] = settings->value( "playlistSize", QSize( 600, 300 ) ).toSize();
897 CONNECT( dialog, visibilityChanged(bool), this, setPlaylistVisibility(bool) );
900 void MainInterface::togglePlaylist()
902 if( !playlistWidget ) createPlaylist();
904 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
907 if ( dialog->hasPlaylistWidget() )
908 playlistWidget = dialog->exportPlaylistWidget();
909 /* Playlist is not visible, show it */
910 if( stackCentralW->currentWidget() != playlistWidget )
912 if( stackCentralW->indexOf( playlistWidget ) == -1 )
913 stackCentralW->addWidget( playlistWidget );
914 showTab( playlistWidget );
918 restoreStackOldWidget();
920 playlistVisible = ( stackCentralW->currentWidget() == playlistWidget );
924 playlistVisible = !playlistVisible;
925 if ( ! dialog->hasPlaylistWidget() )
926 dialog->importPlaylistWidget( playlistWidget );
927 if ( playlistVisible )
935 const Qt::Key MainInterface::kc[10] =
937 Qt::Key_Up, Qt::Key_Up,
938 Qt::Key_Down, Qt::Key_Down,
939 Qt::Key_Left, Qt::Key_Right, Qt::Key_Left, Qt::Key_Right,
943 void MainInterface::dockPlaylist( bool p_docked )
945 if( b_plDocked == p_docked ) return;
946 /* some extra check */
947 if ( b_plDocked && !playlistWidget ) createPlaylist();
949 b_plDocked = p_docked;
950 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
952 if( !p_docked ) /* Previously docked */
954 playlistVisible = playlistWidget->isVisible();
955 stackCentralW->removeWidget( playlistWidget );
956 dialog->importPlaylistWidget( playlistWidget );
957 if ( playlistVisible ) dialog->show();
958 restoreStackOldWidget();
960 else /* Previously undocked */
962 playlistVisible = dialog->isVisible();
964 playlistWidget = dialog->exportPlaylistWidget();
965 stackCentralW->addWidget( playlistWidget );
967 /* If playlist is invisible don't show it */
968 if( playlistVisible ) showTab( playlistWidget );
973 * setMinimalView is the private function used by
974 * the SLOT toggleMinimalView and setVideoFullScreen
976 void MainInterface::setMinimalView( bool b_minimal )
978 menuBar()->setVisible( !b_minimal );
979 controls->setVisible( !b_minimal );
980 statusBar()->setVisible( !b_minimal && b_statusbarVisible );
981 inputC->setVisible( !b_minimal );
985 * This public SLOT is used for moving to minimal View Mode
987 * If b_minimal is false, then we are normalView
989 void MainInterface::toggleMinimalView( bool b_minimal )
991 if( !b_minimalView && b_autoresize ) /* Normal mode */
993 if( stackCentralW->currentWidget() == bgWidget )
995 if( stackCentralW->height() < 16 )
997 resizeStack( stackCentralW->width(), 100 );
1001 b_minimalView = b_minimal;
1002 if( !b_videoFullScreen )
1004 setMinimalView( b_minimalView );
1005 computeMinimumSize();
1008 emit minimalViewToggled( b_minimalView );
1011 /* toggling advanced controls buttons */
1012 void MainInterface::toggleAdvancedButtons()
1014 controls->toggleAdvanced();
1015 // if( fullscreenControls ) fullscreenControls->toggleAdvanced();
1018 /* Get the visibility status of the controls (hidden or not, advanced or not) */
1019 int MainInterface::getControlsVisibilityStatus()
1021 if( !controls ) return 0;
1022 return( (controls->isVisible() ? CONTROLS_VISIBLE : CONTROLS_HIDDEN )
1023 + CONTROLS_ADVANCED * controls->b_advancedVisible );
1026 StandardPLPanel *MainInterface::getPlaylistView()
1028 if( !playlistWidget ) return NULL;
1029 else return playlistWidget->mainView;
1032 void MainInterface::setStatusBarVisibility( bool b_visible )
1034 statusBar()->setVisible( b_visible );
1035 b_statusbarVisible = b_visible;
1036 if( controls ) controls->setGripVisible( !b_statusbarVisible );
1040 void MainInterface::setPlaylistVisibility( bool b_visible )
1042 if( isPlDocked() || THEDP->isDying() || (playlistWidget && playlistWidget->isMinimized() ) )
1045 playlistVisible = b_visible;
1049 void MainInterface::visual()
1051 if( !VISIBLE( visualSelector) )
1053 visualSelector->show();
1054 if( !THEMIM->getIM()->hasVideo() )
1056 /* Show the background widget */
1058 visualSelectorEnabled = true;
1062 /* Stop any currently running visualization */
1063 visualSelector->hide();
1064 visualSelectorEnabled = false;
1069 /************************************************************************
1071 ************************************************************************/
1072 void MainInterface::setName( const QString& name )
1074 input_name = name; /* store it for the QSystray use */
1075 /* Display it in the status bar, but also as a Tooltip in case it doesn't
1077 nameLabel->setText( name );
1078 nameLabel->setToolTip( name );
1082 * Give the decorations of the Main Window a correct Name.
1083 * If nothing is given, set it to VLC...
1085 void MainInterface::setVLCWindowsTitle( const QString& aTitle )
1087 if( aTitle.isEmpty() )
1089 setWindowTitle( qtr( "VLC media player" ) );
1093 setWindowTitle( aTitle + " - " + qtr( "VLC media player" ) );
1097 void MainInterface::showCryptedLabel( bool b_show )
1099 if( cryptedLabel == NULL )
1101 cryptedLabel = new QLabel;
1102 // The lock icon is not the right one for DRM protection/scrambled.
1103 //cryptedLabel->setPixmap( QPixmap( ":/lock" ) );
1104 cryptedLabel->setText( "DRM" );
1105 statusBar()->addWidget( cryptedLabel );
1108 cryptedLabel->setVisible( b_show );
1111 void MainInterface::showBuffering( float f_cache )
1113 QString amount = QString("Buffering: %1%").arg( (int)(100*f_cache) );
1114 statusBar()->showMessage( amount, 1000 );
1117 /*****************************************************************************
1118 * Systray Icon and Systray Menu
1119 *****************************************************************************/
1121 * Create a SystemTray icon and a menu that would go with it.
1122 * Connects to a click handler on the icon.
1124 void MainInterface::createSystray()
1127 if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
1128 iconVLC = QIcon::fromTheme( "vlc-xmas", QIcon( ":/logo/vlc128-xmas.png" ) );
1130 iconVLC = QIcon::fromTheme( "vlc", QIcon( ":/logo/vlc256.png" ) );
1131 sysTray = new QSystemTrayIcon( iconVLC, this );
1132 sysTray->setToolTip( qtr( "VLC media player" ));
1134 systrayMenu = new QMenu( qtr( "VLC media player" ), this );
1135 systrayMenu->setIcon( iconVLC );
1137 VLCMenuBar::updateSystrayMenu( this, p_intf, true );
1140 CONNECT( sysTray, activated( QSystemTrayIcon::ActivationReason ),
1141 this, handleSystrayClick( QSystemTrayIcon::ActivationReason ) );
1143 /* Connects on nameChanged() */
1144 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
1145 this, updateSystrayTooltipName( const QString& ) );
1146 /* Connect PLAY_STATUS on the systray */
1147 CONNECT( THEMIM->getIM(), playingStatusChanged( int ),
1148 this, updateSystrayTooltipStatus( int ) );
1152 * Updates the Systray Icon's menu and toggle the main interface
1154 void MainInterface::toggleUpdateSystrayMenu()
1156 /* If hidden, show it */
1162 else if( isMinimized() )
1170 /* Visible (possibly under other windows) */
1172 /* check if any visible window is above vlc in the z-order,
1173 * but ignore the ones always on top
1174 * and the ones which can't be activated */
1177 QWindow *window = windowHandle();
1178 winId = static_cast<HWND>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("handle", window));
1180 winId = internalWinId();
1185 wi.cbSize = sizeof( WINDOWINFO );
1186 for( hwnd = GetNextWindow( winId, GW_HWNDPREV );
1187 hwnd && ( !IsWindowVisible( hwnd ) ||
1188 ( GetWindowInfo( hwnd, &wi ) &&
1189 (wi.dwExStyle&WS_EX_NOACTIVATE) ) );
1190 hwnd = GetNextWindow( hwnd, GW_HWNDPREV ) );
1191 if( !hwnd || !GetWindowInfo( hwnd, &wi ) ||
1192 (wi.dwExStyle&WS_EX_TOPMOST) )
1205 VLCMenuBar::updateSystrayMenu( this, p_intf );
1208 /* First Item of the systray menu */
1209 void MainInterface::showUpdateSystrayMenu()
1217 VLCMenuBar::updateSystrayMenu( this, p_intf );
1220 /* First Item of the systray menu */
1221 void MainInterface::hideUpdateSystrayMenu()
1224 VLCMenuBar::updateSystrayMenu( this, p_intf );
1227 /* Click on systray Icon */
1228 void MainInterface::handleSystrayClick(
1229 QSystemTrayIcon::ActivationReason reason )
1233 case QSystemTrayIcon::Trigger:
1234 case QSystemTrayIcon::DoubleClick:
1236 VLCMenuBar::updateSystrayMenu( this, p_intf );
1238 toggleUpdateSystrayMenu();
1241 case QSystemTrayIcon::MiddleClick:
1242 sysTray->showMessage( qtr( "VLC media player" ),
1243 qtr( "Control menu for the player" ),
1244 QSystemTrayIcon::Information, 3000 );
1252 * Updates the name of the systray Icon tooltip.
1253 * Doesn't check if the systray exists, check before you call it.
1255 void MainInterface::updateSystrayTooltipName( const QString& name )
1257 if( name.isEmpty() )
1259 sysTray->setToolTip( qtr( "VLC media player" ) );
1263 sysTray->setToolTip( name );
1264 if( ( i_notificationSetting == NOTIFICATION_ALWAYS ) ||
1265 ( i_notificationSetting == NOTIFICATION_MINIMIZED && (isMinimized() || isHidden()) ) )
1267 sysTray->showMessage( qtr( "VLC media player" ), name,
1268 QSystemTrayIcon::NoIcon, 3000 );
1272 VLCMenuBar::updateSystrayMenu( this, p_intf );
1276 * Updates the status of the systray Icon tooltip.
1277 * Doesn't check if the systray exists, check before you call it.
1279 void MainInterface::updateSystrayTooltipStatus( int i_status )
1284 sysTray->setToolTip( input_name );
1287 sysTray->setToolTip( input_name + " - " + qtr( "Paused") );
1290 sysTray->setToolTip( qtr( "VLC media player" ) );
1293 VLCMenuBar::updateSystrayMenu( this, p_intf );
1296 void MainInterface::changeEvent(QEvent *event)
1298 if( event->type() == QEvent::WindowStateChange )
1300 QWindowStateChangeEvent *windowStateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);
1301 Qt::WindowStates newState = windowState();
1302 Qt::WindowStates oldState = windowStateChangeEvent->oldState();
1304 if( newState & Qt::WindowMinimized )
1306 b_hasPausedWhenMinimized = false;
1308 if( THEMIM->getIM()->playingStatus() == PLAYING_S &&
1309 THEMIM->getIM()->hasVideo() && !THEMIM->getIM()->hasVisualisation() &&
1312 b_hasPausedWhenMinimized = true;
1316 else if( oldState & Qt::WindowMinimized && !( newState & Qt::WindowMinimized ) )
1318 if( b_hasPausedWhenMinimized )
1325 QWidget::changeEvent(event);
1328 /************************************************************************
1330 ************************************************************************/
1331 void MainInterface::dropEvent(QDropEvent *event)
1333 dropEventPlay( event, true );
1339 * Event called if something is dropped onto a VLC window
1340 * \param event the event in question
1341 * \param b_play whether to play the file immediately
1342 * \param b_playlist true to add to playlist, false to add to media library
1345 void MainInterface::dropEventPlay( QDropEvent *event, bool b_play, bool b_playlist )
1347 if( event->possibleActions() & ( Qt::CopyAction | Qt::MoveAction | Qt::LinkAction ) )
1348 event->setDropAction( Qt::CopyAction );
1352 const QMimeData *mimeData = event->mimeData();
1354 /* D&D of a subtitles file, add it on the fly */
1355 if( mimeData->urls().count() == 1 && THEMIM->getIM()->hasInput() )
1357 if( !input_AddSubtitleOSD( THEMIM->getInput(),
1358 qtu( toNativeSeparators( mimeData->urls()[0].toLocalFile() ) ),
1366 bool first = b_play;
1367 foreach( const QUrl &url, mimeData->urls() )
1371 QString mrl = toURI( url.toEncoded().constData() );
1372 QFileInfo info( url.toLocalFile() );
1373 if( info.exists() && info.isSymLink() )
1375 QString target = info.symLinkTarget();
1377 if( QFile::exists( target ) )
1379 url = QUrl::fromLocalFile( target );
1383 url.setUrl( target );
1385 mrl = toURI( url.toEncoded().constData() );
1387 if( mrl.length() > 0 )
1389 Open::openMRL( p_intf, mrl, first, b_playlist );
1395 /* Browsers give content as text if you dnd the addressbar,
1396 so check if mimedata has valid url in text and use it
1397 if we didn't get any normal Urls()*/
1398 if( !mimeData->hasUrls() && mimeData->hasText() &&
1399 QUrl(mimeData->text()).isValid() )
1401 QString mrl = toURI( mimeData->text() );
1402 Open::openMRL( p_intf, mrl, first, b_playlist );
1406 void MainInterface::dragEnterEvent(QDragEnterEvent *event)
1408 event->acceptProposedAction();
1410 void MainInterface::dragMoveEvent(QDragMoveEvent *event)
1412 event->acceptProposedAction();
1414 void MainInterface::dragLeaveEvent(QDragLeaveEvent *event)
1419 /************************************************************************
1421 ************************************************************************/
1422 void MainInterface::keyPressEvent( QKeyEvent *e )
1424 handleKeyPress( e );
1426 /* easter eggs sequence handling */
1427 if ( e->key() == kc[ i_kc_offset ] )
1432 if ( i_kc_offset == (sizeof( kc ) / sizeof( Qt::Key )) )
1439 void MainInterface::handleKeyPress( QKeyEvent *e )
1441 if( ( ( e->modifiers() & Qt::ControlModifier ) && ( e->key() == Qt::Key_H ) ) ||
1442 ( b_minimalView && !b_videoFullScreen && e->key() == Qt::Key_Escape ) )
1444 toggleMinimalView( !b_minimalView );
1448 int i_vlck = qtEventToVLCKey( e );
1451 var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
1458 void MainInterface::wheelEvent( QWheelEvent *e )
1460 int i_vlckey = qtWheelEventToVLCKey( e );
1461 var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlckey );
1465 void MainInterface::closeEvent( QCloseEvent *e )
1468 emit askToQuit(); /* ask THEDP to quit, so we have a unique method */
1469 /* Accept session quit. Otherwise we break the desktop mamager. */
1473 bool MainInterface::eventFilter( QObject *obj, QEvent *event )
1475 if ( event->type() == MainInterface::ToolbarsNeedRebuild ) {
1480 return QObject::eventFilter( obj, event );
1484 void MainInterface::toolBarConfUpdated()
1486 QApplication::postEvent( this, new QEvent( MainInterface::ToolbarsNeedRebuild ) );
1489 void MainInterface::setInterfaceFullScreen( bool fs )
1492 setWindowState( windowState() | Qt::WindowFullScreen );
1494 setWindowState( windowState() & ~Qt::WindowFullScreen );
1496 void MainInterface::toggleInterfaceFullScreen()
1498 b_interfaceFullScreen = !b_interfaceFullScreen;
1499 if( !b_videoFullScreen )
1500 setInterfaceFullScreen( b_interfaceFullScreen );
1501 emit fullscreenInterfaceToggled( b_interfaceFullScreen );
1504 void MainInterface::emitBoss()
1508 void MainInterface::setBoss()
1521 void MainInterface::emitRaise()
1525 void MainInterface::setRaise()
1531 /*****************************************************************************
1532 * PopupMenuCB: callback triggered by the intf-popupmenu playlist variable.
1533 * We don't show the menu directly here because we don't want the
1534 * caller to block for a too long time.
1535 *****************************************************************************/
1536 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
1537 vlc_value_t old_val, vlc_value_t new_val, void *param )
1539 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1541 intf_thread_t *p_intf = (intf_thread_t *)param;
1543 if( p_intf->pf_show_dialog )
1545 p_intf->pf_show_dialog( p_intf, INTF_DIALOG_POPUPMENU,
1546 new_val.b_bool, NULL );
1552 /*****************************************************************************
1553 * IntfShowCB: callback triggered by the intf-toggle-fscontrol libvlc variable.
1554 *****************************************************************************/
1555 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
1556 vlc_value_t old_val, vlc_value_t new_val, void *param )
1558 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1559 VLC_UNUSED( new_val );
1561 intf_thread_t *p_intf = (intf_thread_t *)param;
1562 p_intf->p_sys->p_mi->toggleFSC();
1568 /*****************************************************************************
1569 * IntfRaiseMainCB: callback triggered by the intf-show-main libvlc variable.
1570 *****************************************************************************/
1571 static int IntfRaiseMainCB( vlc_object_t *p_this, const char *psz_variable,
1572 vlc_value_t old_val, vlc_value_t new_val, void *param )
1574 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1575 VLC_UNUSED( new_val );
1577 intf_thread_t *p_intf = (intf_thread_t *)param;
1578 p_intf->p_sys->p_mi->emitRaise();
1583 /*****************************************************************************
1584 * IntfBossCB: callback triggered by the intf-boss libvlc variable.
1585 *****************************************************************************/
1586 static int IntfBossCB( vlc_object_t *p_this, const char *psz_variable,
1587 vlc_value_t old_val, vlc_value_t new_val, void *param )
1589 VLC_UNUSED( p_this ); VLC_UNUSED( psz_variable ); VLC_UNUSED( old_val );
1590 VLC_UNUSED( new_val );
1592 intf_thread_t *p_intf = (intf_thread_t *)param;
1593 p_intf->p_sys->p_mi->emitBoss();