1 /*****************************************************************************
2 * main_interface.cpp : Main interface
3 ****************************************************************************
4 * Copyright (C) 2006-2011 VideoLAN and AUTHORS
5 * $Id: a57506533d635a67ca3fff0d9be35635eec11997 $
6 *
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Jean-Baptiste Kempf <jb@videolan.org>
9 * Ilkka Ollakka <ileoo@videolan.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include "qt.hpp"
31
32 #include "main_interface.hpp"
33 #include "input_manager.hpp" // Creation
34 #include "actions_manager.hpp" // killInstance
35
36 #include "util/customwidgets.hpp" // qtEventToVLCKey, QVLCStackedWidget
37 #include "util/qt_dirs.hpp" // toNativeSeparators
38 #include "util/imagehelper.hpp"
39
40 #include "components/interface_widgets.hpp" // bgWidget, videoWidget
41 #include "components/controller.hpp" // controllers
42 #include "components/playlist/playlist.hpp" // plWidget
43 #include "dialogs/firstrun.hpp" // First Run
44 #include "dialogs/playlist.hpp" // PlaylistDialog
45
46 #include "menus.hpp" // Menu creation
47 #include "recents.hpp" // RecentItems when DnD
48
49 #include <QCloseEvent>
50 #include <QKeyEvent>
51
52 #include <QUrl>
53 #include <QSize>
54 #include <QDate>
55 #include <QMimeData>
56
57 #include <QWindow>
58 #include <QMenu>
59 #include <QMenuBar>
60 #include <QStatusBar>
61 #include <QLabel>
62 #include <QStackedWidget>
63 #include <QScreen>
64 #ifdef _WIN32
65 #include <QFileInfo>
66 #endif
67
68 #if ! HAS_QT510 && defined(QT5_HAS_X11)
69 # include <QX11Info>
70 # include <X11/Xlib.h>
71 #endif
72
73 #include <QTimer>
74
75 #include <vlc_actions.h> /* Wheel event */
76 #include <vlc_vout_display.h> /* vout_thread_t and VOUT_ events */
77
78 // #define DEBUG_INTF
79
80 /* Callback prototypes */
81 static int PopupMenuCB( 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 IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
84 vlc_value_t old_val, vlc_value_t new_val, void *param );
85 static int IntfBossCB( vlc_object_t *p_this, const char *psz_variable,
86 vlc_value_t old_val, vlc_value_t new_val, void *param );
87 static int IntfRaiseMainCB( vlc_object_t *p_this, const char *psz_variable,
88 vlc_value_t old_val, vlc_value_t new_val,
89 void *param );
90
91 const QEvent::Type MainInterface::ToolbarsNeedRebuild =
92 (QEvent::Type)QEvent::registerEventType();
93
MainInterface(intf_thread_t * _p_intf)94 MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
95 {
96 /* Variables initialisation */
97 bgWidget = NULL;
98 videoWidget = NULL;
99 playlistWidget = NULL;
100 stackCentralOldWidget= NULL;
101 lastWinScreen = NULL;
102 sysTray = NULL;
103 fullscreenControls = NULL;
104 cryptedLabel = NULL;
105 controls = NULL;
106 inputC = NULL;
107
108 b_hideAfterCreation = false; // --qt-start-minimized
109 playlistVisible = false;
110 input_name = "";
111 b_interfaceFullScreen= false;
112 b_hasPausedWhenMinimized = false;
113 i_kc_offset = false;
114 b_maximizedView = false;
115 b_isWindowTiled = false;
116
117 /* Ask for Privacy */
118 FirstRun::CheckAndRun( this, p_intf );
119
120 /**
121 * Configuration and settings
122 * Pre-building of interface
123 **/
124 /* Main settings */
125 setFocusPolicy( Qt::StrongFocus );
126 setAcceptDrops( true );
127 setWindowRole( "vlc-main" );
128 setWindowIcon( QApplication::windowIcon() );
129 setWindowOpacity( var_InheritFloat( p_intf, "qt-opacity" ) );
130
131 /* Does the interface resize to video size or the opposite */
132 b_autoresize = var_InheritBool( p_intf, "qt-video-autoresize" );
133
134 /* Are we in the enhanced always-video mode or not ? */
135 b_minimalView = var_InheritBool( p_intf, "qt-minimal-view" );
136
137 /* Do we want anoying popups or not */
138 i_notificationSetting = var_InheritInteger( p_intf, "qt-notification" );
139
140 /* */
141 b_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );
142
143 /* Set the other interface settings */
144 settings = getSettings();
145
146 /* */
147 b_plDocked = getSettings()->value( "MainWindow/pl-dock-status", true ).toBool();
148
149 /* Should the UI stays on top of other windows */
150 b_interfaceOnTop = var_InheritBool( p_intf, "video-on-top" );
151
152 #ifdef QT5_HAS_WAYLAND
153 b_hasWayland = QGuiApplication::platformName()
154 .startsWith(QLatin1String("wayland"), Qt::CaseInsensitive);
155 #endif
156
157 /**************************
158 * UI and Widgets design
159 **************************/
160 setVLCWindowsTitle();
161
162 /************
163 * Menu Bar *
164 ************/
165 VLCMenuBar::createMenuBar( this, p_intf );
166 CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ),
167 THEDP, destroyPopupMenu() );
168
169 createMainWidget( settings );
170
171 /**************
172 * Status Bar *
173 **************/
174 createStatusBar();
175 setStatusBarVisibility( getSettings()->value( "MainWindow/status-bar-visible", false ).toBool() );
176
177 /*********************************
178 * Create the Systray Management *
179 *********************************/
180 initSystray();
181
182 /*************************************************************
183 * Connect the input manager to the GUI elements it manages *
184 * Beware initSystray did some connects on input manager too *
185 *************************************************************/
186 /**
187 * Connects on nameChanged()
188 * Those connects are different because options can impeach them to trigger.
189 **/
190 /* Main Interface statusbar */
191 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
192 this, setName( const QString& ) );
193 /* and title of the Main Interface*/
194 if( var_InheritBool( p_intf, "qt-name-in-title" ) )
195 {
196 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
197 this, setVLCWindowsTitle( const QString& ) );
198 }
199 CONNECT( THEMIM, inputChanged( bool ), this, onInputChanged( bool ) );
200
201 /* END CONNECTS ON IM */
202
203 /* VideoWidget connects for asynchronous calls */
204 b_videoFullScreen = false;
205 connect( this, SIGNAL(askGetVideo(struct vout_window_t*, unsigned, unsigned, bool, bool*)),
206 this, SLOT(getVideoSlot(struct vout_window_t*, unsigned, unsigned, bool, bool*)),
207 Qt::BlockingQueuedConnection );
208 connect( this, SIGNAL(askReleaseVideo( void )),
209 this, SLOT(releaseVideoSlot( void )),
210 Qt::BlockingQueuedConnection );
211 CONNECT( this, askVideoOnTop(bool), this, setVideoOnTop(bool));
212
213 if( videoWidget )
214 {
215 if( b_autoresize )
216 {
217 CONNECT( videoWidget, sizeChanged( int, int ),
218 this, videoSizeChanged( int, int ) );
219 }
220 CONNECT( this, askVideoToResize( unsigned int, unsigned int ),
221 this, setVideoSize( unsigned int, unsigned int ) );
222
223 CONNECT( this, askVideoSetFullScreen( bool ),
224 this, setVideoFullScreen( bool ) );
225 CONNECT( this, askHideMouse( bool ),
226 this, setHideMouse( bool ) );
227 }
228
229 CONNECT( THEDP, toolBarConfUpdated(), this, toolBarConfUpdated() );
230 installEventFilter( this );
231
232 CONNECT( this, askToQuit(), THEDP, quit() );
233
234 CONNECT( this, askBoss(), this, setBoss() );
235 CONNECT( this, askRaise(), this, setRaise() );
236
237
238 connect( THEDP, &DialogsProvider::releaseMouseEvents, this, &MainInterface::voutReleaseMouseEvents ) ;
239 /** END of CONNECTS**/
240
241
242 /************
243 * Callbacks
244 ************/
245 var_AddCallback( p_intf->obj.libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
246 var_AddCallback( p_intf->obj.libvlc, "intf-boss", IntfBossCB, p_intf );
247 var_AddCallback( p_intf->obj.libvlc, "intf-show", IntfRaiseMainCB, p_intf );
248
249 /* Register callback for the intf-popupmenu variable */
250 var_AddCallback( p_intf->obj.libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
251
252
253 /* Final Sizing, restoration and placement of the interface */
254 if( settings->value( "MainWindow/playlist-visible", false ).toBool() )
255 togglePlaylist();
256
257 QVLCTools::restoreWidgetPosition( settings, this, QSize(600, 420) );
258
259 b_interfaceFullScreen = isFullScreen();
260
261 setVisible( !b_hideAfterCreation );
262
263 /* Switch to minimal view if needed, must be called after the show() */
264 if( b_minimalView )
265 toggleMinimalView( true );
266
267 computeMinimumSize();
268 }
269
~MainInterface()270 MainInterface::~MainInterface()
271 {
272 /* Unsure we hide the videoWidget before destroying it */
273 if( stackCentralOldWidget == videoWidget )
274 showTab( bgWidget );
275
276 if( videoWidget )
277 releaseVideoSlot();
278
279 /* Be sure to kill the actionsManager... Only used in the MI and control */
280 ActionsManager::killInstance();
281
282 /* Delete the FSC controller */
283 delete fullscreenControls;
284
285 /* Save states */
286
287 settings->beginGroup("MainWindow");
288 settings->setValue( "pl-dock-status", b_plDocked );
289
290 /* Save playlist state */
291 settings->setValue( "playlist-visible", playlistVisible );
292
293 settings->setValue( "adv-controls",
294 getControlsVisibilityStatus() & CONTROLS_ADVANCED );
295 settings->setValue( "status-bar-visible", b_statusbarVisible );
296
297 /* Save the stackCentralW sizes */
298 settings->setValue( "bgSize", stackWidgetsSizes[bgWidget] );
299 settings->setValue( "playlistSize", stackWidgetsSizes[playlistWidget] );
300 settings->endGroup();
301
302 /* Save this size */
303 QVLCTools::saveWidgetPosition(settings, this);
304
305 /* Unregister callbacks */
306 var_DelCallback( p_intf->obj.libvlc, "intf-boss", IntfBossCB, p_intf );
307 var_DelCallback( p_intf->obj.libvlc, "intf-show", IntfRaiseMainCB, p_intf );
308 var_DelCallback( p_intf->obj.libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
309 var_DelCallback( p_intf->obj.libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
310
311 p_intf->p_sys->p_mi = NULL;
312 }
313
computeMinimumSize()314 void MainInterface::computeMinimumSize()
315 {
316 int minWidth = 80;
317 if( menuBar()->isVisible() )
318 minWidth += controls->sizeHint().width();
319
320 setMinimumWidth( minWidth );
321 }
322
323 /*****************************
324 * Main UI handling *
325 *****************************/
recreateToolbars()326 void MainInterface::recreateToolbars()
327 {
328 bool b_adv = getControlsVisibilityStatus() & CONTROLS_ADVANCED;
329
330 delete controls;
331 delete inputC;
332
333 controls = new ControlsWidget( p_intf, b_adv, this );
334 inputC = new InputControlsWidget( p_intf, this );
335 mainLayout->insertWidget( 2, inputC );
336 mainLayout->insertWidget( settings->value( "MainWindow/ToolbarPos", false ).toBool() ? 0: 3,
337 controls );
338
339 if( fullscreenControls )
340 {
341 delete fullscreenControls;
342 fullscreenControls = new FullscreenControllerWidget( p_intf, this );
343 CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
344 this, handleKeyPress( QKeyEvent * ) );
345 THEMIM->requestVoutUpdate();
346 }
347
348 setMinimalView( b_minimalView );
349 }
350
reloadPrefs()351 void MainInterface::reloadPrefs()
352 {
353 i_notificationSetting = var_InheritInteger( p_intf, "qt-notification" );
354 b_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );
355 if( !var_InheritBool( p_intf, "qt-fs-controller" ) && fullscreenControls )
356 {
357 delete fullscreenControls;
358 fullscreenControls = NULL;
359 }
360 }
361
createResumePanel(QWidget * w)362 void MainInterface::createResumePanel( QWidget *w )
363 {
364 resumePanel = new QWidget( w );
365 resumePanel->hide();
366 QHBoxLayout *resumePanelLayout = new QHBoxLayout( resumePanel );
367 resumePanelLayout->setSpacing( 0 ); resumePanelLayout->setMargin( 0 );
368
369 QLabel *continuePixmapLabel = new QLabel();
370 continuePixmapLabel->setPixmap( ImageHelper::loadSvgToPixmap( ":/menu/help.svg" , fontMetrics().height(), fontMetrics().height()) );
371 continuePixmapLabel->setContentsMargins( 5, 0, 5, 0 );
372
373 QLabel *continueLabel = new QLabel( qtr( "Do you want to restart the playback where left off?") );
374
375 QToolButton *cancel = new QToolButton( resumePanel );
376 cancel->setAutoRaise( true );
377 cancel->setText( "X" );
378
379 QPushButton *ok = new QPushButton( qtr( "&Continue" ) );
380
381 resumePanelLayout->addWidget( continuePixmapLabel );
382 resumePanelLayout->addWidget( continueLabel );
383 resumePanelLayout->addStretch( 1 );
384 resumePanelLayout->addWidget( ok );
385 resumePanelLayout->addWidget( cancel );
386
387 resumeTimer = new QTimer( resumePanel );
388 resumeTimer->setSingleShot( true );
389 resumeTimer->setInterval( 6000 );
390
391 CONNECT( resumeTimer, timeout(), this, hideResumePanel() );
392 CONNECT( cancel, clicked(), this, hideResumePanel() );
393 CONNECT( THEMIM->getIM(), resumePlayback(int64_t), this, showResumePanel(int64_t) );
394 BUTTONACT( ok, resumePlayback() );
395
396 w->layout()->addWidget( resumePanel );
397 }
398
showResumePanel(int64_t _time)399 void MainInterface::showResumePanel( int64_t _time ) {
400 int setting = var_InheritInteger( p_intf, "qt-continue" );
401
402 if( setting == 0 )
403 return;
404
405 i_resumeTime = _time;
406
407 if( setting == 2)
408 resumePlayback();
409 else
410 {
411 if( !isFullScreen() && !isMaximized() && !b_isWindowTiled )
412 resizeWindow( width(), height() + resumePanel->height() );
413 resumePanel->setVisible(true);
414 resumeTimer->start();
415 }
416 }
417
hideResumePanel()418 void MainInterface::hideResumePanel()
419 {
420 if( resumePanel->isVisible() )
421 {
422 if( !isFullScreen() && !isMaximized() && !b_isWindowTiled )
423 resizeWindow( width(), height() - resumePanel->height() );
424 resumePanel->hide();
425 resumeTimer->stop();
426 }
427 }
428
resumePlayback()429 void MainInterface::resumePlayback()
430 {
431 if( THEMIM->getIM()->hasInput() ) {
432 var_SetInteger( THEMIM->getInput(), "time", i_resumeTime );
433 }
434 hideResumePanel();
435 }
436
onInputChanged(bool hasInput)437 void MainInterface::onInputChanged( bool hasInput )
438 {
439 if( hasInput == false )
440 return;
441 int autoRaise = var_InheritInteger( p_intf, "qt-auto-raise" );
442 if ( autoRaise == MainInterface::RAISE_NEVER )
443 return;
444 if( THEMIM->getIM()->hasVideo() == true )
445 {
446 if( ( autoRaise & MainInterface::RAISE_VIDEO ) == 0 )
447 return;
448 }
449 else if ( ( autoRaise & MainInterface::RAISE_AUDIO ) == 0 )
450 return;
451 emit askRaise();
452 }
453
createMainWidget(QSettings * creationSettings)454 void MainInterface::createMainWidget( QSettings *creationSettings )
455 {
456 /* Create the main Widget and the mainLayout */
457 QWidget *main = new QWidget;
458 setCentralWidget( main );
459 mainLayout = new QVBoxLayout( main );
460 main->setContentsMargins( 0, 0, 0, 0 );
461 mainLayout->setSpacing( 0 ); mainLayout->setMargin( 0 );
462
463 createResumePanel( main );
464 /* */
465 stackCentralW = new QVLCStackedWidget( main );
466
467 /* Bg Cone */
468 if ( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY
469 && var_InheritBool( p_intf, "qt-icon-change" ) )
470 {
471 bgWidget = new EasterEggBackgroundWidget( p_intf );
472 CONNECT( this, kc_pressed(), bgWidget, animate() );
473 }
474 else
475 bgWidget = new BackgroundWidget( p_intf );
476
477 stackCentralW->addWidget( bgWidget );
478 if ( !var_InheritBool( p_intf, "qt-bgcone" ) )
479 bgWidget->setWithArt( false );
480 else
481 if ( var_InheritBool( p_intf, "qt-bgcone-expands" ) )
482 bgWidget->setExpandstoHeight( true );
483
484 /* And video Outputs */
485 if( var_InheritBool( p_intf, "embedded-video" ) )
486 {
487 videoWidget = new VideoWidget( p_intf, stackCentralW );
488 stackCentralW->addWidget( videoWidget );
489 }
490 mainLayout->insertWidget( 1, stackCentralW );
491
492 stackWidgetsSizes[bgWidget] =
493 creationSettings->value( "MainWindow/bgSize", QSize( 600, 0 ) ).toSize();
494 /* Resize even if no-auto-resize, because we are at creation */
495 resizeStack( stackWidgetsSizes[bgWidget].width(), stackWidgetsSizes[bgWidget].height() );
496
497 /* Create the CONTROLS Widget */
498 controls = new ControlsWidget( p_intf,
499 creationSettings->value( "MainWindow/adv-controls", false ).toBool(), this );
500 inputC = new InputControlsWidget( p_intf, this );
501
502 mainLayout->insertWidget( 2, inputC );
503 mainLayout->insertWidget(
504 creationSettings->value( "MainWindow/ToolbarPos", false ).toBool() ? 0: 3,
505 controls );
506
507 /* Visualisation, disabled for now, they SUCK */
508 #if 0
509 visualSelector = new VisualSelector( p_intf );
510 mainLayout->insertWidget( 0, visualSelector );
511 visualSelector->hide();
512 #endif
513
514
515 /* Enable the popup menu in the MI */
516 main->setContextMenuPolicy( Qt::CustomContextMenu );
517 CONNECT( main, customContextMenuRequested( const QPoint& ),
518 THEDP, setPopupMenu() );
519
520 if ( depth() > 8 ) /* 8bit depth has too many issues with opacity */
521 /* Create the FULLSCREEN CONTROLS Widget */
522 if( var_InheritBool( p_intf, "qt-fs-controller" ) )
523 {
524 fullscreenControls = new FullscreenControllerWidget( p_intf, this );
525 CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
526 this, handleKeyPress( QKeyEvent * ) );
527 }
528
529 if ( b_interfaceOnTop )
530 setWindowFlags( windowFlags() | Qt::WindowStaysOnTopHint );
531 }
532
initSystray()533 inline void MainInterface::initSystray()
534 {
535 bool b_systrayAvailable = QSystemTrayIcon::isSystemTrayAvailable();
536 bool b_systrayWanted = var_InheritBool( p_intf, "qt-system-tray" );
537
538 if( var_InheritBool( p_intf, "qt-start-minimized") )
539 {
540 if( b_systrayAvailable )
541 {
542 b_systrayWanted = true;
543 b_hideAfterCreation = true;
544 }
545 else
546 msg_Err( p_intf, "cannot start minimized without system tray bar" );
547 }
548
549 if( b_systrayAvailable && b_systrayWanted )
550 createSystray();
551 }
552
createStatusBar()553 inline void MainInterface::createStatusBar()
554 {
555 /****************
556 * Status Bar *
557 ****************/
558 /* Widgets Creation*/
559 QStatusBar *statusBarr = statusBar();
560
561 TimeLabel *timeLabel = new TimeLabel( p_intf );
562 nameLabel = new ClickableQLabel();
563 nameLabel->setTextInteractionFlags( Qt::TextSelectableByMouse
564 | Qt::TextSelectableByKeyboard );
565 SpeedLabel *speedLabel = new SpeedLabel( p_intf, this );
566
567 /* Styling those labels */
568 timeLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
569 speedLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
570 nameLabel->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel);
571 timeLabel->setStyleSheet(
572 "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
573 speedLabel->setStyleSheet(
574 "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
575 /* pad both label and its tooltip */
576 nameLabel->setStyleSheet( "padding-left: 5px; padding-right: 5px;" );
577
578 /* and adding those */
579 statusBarr->addWidget( nameLabel, 8 );
580 statusBarr->addPermanentWidget( speedLabel, 0 );
581 statusBarr->addPermanentWidget( timeLabel, 0 );
582
583 CONNECT( nameLabel, doubleClicked(), THEDP, epgDialog() );
584 /* timeLabel behaviour:
585 - double clicking opens the goto time dialog
586 - right-clicking and clicking just toggle between remaining and
587 elapsed time.*/
588 CONNECT( timeLabel, doubleClicked(), THEDP, gotoTimeDialog() );
589
590 CONNECT( THEMIM->getIM(), encryptionChanged( bool ),
591 this, showCryptedLabel( bool ) );
592
593 /* This shouldn't be necessary, but for somehow reason, the statusBarr
594 starts at height of 20px and when a text is shown it needs more space.
595 But, as the QMainWindow policy doesn't allow statusBar to change QMW's
596 geometry, we need to force a height. If you have a better idea, please
597 tell me -- jb
598 */
599 statusBarr->setFixedHeight( statusBarr->sizeHint().height() + 2 );
600 }
601
602 /**********************************************************************
603 * Handling of sizing of the components
604 **********************************************************************/
605
debug()606 void MainInterface::debug()
607 {
608 #ifdef DEBUG_INTF
609 if( controls ) {
610 msg_Dbg( p_intf, "Controls size: %i - %i", controls->size().height(), controls->size().width() );
611 msg_Dbg( p_intf, "Controls minimumsize: %i - %i", controls->minimumSize().height(), controls->minimumSize().width() );
612 msg_Dbg( p_intf, "Controls sizeHint: %i - %i", controls->sizeHint().height(), controls->sizeHint().width() );
613 }
614
615 msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
616 msg_Dbg( p_intf, "sizeHint: %i - %i", sizeHint().height(), sizeHint().width() );
617 msg_Dbg( p_intf, "minimumsize: %i - %i", minimumSize().height(), minimumSize().width() );
618
619 msg_Dbg( p_intf, "Stack size: %i - %i", stackCentralW->size().height(), stackCentralW->size().width() );
620 msg_Dbg( p_intf, "Stack sizeHint: %i - %i", stackCentralW->sizeHint().height(), stackCentralW->sizeHint().width() );
621 msg_Dbg( p_intf, "Central size: %i - %i", centralWidget()->size().height(), centralWidget()->size().width() );
622 #endif
623 }
624
showVideo()625 inline void MainInterface::showVideo() { showTab( videoWidget ); }
restoreStackOldWidget(bool video_closing)626 inline void MainInterface::restoreStackOldWidget( bool video_closing )
627 { showTab( stackCentralOldWidget, video_closing ); }
628
showTab(QWidget * widget,bool video_closing)629 inline void MainInterface::showTab( QWidget *widget, bool video_closing )
630 {
631 if ( !widget ) widget = bgWidget; /* trying to restore a null oldwidget */
632 #ifdef DEBUG_INTF
633 if ( stackCentralOldWidget )
634 msg_Dbg( p_intf, "Old stackCentralOldWidget %s at index %i",
635 stackCentralOldWidget->metaObject()->className(),
636 stackCentralW->indexOf( stackCentralOldWidget ) );
637 msg_Dbg( p_intf, "ShowTab request for %s", widget->metaObject()->className() );
638 #endif
639 if ( stackCentralW->currentWidget() == widget )
640 return;
641
642 /* fixing when the playlist has been undocked after being hidden.
643 restoreStackOldWidget() is called when video stops but
644 stackCentralOldWidget would still be pointing to playlist */
645 if ( widget == playlistWidget && !isPlDocked() )
646 widget = bgWidget;
647
648 stackCentralOldWidget = stackCentralW->currentWidget();
649 if( !isFullScreen() )
650 stackWidgetsSizes[stackCentralOldWidget] = stackCentralW->size();
651
652 /* If we are playing video, embedded */
653 if( !video_closing && videoWidget && THEMIM->getIM()->hasVideo() )
654 {
655 /* Video -> Playlist */
656 if( videoWidget == stackCentralOldWidget && widget == playlistWidget )
657 {
658 stackCentralW->removeWidget( videoWidget );
659 videoWidget->show(); videoWidget->raise();
660 }
661
662 /* Playlist -> Video */
663 if( playlistWidget == stackCentralOldWidget && widget == videoWidget )
664 {
665 playlistWidget->artContainer->removeWidget( videoWidget );
666 videoWidget->show(); videoWidget->raise();
667 stackCentralW->addWidget( videoWidget );
668 }
669
670 /* Embedded playlist -> Non-embedded playlist */
671 if( bgWidget == stackCentralOldWidget && widget == videoWidget )
672 {
673 /* In rare case when video is started before the interface */
674 if( playlistWidget != NULL )
675 playlistWidget->artContainer->removeWidget( videoWidget );
676 videoWidget->show(); videoWidget->raise();
677 stackCentralW->addWidget( videoWidget );
678 stackCentralW->setCurrentWidget( videoWidget );
679 }
680 }
681
682 stackCentralW->setCurrentWidget( widget );
683 if( b_autoresize )
684 resizeStack( stackWidgetsSizes[widget].width(), stackWidgetsSizes[widget].height() );
685
686 #ifdef DEBUG_INTF
687 msg_Dbg( p_intf, "Stack state changed to %s, index %i",
688 stackCentralW->currentWidget()->metaObject()->className(),
689 stackCentralW->currentIndex() );
690 msg_Dbg( p_intf, "New stackCentralOldWidget %s at index %i",
691 stackCentralOldWidget->metaObject()->className(),
692 stackCentralW->indexOf( stackCentralOldWidget ) );
693 #endif
694
695 /* This part is done later, to account for the new pl size */
696 if( !video_closing && videoWidget && THEMIM->getIM()->hasVideo() &&
697 videoWidget == stackCentralOldWidget && widget == playlistWidget )
698 {
699 playlistWidget->artContainer->addWidget( videoWidget );
700 playlistWidget->artContainer->setCurrentWidget( videoWidget );
701 }
702 }
703
toggleFSC()704 void MainInterface::toggleFSC()
705 {
706 if( !fullscreenControls ) return;
707
708 IMEvent *eShow = new IMEvent( IMEvent::FullscreenControlToggle );
709 QApplication::postEvent( fullscreenControls, eShow );
710 }
711
712 /****************************************************************************
713 * Video Handling
714 ****************************************************************************/
715
716 /**
717 * NOTE:
718 * You must not change the state of this object or other Qt UI objects,
719 * from the video output thread - only from the Qt UI main loop thread.
720 * All window provider queries must be handled through signals or events.
721 * That's why we have all those emit statements...
722 */
getVideo(struct vout_window_t * p_wnd,unsigned int i_width,unsigned int i_height,bool fullscreen)723 bool MainInterface::getVideo( struct vout_window_t *p_wnd,
724 unsigned int i_width, unsigned int i_height,
725 bool fullscreen )
726 {
727 bool result;
728
729 /* This is a blocking call signal. Results are stored directly in the
730 * vout_window_t and boolean pointers. Beware of deadlocks! */
731 emit askGetVideo( p_wnd, i_width, i_height, fullscreen, &result );
732 return result;
733 }
734
getVideoSlot(struct vout_window_t * p_wnd,unsigned i_width,unsigned i_height,bool fullscreen,bool * res)735 void MainInterface::getVideoSlot( struct vout_window_t *p_wnd,
736 unsigned i_width, unsigned i_height,
737 bool fullscreen, bool *res )
738 {
739 /* Hidden or minimized, activate */
740 if( isHidden() || isMinimized() )
741 toggleUpdateSystrayMenu();
742
743 /* Request the videoWidget */
744 if ( !videoWidget )
745 {
746 videoWidget = new VideoWidget( p_intf, stackCentralW );
747 stackCentralW->addWidget( videoWidget );
748 }
749 *res = videoWidget->request( p_wnd );
750 if( *res ) /* The videoWidget is available */
751 {
752 setVideoFullScreen( fullscreen );
753
754 /* Consider the video active now */
755 showVideo();
756
757 /* Ask videoWidget to resize correctly, if we are in normal mode */
758 if( b_autoresize ) {
759 #if HAS_QT56
760 qreal factor = videoWidget->devicePixelRatioF();
761
762 i_width = qRound( (qreal) i_width / factor );
763 i_height = qRound( (qreal) i_height / factor );
764 #endif
765
766 videoWidget->setSize( i_width, i_height );
767 }
768 }
769 }
770
771 /* Asynchronous call from the WindowClose function */
releaseVideo(void)772 void MainInterface::releaseVideo( void )
773 {
774 emit askReleaseVideo();
775 }
776
777 /* Function that is CONNECTED to the previous emit */
releaseVideoSlot(void)778 void MainInterface::releaseVideoSlot( void )
779 {
780 /* This function is called when the embedded video window is destroyed,
781 * or in the rare case that the embedded window is still here but the
782 * Qt interface exits. */
783 assert( videoWidget );
784 videoWidget->release();
785 setVideoOnTop( false );
786 setVideoFullScreen( false );
787 hideResumePanel();
788
789 if( stackCentralW->currentWidget() == videoWidget )
790 restoreStackOldWidget( true );
791 else if( playlistWidget &&
792 playlistWidget->artContainer->currentWidget() == videoWidget )
793 {
794 playlistWidget->artContainer->setCurrentIndex( 0 );
795 stackCentralW->addWidget( videoWidget );
796 }
797
798 /* We don't want to have a blank video to popup */
799 stackCentralOldWidget = bgWidget;
800 }
801
802 // The provided size is in physical pixels, coming from the core.
setVideoSize(unsigned int w,unsigned int h)803 void MainInterface::setVideoSize( unsigned int w, unsigned int h )
804 {
805 if (!isFullScreen() && !isMaximized() )
806 {
807 /* Resize video widget to video size, or keep it at the same
808 * size. Call setSize() either way so that vout_window_ReportSize
809 * will always get called.
810 * If the video size is too large for the screen, resize it
811 * to the screen size.
812 */
813 if (b_autoresize)
814 {
815 QRect screen = QApplication::desktop()->availableGeometry();
816 #if HAS_QT56
817 float factor = videoWidget->devicePixelRatioF();
818 #else
819 float factor = 1.0f;
820 #endif
821 if( (float)h / factor > screen.height() )
822 {
823 w = screen.width();
824 h = screen.height();
825 if( !b_minimalView )
826 {
827 if( menuBar()->isVisible() )
828 h -= menuBar()->height();
829 if( controls->isVisible() )
830 h -= controls->height();
831 if( statusBar()->isVisible() )
832 h -= statusBar()->height();
833 if( inputC->isVisible() )
834 h -= inputC->height();
835 }
836 h -= style()->pixelMetric(QStyle::PM_TitleBarHeight);
837 h -= style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
838 h -= 2 * style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
839 }
840 else
841 {
842 // Convert the size in logical pixels
843 w = qRound( (float)w / factor );
844 h = qRound( (float)h / factor );
845 msg_Dbg( p_intf, "Logical video size: %ux%u", w, h );
846 }
847 videoWidget->setSize( w, h );
848 }
849 else
850 videoWidget->setSize( videoWidget->width(), videoWidget->height() );
851 }
852 }
853
videoSizeChanged(int w,int h)854 void MainInterface::videoSizeChanged( int w, int h )
855 {
856 if( !playlistWidget || playlistWidget->artContainer->currentWidget() != videoWidget )
857 resizeStack( w, h );
858 }
859
setVideoFullScreen(bool fs)860 void MainInterface::setVideoFullScreen( bool fs )
861 {
862 b_videoFullScreen = fs;
863 if( fs )
864 {
865 int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
866
867 if ( numscreen >= 0 && numscreen < QApplication::desktop()->screenCount() )
868 {
869 if( fullscreenControls )
870 fullscreenControls->setTargetScreen( numscreen );
871
872 QRect screenres = QApplication::desktop()->screenGeometry( numscreen );
873 lastWinScreen = windowHandle()->screen();
874 #ifdef QT5_HAS_WAYLAND
875 if( !b_hasWayland )
876 windowHandle()->setScreen(QGuiApplication::screens()[numscreen]);
877 #else
878 windowHandle()->setScreen(QGuiApplication::screens()[numscreen]);
879 #endif
880
881 /* To be sure window is on proper-screen in xinerama */
882 if( !screenres.contains( pos() ) )
883 {
884 lastWinPosition = pos();
885 lastWinSize = size();
886 msg_Dbg( p_intf, "Moving video to correct position");
887 move( QPoint( screenres.x(), screenres.y() ) );
888 }
889 }
890
891 if( playlistWidget != NULL && playlistWidget->artContainer->currentWidget() == videoWidget )
892 showTab( videoWidget );
893
894 /* we won't be able to get its windowed sized once in fullscreen, so update it now */
895 stackWidgetsSizes[stackCentralW->currentWidget()] = stackCentralW->size();
896
897 /* */
898 displayNormalView();
899 setInterfaceFullScreen( true );
900 }
901 else
902 {
903 setMinimalView( b_minimalView );
904 setInterfaceFullScreen( b_interfaceFullScreen );
905 #ifdef QT5_HAS_WAYLAND
906 if( lastWinScreen != NULL && !b_hasWayland )
907 windowHandle()->setScreen(lastWinScreen);
908 #else
909 if( lastWinScreen != NULL )
910 windowHandle()->setScreen(lastWinScreen);
911 #endif
912 if( lastWinPosition.isNull() == false )
913 {
914 move( lastWinPosition );
915 resizeWindow( lastWinSize.width(), lastWinSize.height() );
916 lastWinPosition = QPoint();
917 lastWinSize = QSize();
918 }
919
920 }
921 videoWidget->sync();
922 }
923
setHideMouse(bool hide)924 void MainInterface::setHideMouse( bool hide )
925 {
926 videoWidget->setCursor( hide ? Qt::BlankCursor : Qt::ArrowCursor );
927 }
928
929 /* Slot to change the video always-on-top flag.
930 * Emit askVideoOnTop() to invoke this from other thread. */
setVideoOnTop(bool on_top)931 void MainInterface::setVideoOnTop( bool on_top )
932 {
933 //don't apply changes if user has already sets its interface on top
934 if ( b_interfaceOnTop )
935 return;
936
937 Qt::WindowFlags oldflags = windowFlags(), newflags;
938
939 if( on_top )
940 newflags = oldflags | Qt::WindowStaysOnTopHint;
941 else
942 newflags = oldflags & ~Qt::WindowStaysOnTopHint;
943 if( newflags != oldflags && !b_videoFullScreen )
944 {
945 setWindowFlags( newflags );
946 show(); /* necessary to apply window flags */
947 }
948 }
949
setInterfaceAlwaysOnTop(bool on_top)950 void MainInterface::setInterfaceAlwaysOnTop( bool on_top )
951 {
952 b_interfaceOnTop = on_top;
953 Qt::WindowFlags oldflags = windowFlags(), newflags;
954
955 if( on_top )
956 newflags = oldflags | Qt::WindowStaysOnTopHint;
957 else
958 newflags = oldflags & ~Qt::WindowStaysOnTopHint;
959 if( newflags != oldflags && !b_videoFullScreen )
960 {
961 setWindowFlags( newflags );
962 show(); /* necessary to apply window flags */
963 }
964 }
965
966 /* Asynchronous call from WindowControl function */
controlVideo(int i_query,va_list args)967 int MainInterface::controlVideo( int i_query, va_list args )
968 {
969 switch( i_query )
970 {
971 case VOUT_WINDOW_SET_SIZE:
972 {
973 unsigned int i_width = va_arg( args, unsigned int );
974 unsigned int i_height = va_arg( args, unsigned int );
975
976 emit askVideoToResize( i_width, i_height );
977 return VLC_SUCCESS;
978 }
979 case VOUT_WINDOW_SET_STATE:
980 {
981 unsigned i_arg = va_arg( args, unsigned );
982 unsigned on_top = i_arg & VOUT_WINDOW_STATE_ABOVE;
983
984 emit askVideoOnTop( on_top != 0 );
985 return VLC_SUCCESS;
986 }
987 case VOUT_WINDOW_SET_FULLSCREEN:
988 {
989 bool b_fs = va_arg( args, int );
990
991 emit askVideoSetFullScreen( b_fs );
992 return VLC_SUCCESS;
993 }
994 case VOUT_WINDOW_HIDE_MOUSE:
995 {
996 bool b_hide = va_arg( args, int );
997
998 emit askHideMouse( b_hide );
999 return VLC_SUCCESS;
1000 }
1001 default:
1002 msg_Warn( p_intf, "unsupported control query" );
1003 return VLC_EGENERIC;
1004 }
1005 }
1006
1007 /*****************************************************************************
1008 * Playlist, Visualisation and Menus handling
1009 *****************************************************************************/
1010 /**
1011 * Toggle the playlist widget or dialog
1012 **/
createPlaylist()1013 void MainInterface::createPlaylist()
1014 {
1015 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
1016
1017 if( b_plDocked )
1018 {
1019 playlistWidget = dialog->exportPlaylistWidget();
1020 stackCentralW->addWidget( playlistWidget );
1021 stackWidgetsSizes[playlistWidget] = settings->value( "playlistSize", QSize( 600, 300 ) ).toSize();
1022 }
1023 CONNECT( dialog, visibilityChanged(bool), this, setPlaylistVisibility(bool) );
1024 }
1025
togglePlaylist()1026 void MainInterface::togglePlaylist()
1027 {
1028 if( !playlistWidget ) createPlaylist();
1029
1030 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
1031 if( b_plDocked )
1032 {
1033 if ( dialog->hasPlaylistWidget() )
1034 playlistWidget = dialog->exportPlaylistWidget();
1035 /* Playlist is not visible, show it */
1036 if( stackCentralW->currentWidget() != playlistWidget )
1037 {
1038 if( stackCentralW->indexOf( playlistWidget ) == -1 )
1039 stackCentralW->addWidget( playlistWidget );
1040 showTab( playlistWidget );
1041 }
1042 else /* Hide it! */
1043 {
1044 restoreStackOldWidget();
1045 }
1046 playlistVisible = ( stackCentralW->currentWidget() == playlistWidget );
1047 }
1048 else
1049 {
1050 playlistVisible = !playlistVisible;
1051 if ( ! dialog->hasPlaylistWidget() )
1052 dialog->importPlaylistWidget( playlistWidget );
1053 if ( playlistVisible )
1054 dialog->show();
1055 else
1056 dialog->hide();
1057 }
1058 debug();
1059 }
1060
1061 const Qt::Key MainInterface::kc[10] =
1062 {
1063 Qt::Key_Up, Qt::Key_Up,
1064 Qt::Key_Down, Qt::Key_Down,
1065 Qt::Key_Left, Qt::Key_Right, Qt::Key_Left, Qt::Key_Right,
1066 Qt::Key_B, Qt::Key_A
1067 };
1068
dockPlaylist(bool p_docked)1069 void MainInterface::dockPlaylist( bool p_docked )
1070 {
1071 if( b_plDocked == p_docked ) return;
1072 /* some extra check */
1073 if ( b_plDocked && !playlistWidget ) createPlaylist();
1074
1075 b_plDocked = p_docked;
1076 PlaylistDialog *dialog = PlaylistDialog::getInstance( p_intf );
1077
1078 if( !p_docked ) /* Previously docked */
1079 {
1080 playlistVisible = playlistWidget->isVisible();
1081
1082 /* repositioning the videowidget __before__ exporting the
1083 playlistwidget into the playlist dialog avoids two unneeded
1084 calls to the server in the qt library to reparent the underlying
1085 native window back and forth.
1086 For Wayland, this is mandatory since reparenting is not implemented.
1087 For X11 or Windows, this is just an optimization. */
1088 if ( videoWidget && THEMIM->getIM()->hasVideo() )
1089 showTab(videoWidget);
1090 else
1091 showTab(bgWidget);
1092
1093 /* playlistwidget exported into the playlist dialog */
1094 stackCentralW->removeWidget( playlistWidget );
1095 dialog->importPlaylistWidget( playlistWidget );
1096 if ( playlistVisible ) dialog->show();
1097 }
1098 else /* Previously undocked */
1099 {
1100 playlistVisible = dialog->isVisible() && !( videoWidget && THEMIM->getIM()->hasVideo() );
1101 dialog->hide();
1102 playlistWidget = dialog->exportPlaylistWidget();
1103 stackCentralW->addWidget( playlistWidget );
1104
1105 /* If playlist is invisible don't show it */
1106 if( playlistVisible ) showTab( playlistWidget );
1107 }
1108 }
1109
1110 /*
1111 * displayNormalView is the private function used by
1112 * the SLOT setVideoFullScreen to restore the menuBar
1113 * if minimal view is off
1114 */
displayNormalView()1115 void MainInterface::displayNormalView()
1116 {
1117 menuBar()->setVisible( false );
1118 controls->setVisible( false );
1119 statusBar()->setVisible( false );
1120 inputC->setVisible( false );
1121 }
1122
1123 /*
1124 * setMinimalView is the private function used by
1125 * the SLOT toggleMinimalView
1126 */
setMinimalView(bool b_minimal)1127 void MainInterface::setMinimalView( bool b_minimal )
1128 {
1129 bool b_menuBarVisible = menuBar()->isVisible();
1130 bool b_controlsVisible = controls->isVisible();
1131 bool b_statusBarVisible = statusBar()->isVisible();
1132 bool b_inputCVisible = inputC->isVisible();
1133
1134 if( !isFullScreen() && !isMaximized() && b_minimal && !b_isWindowTiled )
1135 {
1136 int i_heightChange = 0;
1137
1138 if( b_menuBarVisible )
1139 i_heightChange += menuBar()->height();
1140 if( b_controlsVisible )
1141 i_heightChange += controls->height();
1142 if( b_statusBarVisible )
1143 i_heightChange += statusBar()->height();
1144 if( b_inputCVisible )
1145 i_heightChange += inputC->height();
1146
1147 if( i_heightChange != 0 )
1148 resizeWindow( width(), height() - i_heightChange );
1149 }
1150
1151 menuBar()->setVisible( !b_minimal );
1152 controls->setVisible( !b_minimal );
1153 statusBar()->setVisible( !b_minimal && b_statusbarVisible );
1154 inputC->setVisible( !b_minimal );
1155
1156 if( !isFullScreen() && !isMaximized() && !b_minimal && !b_isWindowTiled )
1157 {
1158 int i_heightChange = 0;
1159
1160 if( !b_menuBarVisible && menuBar()->isVisible() )
1161 i_heightChange += menuBar()->height();
1162 if( !b_controlsVisible && controls->isVisible() )
1163 i_heightChange += controls->height();
1164 if( !b_statusBarVisible && statusBar()->isVisible() )
1165 i_heightChange += statusBar()->height();
1166 if( !b_inputCVisible && inputC->isVisible() )
1167 i_heightChange += inputC->height();
1168
1169 if( i_heightChange != 0 )
1170 resizeWindow( width(), height() + i_heightChange );
1171 }
1172 }
1173
1174 /*
1175 * This public SLOT is used for moving to minimal View Mode
1176 *
1177 * If b_minimal is false, then we are normalView
1178 */
toggleMinimalView(bool b_minimal)1179 void MainInterface::toggleMinimalView( bool b_minimal )
1180 {
1181 if( !b_minimalView && b_autoresize ) /* Normal mode */
1182 {
1183 if( stackCentralW->currentWidget() == bgWidget )
1184 {
1185 if( stackCentralW->height() < 16 )
1186 {
1187 resizeStack( stackCentralW->width(), 100 );
1188 }
1189 }
1190 }
1191 b_minimalView = b_minimal;
1192 if( !b_videoFullScreen )
1193 {
1194 setMinimalView( b_minimalView );
1195 computeMinimumSize();
1196 }
1197
1198 emit minimalViewToggled( b_minimalView );
1199 }
1200
1201 /* toggling advanced controls buttons */
toggleAdvancedButtons()1202 void MainInterface::toggleAdvancedButtons()
1203 {
1204 controls->toggleAdvanced();
1205 // if( fullscreenControls ) fullscreenControls->toggleAdvanced();
1206 }
1207
1208 /* Get the visibility status of the controls (hidden or not, advanced or not) */
getControlsVisibilityStatus()1209 int MainInterface::getControlsVisibilityStatus()
1210 {
1211 if( !controls ) return 0;
1212 return( (controls->isVisible() ? CONTROLS_VISIBLE : CONTROLS_HIDDEN )
1213 + CONTROLS_ADVANCED * controls->b_advancedVisible );
1214 }
1215
getPlaylistView()1216 StandardPLPanel *MainInterface::getPlaylistView()
1217 {
1218 if( !playlistWidget ) return NULL;
1219 else return playlistWidget->mainView;
1220 }
1221
setStatusBarVisibility(bool b_visible)1222 void MainInterface::setStatusBarVisibility( bool b_visible )
1223 {
1224 statusBar()->setVisible( b_visible );
1225 b_statusbarVisible = b_visible;
1226 if( controls ) controls->setGripVisible( !b_statusbarVisible );
1227 }
1228
1229
setPlaylistVisibility(bool b_visible)1230 void MainInterface::setPlaylistVisibility( bool b_visible )
1231 {
1232 if( isPlDocked() || THEDP->isDying() || (playlistWidget && playlistWidget->isMinimized() ) )
1233 return;
1234
1235 playlistVisible = b_visible;
1236 }
1237
1238 /************************************************************************
1239 * Other stuff
1240 ************************************************************************/
setName(const QString & name)1241 void MainInterface::setName( const QString& name )
1242 {
1243 input_name = name; /* store it for the QSystray use */
1244 /* Display it in the status bar, but also as a Tooltip in case it doesn't
1245 fit in the label */
1246 nameLabel->setText( name );
1247 nameLabel->setToolTip( name );
1248 }
1249
1250 /**
1251 * Give the decorations of the Main Window a correct Name.
1252 * If nothing is given, set it to VLC...
1253 **/
setVLCWindowsTitle(const QString & aTitle)1254 void MainInterface::setVLCWindowsTitle( const QString& aTitle )
1255 {
1256 if( aTitle.isEmpty() )
1257 {
1258 setWindowTitle( qtr( "VLC media player" ) );
1259 }
1260 else
1261 {
1262 setWindowTitle( aTitle + " - " + qtr( "VLC media player" ) );
1263 }
1264 }
1265
showCryptedLabel(bool b_show)1266 void MainInterface::showCryptedLabel( bool b_show )
1267 {
1268 if( cryptedLabel == NULL )
1269 {
1270 cryptedLabel = new QLabel;
1271 // The lock icon is not the right one for DRM protection/scrambled.
1272 //cryptedLabel->setPixmap( QPixmap( ":/lock.svg" ) );
1273 cryptedLabel->setText( "DRM" );
1274 statusBar()->addWidget( cryptedLabel );
1275 }
1276
1277 cryptedLabel->setVisible( b_show );
1278 }
1279
showBuffering(float f_cache)1280 void MainInterface::showBuffering( float f_cache )
1281 {
1282 QString amount = QString("Buffering: %1%").arg( (int)(100*f_cache) );
1283 statusBar()->showMessage( amount, 1000 );
1284 }
1285
1286 /*****************************************************************************
1287 * Systray Icon and Systray Menu
1288 *****************************************************************************/
1289 /**
1290 * Create a SystemTray icon and a menu that would go with it.
1291 * Connects to a click handler on the icon.
1292 **/
createSystray()1293 void MainInterface::createSystray()
1294 {
1295 QIcon iconVLC;
1296 if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
1297 iconVLC = QIcon::fromTheme( "vlc-xmas", QIcon( ":/logo/vlc128-xmas.png" ) );
1298 else
1299 iconVLC = QIcon::fromTheme( "vlc", QIcon( ":/logo/vlc256.png" ) );
1300 sysTray = new QSystemTrayIcon( iconVLC, this );
1301 sysTray->setToolTip( qtr( "VLC media player" ));
1302
1303 systrayMenu = new QMenu( qtr( "VLC media player" ), this );
1304 systrayMenu->setIcon( iconVLC );
1305
1306 VLCMenuBar::updateSystrayMenu( this, p_intf, true );
1307 sysTray->show();
1308
1309 CONNECT( sysTray, activated( QSystemTrayIcon::ActivationReason ),
1310 this, handleSystrayClick( QSystemTrayIcon::ActivationReason ) );
1311
1312 /* Connects on nameChanged() */
1313 CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
1314 this, updateSystrayTooltipName( const QString& ) );
1315 /* Connect PLAY_STATUS on the systray */
1316 CONNECT( THEMIM->getIM(), playingStatusChanged( int ),
1317 this, updateSystrayTooltipStatus( int ) );
1318 }
1319
toggleUpdateSystrayMenuWhenVisible()1320 void MainInterface::toggleUpdateSystrayMenuWhenVisible()
1321 {
1322 hide();
1323 }
1324
resizeWindow(int w,int h)1325 void MainInterface::resizeWindow(int w, int h)
1326 {
1327 #if ! HAS_QT510 && defined(QT5_HAS_X11)
1328 if( QX11Info::isPlatformX11() )
1329 {
1330 #if HAS_QT56
1331 qreal dpr = devicePixelRatioF();
1332 #else
1333 qreal dpr = devicePixelRatio();
1334 #endif
1335 QSize size(w, h);
1336 size = size.boundedTo(maximumSize()).expandedTo(minimumSize());
1337 /* X11 window managers are not required to accept geometry changes on
1338 * the top-level window. Unfortunately, Qt < 5.10 assumes that the
1339 * change will succeed, and resizes all sub-windows unconditionally.
1340 * By calling XMoveResizeWindow directly, Qt will not see our change
1341 * request until the ConfigureNotify event on success
1342 * and not at all if it is rejected. */
1343 XResizeWindow( QX11Info::display(), winId(),
1344 (unsigned int)size.width() * dpr, (unsigned int)size.height() * dpr);
1345 return;
1346 }
1347 #endif
1348 resize(w, h);
1349 }
1350
1351 /**
1352 * Updates the Systray Icon's menu and toggle the main interface
1353 */
toggleUpdateSystrayMenu()1354 void MainInterface::toggleUpdateSystrayMenu()
1355 {
1356 /* If hidden, show it */
1357 if( isHidden() )
1358 {
1359 show();
1360 activateWindow();
1361 }
1362 else if( isMinimized() )
1363 {
1364 /* Minimized */
1365 showNormal();
1366 activateWindow();
1367 }
1368 else
1369 {
1370 /* Visible (possibly under other windows) */
1371 toggleUpdateSystrayMenuWhenVisible();
1372 }
1373 if( sysTray )
1374 VLCMenuBar::updateSystrayMenu( this, p_intf );
1375 }
1376
1377 /* First Item of the systray menu */
showUpdateSystrayMenu()1378 void MainInterface::showUpdateSystrayMenu()
1379 {
1380 if( isHidden() )
1381 show();
1382 if( isMinimized() )
1383 showNormal();
1384 activateWindow();
1385
1386 VLCMenuBar::updateSystrayMenu( this, p_intf );
1387 }
1388
1389 /* First Item of the systray menu */
hideUpdateSystrayMenu()1390 void MainInterface::hideUpdateSystrayMenu()
1391 {
1392 hide();
1393 VLCMenuBar::updateSystrayMenu( this, p_intf );
1394 }
1395
1396 /* Click on systray Icon */
handleSystrayClick(QSystemTrayIcon::ActivationReason reason)1397 void MainInterface::handleSystrayClick(
1398 QSystemTrayIcon::ActivationReason reason )
1399 {
1400 switch( reason )
1401 {
1402 case QSystemTrayIcon::Trigger:
1403 case QSystemTrayIcon::DoubleClick:
1404 #ifdef Q_OS_MAC
1405 VLCMenuBar::updateSystrayMenu( this, p_intf );
1406 #else
1407 toggleUpdateSystrayMenu();
1408 #endif
1409 break;
1410 case QSystemTrayIcon::MiddleClick:
1411 sysTray->showMessage( qtr( "VLC media player" ),
1412 qtr( "Control menu for the player" ),
1413 QSystemTrayIcon::Information, 3000 );
1414 break;
1415 default:
1416 break;
1417 }
1418 }
1419
1420 /**
1421 * Updates the name of the systray Icon tooltip.
1422 * Doesn't check if the systray exists, check before you call it.
1423 **/
updateSystrayTooltipName(const QString & name)1424 void MainInterface::updateSystrayTooltipName( const QString& name )
1425 {
1426 if( name.isEmpty() )
1427 {
1428 sysTray->setToolTip( qtr( "VLC media player" ) );
1429 }
1430 else
1431 {
1432 sysTray->setToolTip( name );
1433 if( ( i_notificationSetting == NOTIFICATION_ALWAYS ) ||
1434 ( i_notificationSetting == NOTIFICATION_MINIMIZED && (isMinimized() || isHidden()) ) )
1435 {
1436 sysTray->showMessage( qtr( "VLC media player" ), name,
1437 QSystemTrayIcon::NoIcon, 3000 );
1438 }
1439 }
1440
1441 VLCMenuBar::updateSystrayMenu( this, p_intf );
1442 }
1443
1444 /**
1445 * Updates the status of the systray Icon tooltip.
1446 * Doesn't check if the systray exists, check before you call it.
1447 **/
updateSystrayTooltipStatus(int i_status)1448 void MainInterface::updateSystrayTooltipStatus( int i_status )
1449 {
1450 switch( i_status )
1451 {
1452 case PLAYING_S:
1453 sysTray->setToolTip( input_name );
1454 break;
1455 case PAUSE_S:
1456 sysTray->setToolTip( input_name + " - " + qtr( "Paused") );
1457 break;
1458 default:
1459 sysTray->setToolTip( qtr( "VLC media player" ) );
1460 break;
1461 }
1462 VLCMenuBar::updateSystrayMenu( this, p_intf );
1463 }
1464
changeEvent(QEvent * event)1465 void MainInterface::changeEvent(QEvent *event)
1466 {
1467 if( event->type() == QEvent::WindowStateChange )
1468 {
1469 QWindowStateChangeEvent *windowStateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);
1470 Qt::WindowStates newState = windowState();
1471 Qt::WindowStates oldState = windowStateChangeEvent->oldState();
1472
1473 /* b_maximizedView stores if the window was maximized before entering fullscreen.
1474 * It is set when entering maximized mode, unset when leaving it to normal mode.
1475 * Upon leaving full screen, if b_maximizedView is set,
1476 * the window should be maximized again. */
1477 if( newState & Qt::WindowMaximized &&
1478 !( oldState & Qt::WindowMaximized ) )
1479 b_maximizedView = true;
1480
1481 if( !( newState & Qt::WindowMaximized ) &&
1482 oldState & Qt::WindowMaximized &&
1483 !b_videoFullScreen )
1484 b_maximizedView = false;
1485
1486 if( !( newState & Qt::WindowFullScreen ) &&
1487 oldState & Qt::WindowFullScreen &&
1488 b_maximizedView )
1489 {
1490 showMaximized();
1491 return;
1492 }
1493
1494 if( newState & Qt::WindowMinimized )
1495 {
1496 b_hasPausedWhenMinimized = false;
1497
1498 if( THEMIM->getIM()->playingStatus() == PLAYING_S &&
1499 THEMIM->getIM()->hasVideo() && !THEMIM->getIM()->hasVisualisation() &&
1500 b_pauseOnMinimize )
1501 {
1502 b_hasPausedWhenMinimized = true;
1503 THEMIM->pause();
1504 }
1505 }
1506 else if( oldState & Qt::WindowMinimized && !( newState & Qt::WindowMinimized ) )
1507 {
1508 if( b_hasPausedWhenMinimized )
1509 {
1510 THEMIM->play();
1511 }
1512 }
1513 }
1514
1515 QWidget::changeEvent(event);
1516 }
1517
1518 /************************************************************************
1519 * D&D Events
1520 ************************************************************************/
dropEvent(QDropEvent * event)1521 void MainInterface::dropEvent(QDropEvent *event)
1522 {
1523 dropEventPlay( event, true );
1524 }
1525
1526 /**
1527 * dropEventPlay
1528 *
1529 * Event called if something is dropped onto a VLC window
1530 * \param event the event in question
1531 * \param b_play whether to play the file immediately
1532 * \param b_playlist true to add to playlist, false to add to media library
1533 * \return nothing
1534 */
dropEventPlay(QDropEvent * event,bool b_play,bool b_playlist)1535 void MainInterface::dropEventPlay( QDropEvent *event, bool b_play, bool b_playlist )
1536 {
1537 if( event->possibleActions() & ( Qt::CopyAction | Qt::MoveAction | Qt::LinkAction ) )
1538 event->setDropAction( Qt::CopyAction );
1539 else
1540 return;
1541
1542 const QMimeData *mimeData = event->mimeData();
1543
1544 /* D&D of a subtitles file, add it on the fly */
1545 if( mimeData->urls().count() == 1 && THEMIM->getIM()->hasInput() )
1546 {
1547 if( !input_AddSlave( THEMIM->getInput(), SLAVE_TYPE_SPU,
1548 qtu( mimeData->urls()[0].toString() ), true, true, true ) )
1549 {
1550 event->accept();
1551 return;
1552 }
1553 }
1554
1555 bool first = b_play;
1556 foreach( const QUrl &url, mimeData->urls() )
1557 {
1558 if( url.isValid() )
1559 {
1560 QString mrl = toURI( url.toEncoded().constData() );
1561 #ifdef _WIN32
1562 QFileInfo info( url.toLocalFile() );
1563 if( info.exists() && info.isSymLink() )
1564 {
1565 QString target = info.symLinkTarget();
1566 QUrl url;
1567 if( QFile::exists( target ) )
1568 {
1569 url = QUrl::fromLocalFile( target );
1570 }
1571 else
1572 {
1573 url.setUrl( target );
1574 }
1575 mrl = toURI( url.toEncoded().constData() );
1576 }
1577 #endif
1578 if( mrl.length() > 0 )
1579 {
1580 Open::openMRL( p_intf, mrl, first, b_playlist );
1581 first = false;
1582 }
1583 }
1584 }
1585
1586 /* Browsers give content as text if you dnd the addressbar,
1587 so check if mimedata has valid url in text and use it
1588 if we didn't get any normal Urls()*/
1589 if( !mimeData->hasUrls() && mimeData->hasText() &&
1590 QUrl(mimeData->text()).isValid() )
1591 {
1592 QString mrl = toURI( mimeData->text() );
1593 Open::openMRL( p_intf, mrl, first, b_playlist );
1594 }
1595 event->accept();
1596 }
dragEnterEvent(QDragEnterEvent * event)1597 void MainInterface::dragEnterEvent(QDragEnterEvent *event)
1598 {
1599 event->acceptProposedAction();
1600 }
dragMoveEvent(QDragMoveEvent * event)1601 void MainInterface::dragMoveEvent(QDragMoveEvent *event)
1602 {
1603 event->acceptProposedAction();
1604 }
dragLeaveEvent(QDragLeaveEvent * event)1605 void MainInterface::dragLeaveEvent(QDragLeaveEvent *event)
1606 {
1607 event->accept();
1608 }
1609
1610 /************************************************************************
1611 * Events stuff
1612 ************************************************************************/
keyPressEvent(QKeyEvent * e)1613 void MainInterface::keyPressEvent( QKeyEvent *e )
1614 {
1615 handleKeyPress( e );
1616
1617 /* easter eggs sequence handling */
1618 if ( e->key() == kc[ i_kc_offset ] )
1619 i_kc_offset++;
1620 else
1621 i_kc_offset = 0;
1622
1623 if ( i_kc_offset == (sizeof( kc ) / sizeof( Qt::Key )) )
1624 {
1625 i_kc_offset = 0;
1626 emit kc_pressed();
1627 }
1628 }
1629
handleKeyPress(QKeyEvent * e)1630 void MainInterface::handleKeyPress( QKeyEvent *e )
1631 {
1632 if( ( ( e->modifiers() & Qt::ControlModifier ) && ( e->key() == Qt::Key_H ) ) ||
1633 ( b_minimalView && !b_videoFullScreen && e->key() == Qt::Key_Escape ) )
1634 {
1635 toggleMinimalView( !b_minimalView );
1636 e->accept();
1637 }
1638 else if( ( e->modifiers() & Qt::ControlModifier ) && ( e->key() == Qt::Key_K ) &&
1639 playlistWidget )
1640 {
1641 playlistWidget->setSearchFieldFocus();
1642 e->accept();
1643 }
1644
1645 int i_vlck = qtEventToVLCKey( e );
1646 if( i_vlck > 0 )
1647 {
1648 var_SetInteger( p_intf->obj.libvlc, "key-pressed", i_vlck );
1649 e->accept();
1650 }
1651 else
1652 e->ignore();
1653 }
1654
wheelEvent(QWheelEvent * e)1655 void MainInterface::wheelEvent( QWheelEvent *e )
1656 {
1657 int i_vlckey = qtWheelEventToVLCKey( e );
1658 var_SetInteger( p_intf->obj.libvlc, "key-pressed", i_vlckey );
1659 e->accept();
1660 }
1661
closeEvent(QCloseEvent * e)1662 void MainInterface::closeEvent( QCloseEvent *e )
1663 {
1664 // hide();
1665 if ( b_minimalView )
1666 setMinimalView( false );
1667 if( videoWidget )
1668 releaseVideoSlot();
1669 emit askToQuit(); /* ask THEDP to quit, so we have a unique method */
1670 /* Accept session quit. Otherwise we break the desktop mamager. */
1671 e->accept();
1672 }
1673
eventFilter(QObject * obj,QEvent * event)1674 bool MainInterface::eventFilter( QObject *obj, QEvent *event )
1675 {
1676 if ( event->type() == MainInterface::ToolbarsNeedRebuild ) {
1677 event->accept();
1678 recreateToolbars();
1679 return true;
1680 } else {
1681 return QObject::eventFilter( obj, event );
1682 }
1683 }
1684
toolBarConfUpdated()1685 void MainInterface::toolBarConfUpdated()
1686 {
1687 QApplication::postEvent( this, new QEvent( MainInterface::ToolbarsNeedRebuild ) );
1688 }
1689
setInterfaceFullScreen(bool fs)1690 void MainInterface::setInterfaceFullScreen( bool fs )
1691 {
1692 if( fs )
1693 setWindowState( windowState() | Qt::WindowFullScreen );
1694 else
1695 setWindowState( windowState() & ~Qt::WindowFullScreen );
1696 }
toggleInterfaceFullScreen()1697 void MainInterface::toggleInterfaceFullScreen()
1698 {
1699 b_interfaceFullScreen = !b_interfaceFullScreen;
1700 if( !b_videoFullScreen )
1701 setInterfaceFullScreen( b_interfaceFullScreen );
1702 emit fullscreenInterfaceToggled( b_interfaceFullScreen );
1703 }
1704
emitBoss()1705 void MainInterface::emitBoss()
1706 {
1707 emit askBoss();
1708 }
setBoss()1709 void MainInterface::setBoss()
1710 {
1711 THEMIM->pause();
1712 if( sysTray )
1713 {
1714 hide();
1715 }
1716 else
1717 {
1718 showMinimized();
1719 }
1720 }
1721
emitRaise()1722 void MainInterface::emitRaise()
1723 {
1724 emit askRaise();
1725 }
setRaise()1726 void MainInterface::setRaise()
1727 {
1728 activateWindow();
1729 raise();
1730 }
1731
voutReleaseMouseEvents()1732 void MainInterface::voutReleaseMouseEvents()
1733 {
1734 if (videoWidget)
1735 {
1736 QPoint pos = QCursor::pos();
1737 QPoint localpos = videoWidget->mapFromGlobal(pos);
1738 int buttons = QApplication::mouseButtons();
1739 int i_button = 1;
1740 while (buttons != 0)
1741 {
1742 if ( (buttons & 1) != 0 )
1743 {
1744 QMouseEvent new_e( QEvent::MouseButtonRelease, localpos,
1745 (Qt::MouseButton)i_button, (Qt::MouseButton)i_button, Qt::NoModifier );
1746 QApplication::sendEvent(videoWidget, &new_e);
1747 }
1748 buttons >>= 1;
1749 i_button <<= 1;
1750 }
1751
1752 }
1753 }
1754
1755 /*****************************************************************************
1756 * PopupMenuCB: callback triggered by the intf-popupmenu playlist variable.
1757 * We don't show the menu directly here because we don't want the
1758 * caller to block for a too long time.
1759 *****************************************************************************/
PopupMenuCB(vlc_object_t *,const char *,vlc_value_t,vlc_value_t new_val,void * param)1760 static int PopupMenuCB( vlc_object_t *, const char *,
1761 vlc_value_t, vlc_value_t new_val, void *param )
1762 {
1763 intf_thread_t *p_intf = (intf_thread_t *)param;
1764
1765 if( p_intf->pf_show_dialog )
1766 {
1767 p_intf->pf_show_dialog( p_intf, INTF_DIALOG_POPUPMENU,
1768 new_val.b_bool, NULL );
1769 }
1770
1771 return VLC_SUCCESS;
1772 }
1773
1774 /*****************************************************************************
1775 * IntfShowCB: callback triggered by the intf-toggle-fscontrol libvlc variable.
1776 *****************************************************************************/
IntfShowCB(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * param)1777 static int IntfShowCB( vlc_object_t *, const char *,
1778 vlc_value_t, vlc_value_t, void *param )
1779 {
1780 intf_thread_t *p_intf = (intf_thread_t *)param;
1781 p_intf->p_sys->p_mi->toggleFSC();
1782
1783 /* Show event */
1784 return VLC_SUCCESS;
1785 }
1786
1787 /*****************************************************************************
1788 * IntfRaiseMainCB: callback triggered by the intf-show-main libvlc variable.
1789 *****************************************************************************/
IntfRaiseMainCB(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * param)1790 static int IntfRaiseMainCB( vlc_object_t *, const char *,
1791 vlc_value_t, vlc_value_t, void *param )
1792 {
1793 intf_thread_t *p_intf = (intf_thread_t *)param;
1794 p_intf->p_sys->p_mi->emitRaise();
1795
1796 return VLC_SUCCESS;
1797 }
1798
1799 /*****************************************************************************
1800 * IntfBossCB: callback triggered by the intf-boss libvlc variable.
1801 *****************************************************************************/
IntfBossCB(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * param)1802 static int IntfBossCB( vlc_object_t *, const char *,
1803 vlc_value_t, vlc_value_t, void *param )
1804 {
1805 intf_thread_t *p_intf = (intf_thread_t *)param;
1806 p_intf->p_sys->p_mi->emitBoss();
1807
1808 return VLC_SUCCESS;
1809 }
1810