Plasma
videowidget.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2009 Marco Martin <notmart@gmail.com> 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU Library General Public License as 00006 * published by the Free Software Foundation; either version 2, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details 00013 * 00014 * You should have received a copy of the GNU Library General Public 00015 * License along with this program; if not, write to the 00016 * Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "videowidget.h" 00021 00022 #include "config-plasma.h" 00023 00024 #include <QUrl> 00025 #include <QTimer> 00026 #include <QGraphicsLinearLayout> 00027 #include <QGraphicsSceneResizeEvent> 00028 00029 #include <kicon.h> 00030 #include <kiconloader.h> 00031 00032 #ifndef PLASMA_NO_KIO 00033 #include <kfiledialog.h> 00034 #else 00035 #include <QFileDialog> 00036 #endif 00037 00038 #include <phonon/videowidget.h> 00039 #include <phonon/mediaobject.h> 00040 #include <phonon/mediasource.h> 00041 #include <phonon/audiooutput.h> 00042 00043 #include <plasma/animations/animation.h> 00044 #include <plasma/widgets/iconwidget.h> 00045 #include <plasma/widgets/slider.h> 00046 #include <plasma/widgets/frame.h> 00047 00048 namespace Plasma 00049 { 00050 00051 class VideoWidgetPrivate 00052 { 00053 public: 00054 VideoWidgetPrivate(VideoWidget *video) 00055 : q(video), 00056 ticking(false), 00057 forceControlsVisible(false), 00058 animation(0), 00059 hideTimer(0), 00060 shownControls(VideoWidget::NoControls), 00061 controlsWidget(0), 00062 previousButton(0), 00063 playButton(0), 00064 pauseButton(0), 00065 stopButton(0), 00066 playPauseButton(0), 00067 nextButton(0), 00068 progress(0), 00069 volume(0), 00070 openFileButton(0) 00071 { 00072 } 00073 00074 ~VideoWidgetPrivate() 00075 { 00076 } 00077 00078 void playPause(); 00079 void ticked(qint64 progress); 00080 void totalTimeChanged(qint64 time); 00081 void setPosition(int newProgress); 00082 void setVolume(int value); 00083 void volumeChanged(qreal value); 00084 void showOpenFileDialog(); 00085 void openFile(const QString &path); 00086 void stateChanged(Phonon::State newState, Phonon::State oldState); 00087 void animateControlWidget(bool show); 00088 void hideControlWidget(); 00089 void slidingCompleted(); 00090 bool spaceForControlsAvailable(); 00091 00092 00093 VideoWidget *q; 00094 00095 Phonon::VideoWidget *videoWidget; 00096 Phonon::AudioOutput *audioOutput; 00097 Phonon::MediaObject *media; 00098 00099 bool ticking; 00100 bool forceControlsVisible; 00101 00102 //control widgets 00103 Plasma::Animation *animation; 00104 QTimer *hideTimer; 00105 VideoWidget::Controls shownControls; 00106 Plasma::Frame *controlsWidget; 00107 IconWidget *previousButton; 00108 IconWidget *playButton; 00109 IconWidget *pauseButton; 00110 IconWidget *stopButton; 00111 IconWidget *playPauseButton; 00112 IconWidget *nextButton; 00113 Slider *progress; 00114 Slider *volume; 00115 IconWidget *openFileButton; 00116 }; 00117 00118 void VideoWidgetPrivate::playPause() 00119 { 00120 if (media->state() == Phonon::PlayingState) { 00121 media->pause(); 00122 } else { 00123 media->play(); 00124 } 00125 } 00126 00127 void VideoWidgetPrivate::ticked(qint64 newProgress) 00128 { 00129 ticking = true; 00130 progress->setValue(newProgress); 00131 ticking = false; 00132 } 00133 00134 void VideoWidgetPrivate::totalTimeChanged(qint64 time) 00135 { 00136 ticking = true; 00137 //FIXME: this will break for veeery long stuff, butPhonon::SeekSlider seems to have the same problem 00138 progress->setRange(0, time); 00139 ticking = false; 00140 } 00141 00142 void VideoWidgetPrivate::setPosition(int progress) 00143 { 00144 if (!ticking) { 00145 media->seek(progress); 00146 } 00147 } 00148 00149 void VideoWidgetPrivate::setVolume(int value) 00150 { 00151 audioOutput->setVolume(qreal(value)/100.0); 00152 } 00153 00154 void VideoWidgetPrivate::volumeChanged(qreal value) 00155 { 00156 volume->setValue(value*100); 00157 } 00158 00159 void VideoWidgetPrivate::showOpenFileDialog() 00160 { 00161 #ifndef PLASMA_NO_KIO 00162 openFile(KFileDialog::getOpenFileName()); 00163 #else 00164 openFile(QFileDialog::getOpenFileName()); 00165 #endif 00166 } 00167 00168 void VideoWidgetPrivate::openFile(const QString &path) 00169 { 00170 media->setCurrentSource(Phonon::MediaSource(path)); 00171 media->play(); 00172 } 00173 00174 void VideoWidgetPrivate::stateChanged(Phonon::State newState, Phonon::State oldState) 00175 { 00176 Q_UNUSED(oldState) 00177 00178 if (playPauseButton) { 00179 if (newState == Phonon::PlayingState) { 00180 playPauseButton->setIcon("media-playback-pause"); 00181 } else { 00182 playPauseButton->setIcon("media-playback-start"); 00183 } 00184 } 00185 } 00186 00187 void VideoWidgetPrivate::animateControlWidget(bool show) 00188 { 00189 if (!controlsWidget || controlsWidget->isVisible() == show) { 00190 return; 00191 } 00192 00193 const int distance = controlsWidget->size().height(); 00194 if (!controlsWidget->isVisible()) { 00195 controlsWidget->setPos(0, -distance); 00196 controlsWidget->show(); 00197 } 00198 00199 //clip only when animating 00200 q->setFlags(q->flags()|QGraphicsItem::ItemClipsChildrenToShape); 00201 00202 if (!animation) { 00203 animation = Plasma::Animator::create(Plasma::Animator::SlideAnimation, q); 00204 animation->setTargetWidget(controlsWidget); 00205 animation->setProperty("movementDirection", Animation::MoveDown); 00206 q->connect(animation, SIGNAL(finished()), q, SLOT(slidingCompleted())); 00207 } 00208 00209 animation->setProperty("distance", distance); 00210 animation->setProperty("direction", show? QAbstractAnimation::Forward : QAbstractAnimation::Backward); 00211 animation->start(); 00212 } 00213 00214 void VideoWidgetPrivate::hideControlWidget() 00215 { 00216 animateControlWidget(false); 00217 } 00218 00219 void VideoWidgetPrivate::slidingCompleted() 00220 { 00221 if (!controlsWidget) { 00222 return; 00223 } 00224 00225 //usually don't clip 00226 q->setFlags(q->flags()^QGraphicsItem::ItemClipsChildrenToShape); 00227 00228 if (controlsWidget->pos().y() < 0) { 00229 controlsWidget->hide(); 00230 } else if (!forceControlsVisible) { 00231 hideTimer->start(3000); 00232 } 00233 } 00234 00235 bool VideoWidgetPrivate::spaceForControlsAvailable() 00236 { 00237 if (controlsWidget) { 00238 QSize hint = controlsWidget->effectiveSizeHint(Qt::MinimumSize).toSize(); 00239 return (q->size().width() >= hint.width()) && 00240 (q->size().height() >= hint.height()); 00241 } else { 00242 return true; 00243 } 00244 } 00245 00246 00247 00248 VideoWidget::VideoWidget(QGraphicsWidget *parent) 00249 : QGraphicsProxyWidget(parent), 00250 d(new VideoWidgetPrivate(this)) 00251 { 00252 d->videoWidget = new Phonon::VideoWidget; 00253 d->audioOutput = new Phonon::AudioOutput(this); 00254 d->media = new Phonon::MediaObject(this); 00255 //it appears that the path has to be created BEFORE setting the proxy 00256 Phonon::createPath(d->media, d->videoWidget); 00257 Phonon::createPath(d->media, d->audioOutput); 00258 00259 00260 setWidget(d->videoWidget); 00261 d->videoWidget->setWindowIcon(QIcon()); 00262 setAcceptHoverEvents(true); 00263 00264 connect(d->media, SIGNAL(tick(qint64)), this, SIGNAL(tick(qint64))); 00265 connect(d->media, SIGNAL(aboutToFinish()), this, SIGNAL(aboutToFinish())); 00266 } 00267 00268 VideoWidget::~VideoWidget() 00269 { 00270 delete d; 00271 } 00272 00273 Phonon::MediaObject *VideoWidget::mediaObject() const 00274 { 00275 return d->media; 00276 } 00277 00278 Phonon::AudioOutput *VideoWidget::audioOutput() const 00279 { 00280 return d->audioOutput; 00281 } 00282 00283 void VideoWidget::setUrl(const QString &url) 00284 { 00285 QString fileUrl; 00286 if (url.startsWith('/')) { 00287 fileUrl = "file://" % url; 00288 } else { 00289 fileUrl = url; 00290 } 00291 00292 if (fileUrl == d->media->currentSource().url().toString()) { 00293 return; 00294 } 00295 00296 d->media->setCurrentSource(Phonon::MediaSource(fileUrl)); 00297 } 00298 00299 QString VideoWidget::url() const 00300 { 00301 return d->media->currentSource().url().toString(); 00302 } 00303 00304 void VideoWidget::setUsedControls(const Controls controls) 00305 { 00306 if (controls == d->shownControls) { 00307 return; 00308 } 00309 00310 d->shownControls = controls; 00311 00312 //kDebug()<<"Setting used controls"<<controls; 00313 00314 QGraphicsLinearLayout *controlsLayout = 0; 00315 if (controls != NoControls && d->controlsWidget == 0) { 00316 d->controlsWidget = new Plasma::Frame(this); 00317 d->controlsWidget->setFrameShadow(Plasma::Frame::Raised); 00318 controlsLayout = new QGraphicsLinearLayout(Qt::Horizontal, d->controlsWidget); 00319 d->hideTimer = new QTimer(this); 00320 connect(d->hideTimer, SIGNAL(timeout()), this, SLOT(hideControlWidget())); 00321 //controls == NoControls 00322 } else if (d->controlsWidget != 0) { 00323 d->controlsWidget->deleteLater(); 00324 d->hideTimer->deleteLater(); 00325 d->controlsWidget = 0; 00326 00327 //disconnect all the stuff that wasn't automatically disconnected 'cause widget deaths 00328 disconnect(d->media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged(Phonon::State, Phonon::State))); 00329 disconnect(d->media, SIGNAL(tick(qint64)), this, SLOT(ticked(qint64))); 00330 disconnect(d->media, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); 00331 disconnect(d->audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(volumeChanged(qreal))); 00332 return; 00333 } 00334 00335 Q_ASSERT(controlsLayout); 00336 00337 //empty the layout 00338 while (controlsLayout->count() > 0) { 00339 controlsLayout->removeAt(0); 00340 } 00341 00342 if (controls&Previous) { 00343 if (!d->previousButton) { 00344 d->previousButton = new IconWidget(d->controlsWidget); 00345 d->previousButton->setIcon("media-playback-start"); 00346 connect(d->playButton, SIGNAL(clicked()), this, SLOT(PreviousRequested())); 00347 } 00348 controlsLayout->addItem(d->previousButton); 00349 } else { 00350 d->previousButton->deleteLater(); 00351 d->previousButton = 0; 00352 } 00353 00354 if (controls&Play) { 00355 if (!d->playButton) { 00356 d->playButton = new IconWidget(d->controlsWidget); 00357 d->playButton->setIcon("media-playback-start"); 00358 connect(d->playButton, SIGNAL(clicked()), this, SLOT(play())); 00359 } 00360 controlsLayout->addItem(d->playButton); 00361 } else { 00362 d->playButton->deleteLater(); 00363 d->playButton = 0; 00364 } 00365 00366 if (controls&Pause) { 00367 if (!d->pauseButton) { 00368 d->pauseButton = new IconWidget(d->controlsWidget); 00369 d->pauseButton->setIcon("media-playback-pause"); 00370 connect(d->pauseButton, SIGNAL(clicked()), this, SLOT(pause())); 00371 } 00372 controlsLayout->addItem(d->pauseButton); 00373 } else { 00374 d->pauseButton->deleteLater(); 00375 d->pauseButton = 0; 00376 } 00377 00378 if (controls&Stop) { 00379 if (!d->stopButton) { 00380 d->stopButton = new IconWidget(d->controlsWidget); 00381 d->stopButton->setIcon("media-playback-stop"); 00382 connect(d->stopButton, SIGNAL(clicked()), this, SLOT(stop())); 00383 } 00384 controlsLayout->addItem(d->stopButton); 00385 } else { 00386 d->stopButton->deleteLater(); 00387 d->stopButton = 0; 00388 } 00389 00390 if (controls&PlayPause) { 00391 if (!d->playPauseButton) { 00392 d->playPauseButton = new IconWidget(d->controlsWidget); 00393 d->playPauseButton->setIcon("media-playback-start"); 00394 connect(d->playPauseButton, SIGNAL(clicked()), this, SLOT(playPause())); 00395 } 00396 controlsLayout->addItem(d->playPauseButton); 00397 } else { 00398 d->playPauseButton->deleteLater(); 00399 d->playPauseButton = 0; 00400 } 00401 00402 if (controls&Next) { 00403 if (!d->nextButton) { 00404 d->nextButton = new IconWidget(d->nextButton); 00405 d->nextButton->setIcon("media-playback-start"); 00406 connect(d->nextButton, SIGNAL(clicked()), this, SIGNAL(nextRequested())); 00407 } 00408 controlsLayout->addItem(d->nextButton); 00409 } else { 00410 d->nextButton->deleteLater(); 00411 d->nextButton = 0; 00412 } 00413 00414 connect(d->media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged(Phonon::State, Phonon::State))); 00415 00416 00417 00418 00419 if (controls&Progress) { 00420 if (!d->progress) { 00421 d->progress = new Slider(d->controlsWidget); 00422 d->progress->setMinimum(0); 00423 d->progress->setMaximum(100); 00424 d->progress->setOrientation(Qt::Horizontal); 00425 controlsLayout->setStretchFactor(d->progress, 4); 00426 d->progress->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); 00427 00428 connect(d->media, SIGNAL(tick(qint64)), this, SLOT(ticked(qint64))); 00429 connect(d->media, SIGNAL(totalTimeChanged(qint64)), SLOT(totalTimeChanged(qint64))); 00430 connect(d->progress, SIGNAL(valueChanged(int)), this, SLOT(setPosition(int))); 00431 } 00432 controlsLayout->addItem(d->progress); 00433 } else { 00434 d->progress->deleteLater(); 00435 d->progress = 0; 00436 } 00437 00438 00439 if (controls&Volume) { 00440 if (!d->volume) { 00441 d->volume = new Slider(d->controlsWidget); 00442 d->volume->setMinimum(0); 00443 d->volume->setMaximum(100); 00444 d->volume->setValue(100); 00445 d->volume->setOrientation(Qt::Horizontal); 00446 d->volume->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); 00447 00448 connect(d->volume, SIGNAL(valueChanged(int)), SLOT(setVolume(int))); 00449 connect(d->audioOutput, SIGNAL(volumeChanged(qreal)), SLOT(volumeChanged(qreal))); 00450 } 00451 controlsLayout->addItem(d->volume); 00452 } else { 00453 d->volume->deleteLater(); 00454 d->volume = 0; 00455 } 00456 00457 00458 if (controls&OpenFile) { 00459 if (!d->openFileButton) { 00460 d->openFileButton = new IconWidget(d->controlsWidget); 00461 d->openFileButton->setIcon(KIcon("document-open")); 00462 connect(d->openFileButton, SIGNAL(clicked()), this, SLOT(showOpenFileDialog())); 00463 } 00464 controlsLayout->addItem(d->openFileButton); 00465 } else { 00466 d->openFileButton->deleteLater(); 00467 d->openFileButton = 0; 00468 } 00469 00470 controlsLayout->activate(); 00471 d->controlsWidget->setPos(0,-d->controlsWidget->size().height()); 00472 d->controlsWidget->resize(size().width(), d->controlsWidget->size().height()); 00473 d->controlsWidget->hide(); 00474 } 00475 00476 VideoWidget::Controls VideoWidget::usedControls() const 00477 { 00478 return d->shownControls; 00479 } 00480 00481 void VideoWidget::play() 00482 { 00483 if (d->media->state() == Phonon::PlayingState) { 00484 return; 00485 } 00486 00487 d->media->play(); 00488 } 00489 00490 void VideoWidget::pause() 00491 { 00492 if (d->media->state() == Phonon::PausedState) { 00493 return; 00494 } 00495 00496 d->media->pause(); 00497 } 00498 00499 void VideoWidget::stop() 00500 { 00501 if (d->media->state() == Phonon::StoppedState) { 00502 return; 00503 } 00504 00505 d->media->stop(); 00506 } 00507 00508 void VideoWidget::seek(qint64 time) 00509 { 00510 if (d->media->currentTime() == time) { 00511 return; 00512 } 00513 00514 d->media->seek(time); 00515 } 00516 00517 qint64 VideoWidget::currentTime() const 00518 { 00519 return d->media->currentTime(); 00520 } 00521 00522 qint64 VideoWidget::totalTime() const 00523 { 00524 return d->media->totalTime(); 00525 } 00526 00527 qint64 VideoWidget::remainingTime() const 00528 { 00529 return d->media->remainingTime(); 00530 } 00531 00532 void VideoWidget::setControlsVisible(bool visible) 00533 { 00534 if (d->controlsWidget) { 00535 d->forceControlsVisible = visible; 00536 d->animateControlWidget(visible); 00537 } 00538 } 00539 00540 bool VideoWidget::controlsVisible() const 00541 { 00542 return d->controlsWidget != 0 && d->controlsWidget->isVisible(); 00543 } 00544 00545 void VideoWidget::setTickInterval(qint64 interval) 00546 { 00547 d->media->setTickInterval(interval); 00548 } 00549 00550 qint64 VideoWidget::tickInterval() const 00551 { 00552 return d->media->tickInterval(); 00553 } 00554 00555 void VideoWidget::setStyleSheet(const QString &stylesheet) 00556 { 00557 d->videoWidget->setStyleSheet(stylesheet); 00558 } 00559 00560 QString VideoWidget::styleSheet() 00561 { 00562 return d->videoWidget->styleSheet(); 00563 } 00564 00565 Phonon::VideoWidget *VideoWidget::nativeWidget() const 00566 { 00567 return d->videoWidget; 00568 } 00569 00570 00571 void VideoWidget::resizeEvent(QGraphicsSceneResizeEvent *event) 00572 { 00573 QGraphicsProxyWidget::resizeEvent(event); 00574 00575 if (d->controlsWidget) { 00576 QSize newControlsSize(event->newSize().width(), d->controlsWidget->size().height()); 00577 int newHeight = event->newSize().height(); 00578 qreal leftMargin, topMargin, rightMargin, bottomMargin; 00579 d->controlsWidget->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); 00580 00581 if (newHeight/5 >= KIconLoader::SizeEnormous) { 00582 newControlsSize.setHeight(KIconLoader::SizeEnormous+topMargin+bottomMargin); 00583 } else if (newHeight/5 >= KIconLoader::SizeHuge) { 00584 newControlsSize.setHeight(KIconLoader::SizeHuge+topMargin+bottomMargin); 00585 } else if (newHeight/5 >= KIconLoader::SizeLarge) { 00586 newControlsSize.setHeight(KIconLoader::SizeLarge+topMargin+bottomMargin); 00587 } else if (newHeight/5 >= KIconLoader::SizeMedium) { 00588 newControlsSize.setHeight(KIconLoader::SizeMedium+topMargin+bottomMargin); 00589 } else { 00590 newControlsSize.setHeight(KIconLoader::SizeSmallMedium+topMargin+bottomMargin); 00591 } 00592 d->controlsWidget->resize(newControlsSize); 00593 00594 if (d->spaceForControlsAvailable()) { 00595 d->animateControlWidget(false); 00596 } 00597 } 00598 } 00599 00600 void VideoWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event) 00601 { 00602 Q_UNUSED(event) 00603 00604 if (d->controlsWidget && 00605 !d->forceControlsVisible && 00606 d->spaceForControlsAvailable()) { 00607 d->animateControlWidget(true); 00608 } 00609 } 00610 00611 void VideoWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) 00612 { 00613 Q_UNUSED(event) 00614 00615 if (d->controlsWidget && !d->forceControlsVisible) { 00616 d->hideTimer->start(1000); 00617 } 00618 } 00619 00620 void VideoWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event) 00621 { 00622 Q_UNUSED(event) 00623 00624 if (d->forceControlsVisible || !d->controlsWidget) { 00625 return; 00626 } 00627 00628 d->hideTimer->start(3000); 00629 00630 if (!d->controlsWidget->isVisible() && 00631 d->spaceForControlsAvailable()) { 00632 d->animateControlWidget(true); 00633 } 00634 } 00635 00636 } // namespace Plasma 00637 00638 #include <videowidget.moc> 00639
KDE 4.7 API Reference