videowidget.cpp 12.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
/***************************************************************************
 *   Copyright (C) 2008 by Pino Toscano <pino@kde.org>                     *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

#include "videowidget.h"

// qt/kde includes
13
#include <qaction.h>
14
#include <qcoreapplication.h>
15
16
#include <qdir.h>
#include <qevent.h>
17
#include <qlabel.h>
18
#include <qlayout.h>
19
#include <qmenu.h>
20
#include <qstackedlayout.h>
21
#include <qtoolbar.h>
22
23
#include <qtoolbutton.h>
#include <qwidgetaction.h>
24
25
26

#include <kicon.h>
#include <klocale.h>
27

28
#include <phonon/mediaobject.h>
29
#include <phonon/seekslider.h>
30
31
32
#include <phonon/videoplayer.h>

#include "core/annotations.h"
33
#include "core/area.h"
34
35
#include "core/document.h"
#include "core/movie.h"
36
#include "snapshottaker.h"
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
static QAction* createToolBarButtonWithWidgetPopup( QToolBar* toolBar, QWidget *widget, const QIcon &icon )
{
    QToolButton *button = new QToolButton( toolBar );
    QAction *action = toolBar->addWidget( button );
    button->setAutoRaise( true );
    button->setIcon( icon );
    button->setPopupMode( QToolButton::InstantPopup );
    QMenu *menu = new QMenu( button );
    button->setMenu( menu );
    QWidgetAction *widgetAction = new QWidgetAction( menu );
    QWidget *dummy = new QWidget( menu );
    widgetAction->setDefaultWidget( dummy );
    QVBoxLayout *dummyLayout = new QVBoxLayout( dummy );
    dummyLayout->setMargin( 5 );
    dummyLayout->addWidget( widget );
    menu->addAction( widgetAction );
    return action;
}

57
58
59
60
/* Private storage. */
class VideoWidget::Private
{
public:
61
62
    Private( Okular::Movie *m, Okular::Document *doc, VideoWidget *qq )
        : q( qq ), movie( m ), document( doc ), player( 0 ), loaded( false )
63
64
65
    {
    }

66
67
68
69
70
71
    ~Private()
    {
        if ( player )
            player->stop();
    }

72
73
    enum PlayPauseMode { PlayMode, PauseMode };

74
    void load();
75
    void setupPlayPauseAction( PlayPauseMode mode );
76
77
78
    void setPosterImage( const QImage& );
    void takeSnapshot();
    void videoStopped();
79
    void stateChanged(Phonon::State, Phonon::State);
80
81
82

    // slots
    void finished();
83
    void playOrPause();
84
85

    VideoWidget *q;
86
    Okular::Movie *movie;
87
88
89
    Okular::Document *document;
    Okular::NormalizedRect geom;
    Phonon::VideoPlayer *player;
90
    Phonon::SeekSlider *seekSlider;
91
92
93
    QToolBar *controlBar;
    QAction *playPauseAction;
    QAction *stopAction;
94
95
    QAction *seekSliderAction;
    QAction *seekSliderMenuAction;
96
97
    QStackedLayout *pageLayout;
    QLabel *posterImagePage;
98
99
100
101
102
103
104
105
106
107
    bool loaded : 1;
};

void VideoWidget::Private::load()
{
    if ( loaded )
        return;

    loaded = true;

108
    QString url = movie->url();
109
110
111
112
113
114
115
116
117
118
119
120
121
122
    KUrl newurl;
    if ( QDir::isRelativePath( url ) )
    {
        newurl = document->currentDocument();
        newurl.setFileName( url );
    }
    else
    {
        newurl = url;
    }
    if ( newurl.isLocalFile() )
        player->load( newurl.toLocalFile() );
    else
        player->load( newurl );
123

124
125
126
    connect( player->mediaObject(), SIGNAL( stateChanged( Phonon::State, Phonon::State ) ),
             q, SLOT( stateChanged( Phonon::State, Phonon::State ) ) );

127
    seekSlider->setEnabled( true );
128
129
}

130
131
132
133
void VideoWidget::Private::setupPlayPauseAction( PlayPauseMode mode )
{
    if ( mode == PlayMode )
    {
Vishesh Handa's avatar
Vishesh Handa committed
134
        playPauseAction->setIcon( QIcon::fromTheme( "media-playback-start" ) );
135
136
137
138
        playPauseAction->setText( i18nc( "start the movie playback", "Play" ) );
    }
    else if ( mode == PauseMode )
    {
Vishesh Handa's avatar
Vishesh Handa committed
139
        playPauseAction->setIcon( QIcon::fromTheme( "media-playback-pause" ) );
140
141
142
143
        playPauseAction->setText( i18nc( "pause the movie playback", "Pause" ) );
    }
}

144
145
void VideoWidget::Private::takeSnapshot()
{
146
    const QString url = movie->url();
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
    KUrl newurl;
    if ( QDir::isRelativePath( url ) )
    {
        newurl = document->currentDocument();
        newurl.setFileName( url );
    }
    else
    {
        newurl = url;
    }

    SnapshotTaker *taker = 0;
    if ( newurl.isLocalFile() )
        taker = new SnapshotTaker( newurl.toLocalFile(), q );
    else
        taker = new SnapshotTaker( newurl.url(), q );

    q->connect( taker, SIGNAL( finished( const QImage& ) ), q, SLOT( setPosterImage( const QImage& ) ) );
}

void VideoWidget::Private::videoStopped()
{
169
    if ( movie->showPosterImage() )
170
171
172
173
174
        pageLayout->setCurrentIndex( 1 );
    else
        q->hide();
}

175
176
void VideoWidget::Private::finished()
{
177
    switch ( movie->playMode() )
178
179
180
181
    {
        case Okular::Movie::PlayOnce:
        case Okular::Movie::PlayOpen:
            // playback has ended, nothing to do
182
183
            stopAction->setEnabled( false );
            setupPlayPauseAction( PlayMode );
184
            if ( movie->playMode() == Okular::Movie::PlayOnce )
185
                controlBar->setVisible( false );
186
            videoStopped();
187
188
189
190
191
192
193
194
195
196
197
198
            break;
        case Okular::Movie::PlayRepeat:
            // repeat the playback
            player->play();
            break;
        case Okular::Movie::PlayPalindrome:
            // FIXME we should play backward, but we cannot
            player->play();
            break;
    }
}

199
200
201
202
203
204
205
206
207
208
209
210
211
void VideoWidget::Private::playOrPause()
{
    if ( player->isPlaying() )
    {
        player->pause();
        setupPlayPauseAction( PlayMode );
    }
    else
    {
        q->play();
    }
}

212
213
214
215
216
void VideoWidget::Private::setPosterImage( const QImage &image )
{
    if ( !image.isNull() )
    {
        // cache the snapshot image
217
        movie->setPosterImage( image );
218
219
220
221
222
    }

    posterImagePage->setPixmap( QPixmap::fromImage( image ) );
}

223
224
225
226
227
228
void VideoWidget::Private::stateChanged( Phonon::State newState, Phonon::State )
{
    if ( newState == Phonon::PlayingState )
        pageLayout->setCurrentIndex( 0 );
}

229
230
VideoWidget::VideoWidget( const Okular::Annotation *annotation, Okular::Movie *movie, Okular::Document *document, QWidget *parent )
    : QWidget( parent ), d( new Private( movie, document, this ) )
231
{
232
233
234
235
    // do not propagate the mouse events to the parent widget;
    // they should be tied to this widget, not spread around...
    setAttribute( Qt::WA_NoMousePropagation );

236
237
238
239
    // Setup player page
    QWidget *playerPage = new QWidget;

    QVBoxLayout *mainlay = new QVBoxLayout( playerPage );
240
    mainlay->setMargin( 0 );
241
    mainlay->setSpacing( 0 );
242

243
244
    d->player = new Phonon::VideoPlayer( Phonon::NoCategory, playerPage );
    d->player->installEventFilter( playerPage );
245
246
    mainlay->addWidget( d->player );

247
    d->controlBar = new QToolBar( playerPage );
248
249
250
251
252
253
254
255
    d->controlBar->setIconSize( QSize( 16, 16 ) );
    d->controlBar->setAutoFillBackground( true );
    mainlay->addWidget( d->controlBar );

    d->playPauseAction = new QAction( d->controlBar );
    d->controlBar->addAction( d->playPauseAction );
    d->setupPlayPauseAction( Private::PlayMode );
    d->stopAction = d->controlBar->addAction(
Vishesh Handa's avatar
Vishesh Handa committed
256
        QIcon::fromTheme( "media-playback-stop" ),
257
        i18nc( "stop the movie playback", "Stop" ),
Laurent Montel's avatar
Laurent Montel committed
258
        this, SLOT(stop()) );
259
    d->stopAction->setEnabled( false );
260
261
262
263
264
265
266
267
    d->controlBar->addSeparator();
    d->seekSlider = new Phonon::SeekSlider( d->player->mediaObject(), d->controlBar );
    d->seekSliderAction = d->controlBar->addWidget( d->seekSlider );
    d->seekSlider->setEnabled( false );

    Phonon::SeekSlider *verticalSeekSlider = new Phonon::SeekSlider( d->player->mediaObject(), 0 );
    verticalSeekSlider->setMaximumHeight( 100 );
    d->seekSliderMenuAction = createToolBarButtonWithWidgetPopup(
Vishesh Handa's avatar
Vishesh Handa committed
268
        d->controlBar, verticalSeekSlider, QIcon::fromTheme( "player-time" ) );
269
    d->seekSliderMenuAction->setVisible( false );
270

271
    d->controlBar->setVisible( movie->showControls() );
272

Laurent Montel's avatar
Laurent Montel committed
273
274
    connect( d->player, SIGNAL(finished()), this, SLOT(finished()) );
    connect( d->playPauseAction, SIGNAL(triggered()), this, SLOT(playOrPause()) );
275

276
    d->geom = annotation->transformedBoundingRectangle();
277
278
279
280
281
282
283
284
285
286
287
288
289
290

    // Setup poster image page
    d->posterImagePage = new QLabel;
    d->posterImagePage->setScaledContents( true );
    d->posterImagePage->installEventFilter( this );
    d->posterImagePage->setCursor( Qt::PointingHandCursor );

    d->pageLayout = new QStackedLayout( this );
    d->pageLayout->setMargin( 0 );
    d->pageLayout->setSpacing( 0 );
    d->pageLayout->addWidget( playerPage );
    d->pageLayout->addWidget( d->posterImagePage );


291
    if ( movie->showPosterImage() )
292
293
294
    {
        d->pageLayout->setCurrentIndex( 1 );

295
        const QImage posterImage = movie->posterImage();
296
297
298
299
300
301
302
303
304
305
306
307
308
        if ( posterImage.isNull() )
        {
            d->takeSnapshot();
        }
        else
        {
            d->setPosterImage( posterImage );
        }
    }
    else
    {
        d->pageLayout->setCurrentIndex( 0 );
    }
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
}

VideoWidget::~VideoWidget()
{
    delete d;
}

void VideoWidget::setNormGeometry( const Okular::NormalizedRect &rect )
{
    d->geom = rect;
}

Okular::NormalizedRect VideoWidget::normGeometry() const
{
    return d->geom;
}

326
327
328
329
330
bool VideoWidget::isPlaying() const
{
    return d->player->isPlaying();
}

331
332
333
334
335
void VideoWidget::pageInitialized()
{
    hide();
}

336
337
void VideoWidget::pageEntered()
{
338
    if ( d->movie->showPosterImage() )
339
340
341
342
343
    {
        d->pageLayout->setCurrentIndex( 1 );
        show();
    }

344
    if ( d->movie->autoPlay() )
345
    {
346
347
348
349
350
        show();
        QMetaObject::invokeMethod(this, "play", Qt::QueuedConnection);
    }
}

351
352
void VideoWidget::pageLeft()
{
353
    d->player->stop();
354
355
356
357
358
    d->videoStopped();

    hide();
}

359
360
void VideoWidget::play()
{
Jaan Vajakas's avatar
Jaan Vajakas committed
361
    d->controlBar->setVisible( d->movie->showControls() );
362
363
    d->load();
    d->player->play();
364
365
    d->stopAction->setEnabled( true );
    d->setupPlayPauseAction( Private::PauseMode );
366
367
368
369
370
}

void VideoWidget::stop()
{
    d->player->stop();
371
372
    d->stopAction->setEnabled( false );
    d->setupPlayPauseAction( Private::PlayMode );
373
374
375
376
377
}

void VideoWidget::pause()
{
    d->player->pause();
378
    d->setupPlayPauseAction( Private::PlayMode );
379
380
}

381
bool VideoWidget::eventFilter( QObject * object, QEvent * event )
382
{
383
    if ( object == d->player || object == d->posterImagePage )
384
    {
385
        switch ( event->type() )
386
        {
387
            case QEvent::MouseButtonPress:
388
            {
389
390
391
392
393
394
395
396
397
                QMouseEvent * me = static_cast< QMouseEvent * >( event );
                if ( me->button() == Qt::LeftButton )
                {
                    if ( !d->player->isPlaying() )
                    {
                        play();
                    }
                    event->accept();
                }
398
            }
399
400
401
402
403
404
405
406
407
408
409
410
            case QEvent::Wheel:
            {
                if ( object == d->posterImagePage )
                {
                    QWheelEvent * we = static_cast< QWheelEvent * >( event );

                    // forward wheel events to parent widget
                    QWheelEvent *copy = new QWheelEvent( we->pos(), we->globalPos(), we->delta(), we->buttons(), we->modifiers(), we->orientation() );
                    QCoreApplication::postEvent( parentWidget(), copy );
                }
                break;
            }
411
            default: ;
412
413
414
        }
    }

415
    return false;
416
417
}

418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
bool VideoWidget::event( QEvent * event )
{
    switch ( event->type() )
    {
        case QEvent::ToolTip:
            // "eat" the help events (= tooltips), avoid parent widgets receiving them
            event->accept();
            return true;
            break;
        default: ;
    }

    return QWidget::event( event );
}

void VideoWidget::resizeEvent( QResizeEvent * event )
{
    const QSize &s = event->size();
    int usedSpace = d->seekSlider->geometry().left() + d->seekSlider->iconSize().width();
    // try to give the slider at least 30px of space
    if ( s.width() < ( usedSpace + 30 ) )
    {
        d->seekSliderAction->setVisible( false );
        d->seekSliderMenuAction->setVisible( true );
    }
    else
    {
        d->seekSliderAction->setVisible( true );
        d->seekSliderMenuAction->setVisible( false );
    }
}

450
#include "moc_videowidget.cpp"