main_interface.cpp 53.3 KB
Newer Older
Clément Stenac's avatar
Clément Stenac committed
1
/*****************************************************************************
Clément Stenac's avatar
Wtf ?  
Clément Stenac committed
2
 * main_interface.cpp : Main interface
Clément Stenac's avatar
Clément Stenac committed
3
 ****************************************************************************
4
 * Copyright (C) 2006-2011 VideoLAN and AUTHORS
5
 * $Id$
Clément Stenac's avatar
Clément Stenac committed
6 7
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
8
 *          Jean-Baptiste Kempf <jb@videolan.org>
9
 *          Ilkka Ollakka <ileoo@videolan.org>
Clément Stenac's avatar
Clément Stenac committed
10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
22 23
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
 *****************************************************************************/
25

26 27 28
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
Clément Stenac's avatar
Clément Stenac committed
29

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
30
#include "qt.hpp"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
31

Clément Stenac's avatar
Clément Stenac committed
32
#include "main_interface.hpp"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
33 34
#include "input_manager.hpp"                    // Creation
#include "actions_manager.hpp"                  // killInstance
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
35

36
#include "util/customwidgets.hpp"               // qtEventToVLCKey, QVLCStackedWidget
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
37
#include "util/qt_dirs.hpp"                     // toNativeSeparators
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
38

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
39 40 41 42
#include "components/interface_widgets.hpp"     // bgWidget, videoWidget
#include "components/controller.hpp"            // controllers
#include "components/playlist/playlist.hpp"     // plWidget
#include "dialogs/firstrun.hpp"                 // First Run
43
#include "dialogs/playlist.hpp"                 // PlaylistDialog
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
44

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
45 46
#include "menus.hpp"                            // Menu creation
#include "recents.hpp"                          // RecentItems when DnD
47

Clément Stenac's avatar
Clément Stenac committed
48
#include <QCloseEvent>
49
#include <QKeyEvent>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
50

51
#include <QUrl>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
52
#include <QSize>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
53
#include <QDate>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
54
#include <QMimeData>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
55

56
#include <QMenu>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
57 58
#include <QMenuBar>
#include <QStatusBar>
59
#include <QLabel>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
60
#include <QStackedWidget>
61
#ifdef _WIN32
62
#include <QFileInfo>
63
#endif
64

65 66
#include <QTimer>

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
67 68
#include <vlc_keys.h>                       /* Wheel event */
#include <vlc_vout_display.h>               /* vout_thread_t and VOUT_ events */
69

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
70
// #define DEBUG_INTF
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
71

72 73 74 75 76
/* Callback prototypes */
static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
                        vlc_value_t old_val, vlc_value_t new_val, void *param );
static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
                       vlc_value_t old_val, vlc_value_t new_val, void *param );
77 78
static int IntfBossCB( vlc_object_t *p_this, const char *psz_variable,
                       vlc_value_t old_val, vlc_value_t new_val, void *param );
79 80 81
static int IntfRaiseMainCB( vlc_object_t *p_this, const char *psz_variable,
                           vlc_value_t old_val, vlc_value_t new_val,
                           void *param );
82

83 84 85
const QEvent::Type MainInterface::ToolbarsNeedRebuild =
        (QEvent::Type)QEvent::registerEventType();

86
MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
Clément Stenac's avatar
Clément Stenac committed
87
{
88
    /* Variables initialisation */
89 90 91
    bgWidget             = NULL;
    videoWidget          = NULL;
    playlistWidget       = NULL;
92
    stackCentralOldWidget= NULL;
93 94
    sysTray              = NULL;
    fullscreenControls   = NULL;
95
    cryptedLabel         = NULL;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
96 97
    controls             = NULL;
    inputC               = NULL;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
98

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
99
    b_hideAfterCreation  = false; // --qt-start-minimized
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
100
    playlistVisible      = false;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
101
    input_name           = "";
102
    b_interfaceFullScreen= false;
103
    b_hasPausedWhenMinimized = false;
104
    i_kc_offset          = false;
105
    b_maximizedView      = false;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
106

107
    /* Ask for Privacy */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
108
    FirstRun::CheckAndRun( this, p_intf );
109

110
    /**
111
     *  Configuration and settings
112
     *  Pre-building of interface
113
     **/
114 115
    /* Main settings */
    setFocusPolicy( Qt::StrongFocus );
116
    setAcceptDrops( true );
Nick Pope's avatar
Nick Pope committed
117
    setWindowRole( "vlc-main" );
118
    setWindowIcon( QApplication::windowIcon() );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
119
    setWindowOpacity( var_InheritFloat( p_intf, "qt-opacity" ) );
120
#ifdef Q_OS_MAC
121 122
    setAttribute( Qt::WA_MacBrushedMetal );
#endif
123

Rémi Denis-Courmont's avatar
Grammar  
Rémi Denis-Courmont committed
124
    /* Is video in embedded in the UI or not */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
125
    b_videoEmbedded = var_InheritBool( p_intf, "embedded-video" );
Clément Stenac's avatar
Clément Stenac committed
126

127
    /* Does the interface resize to video size or the opposite */
128
    b_autoresize = var_InheritBool( p_intf, "qt-video-autoresize" );
129

130
    /* Are we in the enhanced always-video mode or not ? */
131
    b_minimalView = var_InheritBool( p_intf, "qt-minimal-view" );
Clément Stenac's avatar
Clément Stenac committed
132

133
    /* Do we want anoying popups or not */
134
    i_notificationSetting = var_InheritInteger( p_intf, "qt-notification" );
135

136
    /* */
137 138
    b_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );

139
    /* Set the other interface settings */
140
    settings = getSettings();
141

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
142
    /* */
143
    b_plDocked = getSettings()->value( "MainWindow/pl-dock-status", true ).toBool();
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
144

145

146 147 148
    /**************************
     *  UI and Widgets design
     **************************/
149
    setVLCWindowsTitle();
150

151 152 153
    /************
     * Menu Bar *
     ************/
154
    VLCMenuBar::createMenuBar( this, p_intf );
155
    CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ),
156
             THEDP, destroyPopupMenu() );
157

158
    createMainWidget( settings );
159 160 161 162 163 164 165

    /**************
     * Status Bar *
     **************/
    createStatusBar();
    setStatusBarVisibility( getSettings()->value( "MainWindow/status-bar-visible", false ).toBool() );

166 167 168 169 170
    /*********************************
     * Create the Systray Management *
     *********************************/
    initSystray();

171 172 173 174
    /*************************************************************
     * Connect the input manager to the GUI elements it manages  *
     * Beware initSystray did some connects on input manager too *
     *************************************************************/
175 176
    /**
     * Connects on nameChanged()
177 178 179
     * Those connects are different because options can impeach them to trigger.
     **/
    /* Main Interface statusbar */
Rémi Duraffort's avatar
Rémi Duraffort committed
180 181
    CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
             this, setName( const QString& ) );
182
    /* and title of the Main Interface*/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
183
    if( var_InheritBool( p_intf, "qt-name-in-title" ) )
184
    {
185 186
        CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
                 this, setVLCWindowsTitle( const QString& ) );
187
    }
188 189
    CONNECT( THEMIM, inputChanged( bool ), this, onInputChanged( bool ) );

190 191
    /* END CONNECTS ON IM */

192
    /* VideoWidget connects for asynchronous calls */
193
    b_videoFullScreen = false;
194 195
    connect( this, SIGNAL(askGetVideo(WId*, struct vout_window_t*, unsigned, unsigned, bool)),
             this, SLOT(getVideoSlot(WId*, struct vout_window_t*, unsigned, unsigned, bool)),
196
             Qt::BlockingQueuedConnection );
197
    connect( this, SIGNAL(askReleaseVideo( void )),
198 199
             this, SLOT(releaseVideoSlot( void )),
             Qt::BlockingQueuedConnection );
200
    CONNECT( this, askVideoOnTop(bool), this, setVideoOnTop(bool));
201

202
    if( videoWidget )
203
    {
204 205 206
        if( b_autoresize )
        {
            CONNECT( videoWidget, sizeChanged( int, int ),
207
                     this, videoSizeChanged( int,  int ) );
208
        }
209 210 211
        CONNECT( this, askVideoToResize( unsigned int, unsigned int ),
                 this, setVideoSize( unsigned int, unsigned int ) );

212
        CONNECT( this, askVideoSetFullScreen( bool ),
213
                 this, setVideoFullScreen( bool ) );
214 215
        CONNECT( this, askHideMouse( bool ),
                 this, setHideMouse( bool ) );
216 217
    }

218 219
    CONNECT( THEDP, toolBarConfUpdated(), this, toolBarConfUpdated() );
    installEventFilter( this );
220

221 222
    CONNECT( this, askToQuit(), THEDP, quit() );

223
    CONNECT( this, askBoss(), this, setBoss() );
224
    CONNECT( this, askRaise(), this, setRaise() );
225

226 227 228
    /** END of CONNECTS**/


229 230 231
    /************
     * Callbacks
     ************/
232 233 234
    var_AddCallback( p_intf->obj.libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
    var_AddCallback( p_intf->obj.libvlc, "intf-boss", IntfBossCB, p_intf );
    var_AddCallback( p_intf->obj.libvlc, "intf-show", IntfRaiseMainCB, p_intf );
235 236

    /* Register callback for the intf-popupmenu variable */
237
    var_AddCallback( p_intf->obj.libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
238

239

240
    /* Final Sizing, restoration and placement of the interface */
241
    if( settings->value( "MainWindow/playlist-visible", false ).toBool() )
242 243
        togglePlaylist();

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
244
    QVLCTools::restoreWidgetPosition( settings, this, QSize(600, 420) );
245

246 247
    b_interfaceFullScreen = isFullScreen();

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
248
    setVisible( !b_hideAfterCreation );
249

250
    /* Switch to minimal view if needed, must be called after the show() */
251
    if( b_minimalView )
252
        toggleMinimalView( true );
253 254

    computeMinimumSize();
255 256 257 258
}

MainInterface::~MainInterface()
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
259
    /* Unsure we hide the videoWidget before destroying it */
260
    if( stackCentralOldWidget == videoWidget )
261
        showTab( bgWidget );
262

263 264
    if( videoWidget )
        releaseVideoSlot();
265

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
266
    /* Be sure to kill the actionsManager... Only used in the MI and control */
267 268
    ActionsManager::killInstance();

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
269
    /* Delete the FSC controller */
270
    delete fullscreenControls;
271

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
272
    /* Save states */
273

274
    settings->beginGroup("MainWindow");
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
275
    settings->setValue( "pl-dock-status", b_plDocked );
276

277
    /* Save playlist state */
278
    settings->setValue( "playlist-visible", playlistVisible );
279

280
    settings->setValue( "adv-controls",
281
                        getControlsVisibilityStatus() & CONTROLS_ADVANCED );
282
    settings->setValue( "status-bar-visible", b_statusbarVisible );
283

284 285 286
    /* Save the stackCentralW sizes */
    settings->setValue( "bgSize", stackWidgetsSizes[bgWidget] );
    settings->setValue( "playlistSize", stackWidgetsSizes[playlistWidget] );
287
    settings->endGroup();
288

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
289
    /* Save this size */
290
    QVLCTools::saveWidgetPosition(settings, this);
291

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
292
    /* Unregister callbacks */
293 294 295 296
    var_DelCallback( p_intf->obj.libvlc, "intf-boss", IntfBossCB, p_intf );
    var_DelCallback( p_intf->obj.libvlc, "intf-show", IntfRaiseMainCB, p_intf );
    var_DelCallback( p_intf->obj.libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
    var_DelCallback( p_intf->obj.libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
297

298
    p_intf->p_sys->p_mi = NULL;
299 300
}

301 302
void MainInterface::computeMinimumSize()
{
303
    int minWidth = 80;
304
    if( menuBar()->isVisible() )
305
        minWidth += controls->sizeHint().width();
306 307 308 309

    setMinimumWidth( minWidth );
}

310 311 312
/*****************************
 *   Main UI handling        *
 *****************************/
313 314
void MainInterface::recreateToolbars()
{
315 316
    bool b_adv = getControlsVisibilityStatus() & CONTROLS_ADVANCED;

317 318
    delete controls;
    delete inputC;
319

320
    controls = new ControlsWidget( p_intf, b_adv, this );
321
    inputC = new InputControlsWidget( p_intf, this );
322
    mainLayout->insertWidget( 2, inputC );
323
    mainLayout->insertWidget( settings->value( "MainWindow/ToolbarPos", 0 ).toInt() ? 0: 3,
324
                              controls );
325

326 327 328 329 330 331
    if( fullscreenControls )
    {
        delete fullscreenControls;
        fullscreenControls = new FullscreenControllerWidget( p_intf, this );
        CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
                 this, handleKeyPress( QKeyEvent * ) );
332
        THEMIM->requestVoutUpdate();
333
    }
334 335

    setMinimalView( b_minimalView );
336 337
}

338 339
void MainInterface::reloadPrefs()
{
340
    i_notificationSetting = var_InheritInteger( p_intf, "qt-notification" );
341 342 343 344 345 346 347 348
    b_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );
    if( !var_InheritBool( p_intf, "qt-fs-controller" ) && fullscreenControls )
    {
        delete fullscreenControls;
        fullscreenControls = NULL;
    }
}

349
void MainInterface::createResumePanel( QWidget *w )
350
{
351 352 353 354
    resumePanel = new QWidget( w );
    resumePanel->hide();
    QHBoxLayout *resumePanelLayout = new QHBoxLayout( resumePanel );
    resumePanelLayout->setSpacing( 0 ); resumePanelLayout->setMargin( 0 );
355

356 357 358 359
    QLabel *continuePixmapLabel = new QLabel();
    continuePixmapLabel->setPixmap( QPixmap( ":/menu/help" ) );
    continuePixmapLabel->setContentsMargins( 5, 0, 5, 0 );

360
    QLabel *continueLabel = new QLabel( qtr( "Do you want to restart the playback where left off?") );
361

362
    QToolButton *cancel = new QToolButton( resumePanel );
363 364
    cancel->setAutoRaise( true );
    cancel->setText( "X" );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
365 366

    QPushButton *ok = new QPushButton( qtr( "&Continue" )  );
367

368
    resumePanelLayout->addWidget( continuePixmapLabel );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
369
    resumePanelLayout->addWidget( continueLabel );
370 371 372
    resumePanelLayout->addStretch( 1 );
    resumePanelLayout->addWidget( ok );
    resumePanelLayout->addWidget( cancel );
373

374 375 376
    resumeTimer = new QTimer( resumePanel );
    resumeTimer->setSingleShot( true );
    resumeTimer->setInterval( 6000 );
377

378 379 380
    CONNECT( resumeTimer, timeout(), this, hideResumePanel() );
    CONNECT( cancel, clicked(), this, hideResumePanel() );
    CONNECT( THEMIM->getIM(), resumePlayback(int64_t), this, showResumePanel(int64_t) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
381
    BUTTONACT( ok, resumePlayback() );
382

383
    w->layout()->addWidget( resumePanel );
384 385
}

386
void MainInterface::showResumePanel( int64_t _time ) {
387 388 389 390 391
    int setting = var_InheritInteger( p_intf, "qt-continue" );

    if( setting == 0 )
        return;

392
    i_resumeTime = _time;
393 394

    if( setting == 2)
395
        resumePlayback();
396 397
    else
    {
398 399
        if( !isFullScreen() && !isMaximized() )
            resize( width(), height() + resumePanel->height() );
400 401
        resumePanel->setVisible(true);
        resumeTimer->start();
402 403 404
    }
}

405
void MainInterface::hideResumePanel()
406
{
407
    if( resumePanel->isVisible() )
408 409
    {
        if( !isFullScreen() && !isMaximized() )
410 411 412
            resize( width(), height() - resumePanel->height() );
        resumePanel->hide();
        resumeTimer->stop();
413 414 415
    }
}

416
void MainInterface::resumePlayback()
417
{
418
    if( THEMIM->getIM()->hasInput() ) {
419
        var_SetInteger( THEMIM->getInput(), "time", i_resumeTime );
420
    }
421
    hideResumePanel();
422 423
}

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
void MainInterface::onInputChanged( bool hasInput )
{
    if( hasInput == false )
        return;
    int autoRaise = var_InheritInteger( p_intf, "qt-auto-raise" );
    if ( autoRaise == MainInterface::RAISE_NEVER )
        return;
    if( THEMIM->getIM()->hasVideo() == true )
    {
        if( ( autoRaise & MainInterface::RAISE_VIDEO ) == 0 )
            return;
    }
    else if ( ( autoRaise & MainInterface::RAISE_AUDIO ) == 0 )
        return;
    emit askRaise();
}

441
void MainInterface::createMainWidget( QSettings *creationSettings )
442
{
443
    /* Create the main Widget and the mainLayout */
444
    QWidget *main = new QWidget;
445
    setCentralWidget( main );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
446
    mainLayout = new QVBoxLayout( main );
447
    main->setContentsMargins( 0, 0, 0, 0 );
448
    mainLayout->setSpacing( 0 ); mainLayout->setMargin( 0 );
449

450
    createResumePanel( main );
451
    /* */
452
    stackCentralW = new QVLCStackedWidget( main );
453

454
    /* Bg Cone */
455 456 457 458 459 460 461 462 463
    if ( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY
         && var_InheritBool( p_intf, "qt-icon-change" ) )
    {
        bgWidget = new EasterEggBackgroundWidget( p_intf );
        CONNECT( this, kc_pressed(), bgWidget, animate() );
    }
    else
        bgWidget = new BackgroundWidget( p_intf );

464
    stackCentralW->addWidget( bgWidget );
465 466 467 468 469
    if ( !var_InheritBool( p_intf, "qt-bgcone" ) )
        bgWidget->setWithArt( false );
    else
        if ( var_InheritBool( p_intf, "qt-bgcone-expands" ) )
            bgWidget->setExpandstoHeight( true );
470

471
    /* And video Outputs */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
472
    if( b_videoEmbedded )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
473
    {
474
        videoWidget = new VideoWidget( p_intf, stackCentralW );
475
        stackCentralW->addWidget( videoWidget );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
476
    }
477
    mainLayout->insertWidget( 1, stackCentralW );
478

479 480
    stackWidgetsSizes[bgWidget] =
        creationSettings->value( "MainWindow/bgSize", QSize( 600, 0 ) ).toSize();
481 482
    /* Resize even if no-auto-resize, because we are at creation */
    resizeStack( stackWidgetsSizes[bgWidget].width(), stackWidgetsSizes[bgWidget].height() );
Jakob Leben's avatar
Jakob Leben committed
483

484 485
    /* Create the CONTROLS Widget */
    controls = new ControlsWidget( p_intf,
486
        creationSettings->value( "MainWindow/adv-controls", false ).toBool(), this );
487 488
    inputC = new InputControlsWidget( p_intf, this );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
489
    mainLayout->insertWidget( 2, inputC );
490 491 492
    mainLayout->insertWidget(
        creationSettings->value( "MainWindow/ToolbarPos", 0 ).toInt() ? 0: 3,
        controls );
493

494
    /* Visualisation, disabled for now, they SUCK */
495 496 497 498 499 500
    #if 0
    visualSelector = new VisualSelector( p_intf );
    mainLayout->insertWidget( 0, visualSelector );
    visualSelector->hide();
    #endif

501

502 503 504
    /* Enable the popup menu in the MI */
    main->setContextMenuPolicy( Qt::CustomContextMenu );
    CONNECT( main, customContextMenuRequested( const QPoint& ),
505
             THEDP, setPopupMenu() );
506

507 508 509 510 511 512 513 514
    if ( depth() > 8 ) /* 8bit depth has too many issues with opacity */
        /* Create the FULLSCREEN CONTROLS Widget */
        if( var_InheritBool( p_intf, "qt-fs-controller" ) )
        {
            fullscreenControls = new FullscreenControllerWidget( p_intf, this );
            CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
                     this, handleKeyPress( QKeyEvent * ) );
        }
Clément Stenac's avatar
Clément Stenac committed
515 516
}

517 518 519
inline void MainInterface::initSystray()
{
    bool b_systrayAvailable = QSystemTrayIcon::isSystemTrayAvailable();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
520
    bool b_systrayWanted = var_InheritBool( p_intf, "qt-system-tray" );
521

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
522
    if( var_InheritBool( p_intf, "qt-start-minimized") )
523 524 525 526 527 528 529 530 531 532 533 534 535 536
    {
        if( b_systrayAvailable )
        {
            b_systrayWanted = true;
            b_hideAfterCreation = true;
        }
        else
            msg_Err( p_intf, "cannot start minimized without system tray bar" );
    }

    if( b_systrayAvailable && b_systrayWanted )
        createSystray();
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
537 538 539 540 541 542 543 544 545
inline void MainInterface::createStatusBar()
{
    /****************
     *  Status Bar  *
     ****************/
    /* Widgets Creation*/
    QStatusBar *statusBarr = statusBar();

    TimeLabel *timeLabel = new TimeLabel( p_intf );
546
    nameLabel = new ClickableQLabel();
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
547 548
    nameLabel->setTextInteractionFlags( Qt::TextSelectableByMouse
                                      | Qt::TextSelectableByKeyboard );
549
    SpeedLabel *speedLabel = new SpeedLabel( p_intf, this );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
550 551 552 553 554

    /* Styling those labels */
    timeLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
    speedLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
    nameLabel->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel);
555 556 557 558
    timeLabel->setStyleSheet(
            "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
    speedLabel->setStyleSheet(
            "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
559 560
    /* pad both label and its tooltip */
    nameLabel->setStyleSheet( "padding-left: 5px; padding-right: 5px;" );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
561 562 563 564 565 566

    /* and adding those */
    statusBarr->addWidget( nameLabel, 8 );
    statusBarr->addPermanentWidget( speedLabel, 0 );
    statusBarr->addPermanentWidget( timeLabel, 0 );

567
    CONNECT( nameLabel, doubleClicked(), THEDP, epgDialog() );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
568 569 570 571
    /* timeLabel behaviour:
       - double clicking opens the goto time dialog
       - right-clicking and clicking just toggle between remaining and
         elapsed time.*/
572
    CONNECT( timeLabel, doubleClicked(), THEDP, gotoTimeDialog() );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
573 574 575

    CONNECT( THEMIM->getIM(), encryptionChanged( bool ),
             this, showCryptedLabel( bool ) );
576

577 578
    CONNECT( THEMIM->getIM(), seekRequested( float ),
             timeLabel, setDisplayPosition( float ) );
579 580 581 582 583 584 585 586

    /* This shouldn't be necessary, but for somehow reason, the statusBarr
       starts at height of 20px and when a text is shown it needs more space.
       But, as the QMainWindow policy doesn't allow statusBar to change QMW's
       geometry, we need to force a height. If you have a better idea, please
       tell me -- jb
     */
    statusBarr->setFixedHeight( statusBarr->sizeHint().height() + 2 );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
587 588
}

589
/**********************************************************************
590
 * Handling of sizing of the components
591
 **********************************************************************/
592

593 594
void MainInterface::debug()
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
595
#ifdef DEBUG_INTF
596 597 598 599 600 601
    if( controls ) {
        msg_Dbg( p_intf, "Controls size: %i - %i", controls->size().height(), controls->size().width() );
        msg_Dbg( p_intf, "Controls minimumsize: %i - %i", controls->minimumSize().height(), controls->minimumSize().width() );
        msg_Dbg( p_intf, "Controls sizeHint: %i - %i", controls->sizeHint().height(), controls->sizeHint().width() );
    }

602 603
    msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
    msg_Dbg( p_intf, "sizeHint: %i - %i", sizeHint().height(), sizeHint().width() );
604
    msg_Dbg( p_intf, "minimumsize: %i - %i", minimumSize().height(), minimumSize().width() );
605

606
    msg_Dbg( p_intf, "Stack size: %i - %i", stackCentralW->size().height(), stackCentralW->size().width() );
607
    msg_Dbg( p_intf, "Stack sizeHint: %i - %i", stackCentralW->sizeHint().height(), stackCentralW->sizeHint().width() );
608
    msg_Dbg( p_intf, "Central size: %i - %i", centralWidget()->size().height(), centralWidget()->size().width() );
609
#endif
610 611
}

612
inline void MainInterface::showVideo() { showTab( videoWidget ); }
613 614
inline void MainInterface::restoreStackOldWidget()
            { showTab( stackCentralOldWidget ); }
615 616

inline void MainInterface::showTab( QWidget *widget )
617
{
618
    if ( !widget ) widget = bgWidget; /* trying to restore a null oldwidget */
619
#ifdef DEBUG_INTF
620 621 622 623 624
    if ( stackCentralOldWidget )
        msg_Dbg( p_intf, "Old stackCentralOldWidget %s at index %i",
                 stackCentralOldWidget->metaObject()->className(),
                 stackCentralW->indexOf( stackCentralOldWidget ) );
    msg_Dbg( p_intf, "ShowTab request for %s", widget->metaObject()->className() );
625
#endif
François Cartegnie's avatar
typo  
François Cartegnie committed
626
    /* fixing when the playlist has been undocked after being hidden.
627 628 629 630
       restoreStackOldWidget() is called when video stops but
       stackCentralOldWidget would still be pointing to playlist */
    if ( widget == playlistWidget && !isPlDocked() )
        widget = bgWidget;
631

632
    stackCentralOldWidget = stackCentralW->currentWidget();
633 634
    stackWidgetsSizes[stackCentralOldWidget] = stackCentralW->size();

635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
    /* If we are playing video, embedded */
    if( videoWidget && THEMIM->getIM()->hasVideo() )
    {
        /* Video -> Playlist */
        if( videoWidget == stackCentralOldWidget && widget == playlistWidget )
        {
            stackCentralW->removeWidget( videoWidget );
            videoWidget->show(); videoWidget->raise();
        }

        /* Playlist -> Video */
        if( playlistWidget == stackCentralOldWidget && widget == videoWidget )
        {
            playlistWidget->artContainer->removeWidget( videoWidget );
            videoWidget->show(); videoWidget->raise();
            stackCentralW->addWidget( videoWidget );
        }
652 653 654 655

        /* Embedded playlist -> Non-embedded playlist */
        if( bgWidget == stackCentralOldWidget && widget == videoWidget )
        {
656 657 658
            /* In rare case when video is started before the interface */
            if( playlistWidget != NULL )
                playlistWidget->artContainer->removeWidget( videoWidget );
659 660 661 662
            videoWidget->show(); videoWidget->raise();
            stackCentralW->addWidget( videoWidget );
            stackCentralW->setCurrentWidget( videoWidget );
        }
663 664
    }

665
    stackCentralW->setCurrentWidget( widget );
666 667
    if( b_autoresize )
        resizeStack( stackWidgetsSizes[widget].width(), stackWidgetsSizes[widget].height() );
668

669
#ifdef DEBUG_INTF
670 671 672 673 674 675
    msg_Dbg( p_intf, "Stack state changed to %s, index %i",
              stackCentralW->currentWidget()->metaObject()->className(),
              stackCentralW->currentIndex() );
    msg_Dbg( p_intf, "New stackCentralOldWidget %s at index %i",
              stackCentralOldWidget->metaObject()->className(),
              stackCentralW->indexOf( stackCentralOldWidget ) );
676
#endif
677 678 679 680 681 682 683 684

    /* This part is done later, to account for the new pl size */
    if( videoWidget && THEMIM->getIM()->hasVideo() &&
        videoWidget == stackCentralOldWidget && widget == playlistWidget )
    {
        playlistWidget->artContainer->addWidget( videoWidget );
        playlistWidget->artContainer->setCurrentWidget( videoWidget );
    }
685
}
686

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
687 688 689 690
void MainInterface::toggleFSC()
{
   if( !fullscreenControls ) return;

691
   IMEvent *eShow = new IMEvent( IMEvent::FullscreenControlToggle );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
692 693 694
   QApplication::postEvent( fullscreenControls, eShow );
}

695 696 697
/****************************************************************************
 * Video Handling
 ****************************************************************************/
698

699
/**
700
 * NOTE:
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
701 702
 * You must not change the state of this object or other Qt UI objects,
 * from the video output thread - only from the Qt UI main loop thread.
703 704 705
 * All window provider queries must be handled through signals or events.
 * That's why we have all those emit statements...
 */
706
WId MainInterface::getVideo( struct vout_window_t *p_wnd,
707
                             unsigned int i_width, unsigned int i_height,
708
                             bool fullscreen )
709 710 711 712 713 714 715
{
    if( !videoWidget )
        return 0;

    /* This is a blocking call signal. Results are returned through pointers.
     * Beware of deadlocks! */
    WId id;
716
    emit askGetVideo( &id, p_wnd, i_width, i_height, fullscreen );
717 718 719
    return id;
}

720
void MainInterface::getVideoSlot( WId *p_id, struct vout_window_t *p_wnd,
721
                                  unsigned i_width, unsigned i_height,
722
                                  bool fullscreen )
723
{
724 725 726 727
    /* Hidden or minimized, activate */
    if( isHidden() || isMinimized() )
        toggleUpdateSystrayMenu();

728
    /* Request the videoWidget */
729
    WId ret = videoWidget->request( p_wnd );
730
    *p_id = ret;
731
    if( ret ) /* The videoWidget is available */
732
    {
733 734
        setVideoFullScreen( fullscreen );

735
        /* Consider the video active now */
736
        showVideo();
737 738

        /* Ask videoWidget to resize correctly, if we are in normal mode */
739
        if( b_autoresize )
740
            videoWidget->setSize( i_width, i_height );
741
    }
742 743
}

744
/* Asynchronous call from the WindowClose function */
745
void MainInterface::releaseVideo( void )
746
{
747
    emit askReleaseVideo();
748 749
}

750
/* Function that is CONNECTED to the previous emit */
751
void MainInterface::releaseVideoSlot( void )
752
{
753 754
    /* This function is called when the embedded video window is destroyed,
     * or in the rare case that the embedded window is still here but the
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
755
     * Qt interface exits. */
756 757
    assert( videoWidget );
    videoWidget->release();
758
    setVideoOnTop( false );
759
    setVideoFullScreen( false );
760
    hideResumePanel();
Clément Stenac's avatar
Clément Stenac committed
761

Jakob Leben's avatar
Jakob Leben committed
762 763
    if( stackCentralW->currentWidget() == videoWidget )
        restoreStackOldWidget();
764 765
    else if( playlistWidget &&
             playlistWidget->artContainer->currentWidget() == videoWidget )
766 767 768 769
    {
        playlistWidget->artContainer->setCurrentIndex( 0 );
        stackCentralW->addWidget( videoWidget );
    }
Clément Stenac's avatar
Clément Stenac committed
770

771 772
    /* We don't want to have a blank video to popup */
    stackCentralOldWidget = bgWidget;
773 774
}

775 776
void MainInterface::setVideoSize( unsigned int w, unsigned int h )
{
777 778 779 780 781
    if (!isFullScreen() && !isMaximized() )
    {
        /* Resize video widget to video size, or keep it at the same
         * size. Call setSize() either way so that vout_window_ReportSize
         * will always get called.
782 783
         * If the video size is too large for the screen, resize it
         * to the screen size.
784 785
         */
        if (b_autoresize)
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
        {
            QRect screen = QApplication::desktop()->availableGeometry();
            if( h > screen.height() )
            {
                w = screen.width();
                h = screen.height();
                if( !b_minimalView )
                {
                    if( menuBar()->isVisible() )
                        h -= menuBar()->height();
                    if( controls->isVisible() )
                        h -= controls->height();
                    if( statusBar()->isVisible() )
                        h -= statusBar()->height();
                    if( inputC->isVisible() )
                        h -= inputC->height();
                }
                h -= style()->pixelMetric(QStyle::PM_TitleBarHeight);
                h -= style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
                h -= 2 * style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
            }
807
            videoWidget->setSize( w, h );
808
        }
809 810 811
        else
            videoWidget->setSize( videoWidget->width(), videoWidget->height() );
    }
812 813
}

814 815 816 817 818 819
void MainInterface::videoSizeChanged( int w, int h )
{
    if( !playlistWidget || playlistWidget->artContainer->currentWidget() != videoWidget )
        resizeStack( w, h );
}

820 821 822
void MainInterface::setVideoFullScreen( bool fs )
{
    b_videoFullScreen = fs;
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
    if( fs )
    {
        int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
        /* if user hasn't defined screennumber, or screennumber that is bigger
         * than current number of screens, take screennumber where current interface
         * is
         */
        if( numscreen == -1 || numscreen > QApplication::desktop()->numScreens() )
            numscreen = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );

        QRect screenres = QApplication::desktop()->screenGeometry( numscreen );

        /* To be sure window is on proper-screen in xinerama */
        if( !screenres.contains( pos() ) )
        {
838 839 840
            lastWinPosition = pos();
            lastWinSize = size();
            msg_Dbg( p_intf, "Moving video to correct position");
841 842
            move( QPoint( screenres.x(), screenres.y() ) );
        }
843 844

        /* */
Edward Wang's avatar
Edward Wang committed
845
        if( playlistWidget != NULL && playlistWidget->artContainer->currentWidget() == videoWidget )
846 847 848 849 850
        {
            showTab( videoWidget );
        }

        /* */
851
        displayNormalView();
852 853 854 855
        setInterfaceFullScreen( true );
    }
    else
    {
856
        setMinimalView( b_minimalView );
857
        setInterfaceFullScreen( b_interfaceFullScreen );
858 859 860 861 862 863 864 865
        if( lastWinPosition.isNull() == false )
        {
            move( lastWinPosition );
            resize( lastWinSize );
            lastWinPosition = QPoint();
            lastWinSize = QSize();
        }

866 867
    }
    videoWidget->sync();
868 869
}

870 871 872 873 874
void MainInterface::setHideMouse( bool hide )
{
    videoWidget->setCursor( hide ? Qt::BlankCursor : Qt::ArrowCursor );
}

875 876 877 878 879 880
/* Slot to change the video always-on-top flag.
 * Emit askVideoOnTop() to invoke this from other thread. */
void MainInterface::setVideoOnTop( bool on_top )
{
    Qt::WindowFlags oldflags = windowFlags(), newflags;

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
881
    if( on_top )
882 883 884
        newflags = oldflags | Qt::WindowStaysOnTopHint;
    else
        newflags = oldflags & ~Qt::WindowStaysOnTopHint;
885
    if( newflags != oldflags && !b_videoFullScreen )
886 887 888 889 890 891 892

    {
        setWindowFlags( newflags );
        show(); /* necessary to apply window flags */
    }
}

893
/* Asynchronous call from WindowControl function */
894
int MainInterface::controlVideo( int i_query, va_list args )
895 896 897
{
    switch( i_query )
    {
898 899 900 901
    case VOUT_WINDOW_SET_SIZE:
    {
        unsigned int i_width  = va_arg( args, unsigned int );
        unsigned int i_height = va_arg( args, unsigned int );
902

903
        emit askVideoToResize( i_width, i_height );
904
        return VLC_SUCCESS;
905
    }
906
    case VOUT_WINDOW_SET_STATE:
907
    {
908 909
        unsigned i_arg = va_arg( args, unsigned );
        unsigned on_top = i_arg & VOUT_WINDOW_STATE_ABOVE;
910 911

        emit askVideoOnTop( on_top != 0 );
912 913
        return VLC_SUCCESS;
    }
914 915 916
    case VOUT_WINDOW_SET_FULLSCREEN:
    {
        bool b_fs = va_arg( args, int );
917

918 919 920
        emit askVideoSetFullScreen( b_fs );
        return VLC_SUCCESS;
    }
921 922 923 924 925 926 927
    case VOUT_WINDOW_HIDE_MOUSE:
    {
        bool b_hide = va_arg( args, int );

        emit askHideMouse( b_hide );
        return VLC_SUCCESS;
    }