File indexing completed on 2024-04-28 04:48:39

0001 /*
0002     SPDX-FileCopyrightText: 2005 Max Howell <max.howell@methylblue.com>
0003     SPDX-FileCopyrightText: 2007 Christoph Pfister <christophpfister@gmail.com>
0004     SPDX-FileCopyrightText: 2007 Ian Monroe <ian@monroe.nu>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include <config.h>
0010 
0011 #include "timeLabel.h"
0012 #include "videoWindow.h"
0013 
0014 #include "actions.h" //::seek() FIXME unfortunate
0015 #include "theStream.h"
0016 
0017 #include <QActionGroup>
0018 #include <QApplication>
0019 #include <QContextMenuEvent>
0020 #include <QDebug>
0021 #include <QIcon>
0022 #include <QLabel>
0023 #include <QMenu>
0024 #include <QMimeDatabase>
0025 #include <QPainter>
0026 #include <QTimer>
0027 #include <QVBoxLayout>
0028 
0029 #include <KLocalizedString>
0030 #include <KSharedConfig>
0031 
0032 #include <phonon/AudioOutput>
0033 #include <phonon/MediaController>
0034 #include <phonon/MediaObject>
0035 #include <phonon/SeekSlider>
0036 #include <phonon/VideoWidget>
0037 #include <phonon/VolumeFaderEffect>
0038 #include <phonon/VolumeSlider>
0039 #include <phonon/audiodataoutput.h>
0040 
0041 #include <Solid/Block>
0042 #include <Solid/OpticalDisc>
0043 
0044 #ifdef Q_OS_WIN
0045 #include <windows.h>
0046 #endif
0047 
0048 #if defined(HAVE_UNISTD_H)
0049 #include <unistd.h>
0050 #endif
0051 
0052 using Phonon::AudioOutput;
0053 using Phonon::MediaController;
0054 using Phonon::MediaObject;
0055 using Phonon::SeekSlider;
0056 using Phonon::VideoWidget;
0057 using Phonon::VolumeSlider;
0058 
0059 namespace Dragon
0060 {
0061 
0062 VideoWindow *VideoWindow::s_instance = nullptr;
0063 
0064 VideoWindow::VideoWindow(QWidget *parent)
0065     : QWidget(parent)
0066     , m_cursorTimer(new QTimer(this))
0067     , m_justLoaded(false)
0068     , m_subLanguages(new QActionGroup(this))
0069     , m_audioLanguages(new QActionGroup(this))
0070     , m_logo(new QLabel(this))
0071     , m_initialOffset(0)
0072     , m_aDataOutput(nullptr)
0073 {
0074     m_isPreview = false;
0075 
0076     s_instance = this;
0077     setObjectName(QLatin1String("VideoWindow"));
0078 
0079     QVBoxLayout *box = new QVBoxLayout(this);
0080     box->setContentsMargins(0, 0, 0, 0);
0081     box->setSpacing(0);
0082     m_vWidget = new VideoWidget(this);
0083     m_vWidget->hide();
0084     box->addWidget(m_vWidget);
0085     m_aOutput = new AudioOutput(Phonon::VideoCategory, this);
0086     m_media = new MediaObject(this);
0087     m_controller = new MediaController(m_media);
0088     m_controller->setAutoplayTitles(true);
0089     Phonon::createPath(m_media, m_vWidget);
0090     m_audioPath = Phonon::createPath(m_media, m_aOutput);
0091     m_media->setTickInterval(1000);
0092     connect(m_media, &Phonon::MediaObject::tick, this, &VideoWindow::tick);
0093     connect(m_media, &Phonon::MediaObject::currentSourceChanged, this, &VideoWindow::currentSourceChanged);
0094     connect(m_media, &Phonon::MediaObject::totalTimeChanged, this, &VideoWindow::totalTimeChanged);
0095     connect(m_media, &Phonon::MediaObject::seekableChanged, this, &VideoWindow::seekableChanged);
0096     connect(m_media, &Phonon::MediaObject::metaDataChanged, this, &VideoWindow::metaDataChanged);
0097     connect(m_aOutput, &Phonon::AudioOutput::mutedChanged, this, &VideoWindow::mutedChanged);
0098     connect(m_aOutput, &Phonon::AudioOutput::volumeChanged, this, &VideoWindow::volumeChanged);
0099     connect(m_media, &Phonon::MediaObject::hasVideoChanged, this, &VideoWindow::hasVideoChanged);
0100     connect(m_media, &Phonon::MediaObject::hasVideoChanged, m_vWidget, &QWidget::setVisible); // hide video widget if no video to show
0101     connect(m_media, &Phonon::MediaObject::hasVideoChanged, m_logo, &QWidget::setHidden);
0102     connect(m_media, &Phonon::MediaObject::finished, this, &VideoWindow::finished);
0103     connect(m_controller, &Phonon::MediaController::availableSubtitlesChanged, this, &VideoWindow::updateChannels);
0104 
0105     {
0106         m_subLanguages->setExclusive(true);
0107         QAction *turnOff = new QAction(i18nc("@option:radio", "&DVD Subtitle Selection"), m_subLanguages);
0108         turnOff->setCheckable(true);
0109         turnOff->setProperty(TheStream::CHANNEL_PROPERTY, -1);
0110         connect(turnOff, &QAction::triggered, this, &VideoWindow::slotSetSubtitle);
0111 
0112         QAction *separator = new QAction(m_subLanguages);
0113         separator->setSeparator(true);
0114     }
0115     {
0116         m_audioLanguages->setExclusive(true);
0117         QAction *autoLang = new QAction(i18nc("@option:radio audio language", "&Auto"), m_audioLanguages);
0118         autoLang->setProperty(TheStream::CHANNEL_PROPERTY, -1);
0119         autoLang->setCheckable(true);
0120         connect(autoLang, &QAction::triggered, this, &VideoWindow::slotSetAudio);
0121 
0122         QAction *separator = new QAction(m_audioLanguages);
0123         separator->setSeparator(true);
0124     }
0125 
0126     connect(m_media, &Phonon::MediaObject::stateChanged, this, &VideoWindow::stateChanged);
0127     connect(m_cursorTimer, &QTimer::timeout, this, &VideoWindow::hideCursor);
0128     m_cursorTimer->setSingleShot(true);
0129     {
0130         m_logo->setAutoFillBackground(true);
0131         QPalette pal;
0132         pal.setColor(QPalette::Window, Qt::white);
0133         m_logo->setPalette(pal);
0134         QLayout *layout = new QVBoxLayout(m_logo);
0135         layout->setAlignment(Qt::AlignCenter);
0136         m_logo->setLayout(layout);
0137         box->addWidget(m_logo);
0138         m_logo->show();
0139     }
0140     {
0141         KConfigGroup config = KSharedConfig::openConfig()->group(QStringLiteral("General"));
0142         m_aOutput->setVolume(config.readEntry<double>("Volume", 1.0));
0143     }
0144 }
0145 
0146 VideoWindow::~VideoWindow()
0147 {
0148     eject();
0149     KConfigGroup config = KSharedConfig::openConfig()->group(QStringLiteral("General"));
0150     config.writeEntry("Volume", static_cast<double>(m_aOutput->volume()));
0151 }
0152 
0153 bool VideoWindow::init()
0154 {
0155     return m_media->state() != Phonon::ErrorState;
0156 }
0157 
0158 bool VideoWindow::load(const QUrl &url)
0159 {
0160     QApplication::setOverrideCursor(Qt::WaitCursor);
0161 
0162     eject();
0163 
0164     QMimeDatabase db;
0165     QMimeType mimeType = db.mimeTypeForUrl(url);
0166     qDebug() << "detected mimetype: " << mimeType.name();
0167     if (mimeType.inherits(QLatin1String("application/x-cd-image")) || mimeType.inherits(QLatin1String("inode/directory")))
0168         m_media->setCurrentSource(Phonon::MediaSource(Phonon::Dvd, url.path()));
0169     else
0170         m_media->setCurrentSource(url);
0171     m_justLoaded = true;
0172 
0173     QApplication::restoreOverrideCursor();
0174 
0175     return true;
0176 }
0177 
0178 bool VideoWindow::load(const QList<QUrl> &urls)
0179 {
0180     QApplication::setOverrideCursor(Qt::WaitCursor);
0181 
0182     eject();
0183     QList<QUrl> tmpUrls = urls;
0184     m_media->setCurrentSource(tmpUrls.takeFirst());
0185     m_media->enqueue(tmpUrls);
0186     m_justLoaded = true;
0187 
0188     QApplication::restoreOverrideCursor();
0189 
0190     return true;
0191 }
0192 
0193 bool VideoWindow::play(qint64 offset)
0194 {
0195     QApplication::setOverrideCursor(Qt::WaitCursor);
0196 
0197     m_justLoaded = false;
0198     m_initialOffset = offset;
0199     m_media->play();
0200 
0201     QApplication::restoreOverrideCursor();
0202 
0203     return true;
0204 }
0205 
0206 bool VideoWindow::resume()
0207 {
0208     m_media->play();
0209     return true;
0210 }
0211 
0212 bool VideoWindow::playDvd()
0213 {
0214     eject();
0215     m_media->setCurrentSource(Phonon::MediaSource(Phonon::Dvd));
0216     m_media->play();
0217     return true;
0218 }
0219 
0220 bool VideoWindow::playDisc(const Solid::Device &device)
0221 {
0222     QString devicePath;
0223     {
0224         const Solid::Block *block = device.as<const Solid::Block>();
0225         if (block)
0226             devicePath = block->device();
0227         else {
0228             qDebug() << "device was not a block";
0229             return false;
0230         }
0231     }
0232     const Solid::OpticalDisc *disc = device.as<const Solid::OpticalDisc>();
0233     if (disc) {
0234         Phonon::DiscType phononType = Phonon::NoDisc;
0235         {
0236             Solid::OpticalDisc::ContentTypes solidType = disc->availableContent();
0237             if (solidType & Solid::OpticalDisc::VideoDvd)
0238                 phononType = Phonon::Dvd;
0239             if (solidType & (Solid::OpticalDisc::VideoCd | Solid::OpticalDisc::SuperVideoCd))
0240                 phononType = Phonon::Vcd;
0241             if (solidType & Solid::OpticalDisc::Audio)
0242                 phononType = Phonon::Cd;
0243 
0244             // No change -> cannot play the disc.... should not really happen as
0245             // mainWindow already preprocesses the type, so this would indicate
0246             // bogus handling in one of the classes -> assertation.
0247             Q_ASSERT(phononType != Phonon::NoDisc);
0248             if (phononType == Phonon::NoDisc) {
0249                 qDebug() << "not a playable disc type: " << disc->availableContent() << " type";
0250                 return false;
0251             }
0252         }
0253         eject();
0254         m_media->setCurrentSource(Phonon::MediaSource(phononType, devicePath));
0255         qDebug() << "actually playing the disc at " << devicePath;
0256         m_media->play();
0257         return true;
0258     } else {
0259         qDebug() << "device was not a disc";
0260         return false;
0261     }
0262 }
0263 
0264 bool VideoWindow::isPreview(const bool &v)
0265 {
0266     if (v) {
0267         m_isPreview = v;
0268     }
0269     return m_isPreview;
0270 }
0271 
0272 void VideoWindow::relativeSeek(qint64 step)
0273 {
0274     qDebug() << "** relative seek";
0275     const qint64 new_pos = currentTime() + step;
0276     if ((new_pos >= 0) && (new_pos < length())) {
0277         seek(new_pos);
0278         play();
0279     } else if (new_pos < 0) {
0280         seek(0);
0281         play();
0282     }
0283 }
0284 
0285 void VideoWindow::stop()
0286 {
0287     qDebug() << "Stop called";
0288     eject();
0289     m_media->stop();
0290     m_media->setCurrentSource(Phonon::MediaSource()); // set the current source to    Phonon::MediaSource::Empty
0291     qDebug() << "Media source valid? " << TheStream::hasMedia();
0292     m_vWidget->hide();
0293     m_logo->show();
0294 }
0295 
0296 void VideoWindow::pause()
0297 {
0298     m_media->pause();
0299 }
0300 
0301 void VideoWindow::playPause()
0302 {
0303     if (m_media->state() == Phonon::PlayingState)
0304         pause();
0305     else
0306         resume();
0307 }
0308 
0309 QString VideoWindow::urlOrDisc() const
0310 {
0311     Phonon::MediaSource source = m_media->currentSource();
0312     switch (source.type()) {
0313     case Phonon::MediaSource::Invalid:
0314     case Phonon::MediaSource::Empty:
0315         return QLatin1String("Invalid"); // no i18n, used for DBus responses
0316         break;
0317     case Phonon::MediaSource::Url:
0318     case Phonon::MediaSource::LocalFile:
0319         return source.url().toString();
0320         break;
0321     case Phonon::MediaSource::Disc:
0322         return source.deviceName();
0323         break;
0324     case Phonon::MediaSource::Stream:
0325         return QLatin1String("Data Stream");
0326         break;
0327     default:
0328         break;
0329     }
0330     return QLatin1String("Error");
0331 }
0332 
0333 Phonon::MediaSource::Type VideoWindow::mediaSourceType() const
0334 {
0335     return m_media->currentSource().type();
0336 }
0337 
0338 QMultiMap<QString, QString> VideoWindow::metaData() const
0339 {
0340     return m_media->metaData();
0341 }
0342 
0343 bool VideoWindow::isSeekable() const
0344 {
0345     return m_media->isSeekable();
0346 }
0347 
0348 Phonon::State VideoWindow::state() const
0349 {
0350     return m_media->state();
0351 }
0352 
0353 bool VideoWindow::isActiveState() const
0354 {
0355     return isActiveState(state());
0356 }
0357 
0358 bool VideoWindow::isActiveState(Phonon::State s) const
0359 {
0360     return (s == Phonon::PlayingState || s == Phonon::PausedState || s == Phonon::BufferingState);
0361 }
0362 
0363 qreal VideoWindow::volume() const
0364 {
0365     return m_aOutput->volume();
0366 }
0367 
0368 void VideoWindow::setVolume(qreal vol)
0369 {
0370     m_aOutput->setVolume(vol);
0371 }
0372 
0373 void VideoWindow::mute(bool muted)
0374 {
0375     m_aOutput->setMuted(muted);
0376 }
0377 
0378 bool VideoWindow::isMuted()
0379 {
0380     return m_aOutput->isMuted();
0381 }
0382 
0383 void VideoWindow::seek(qint64 pos)
0384 {
0385     m_media->seek(pos);
0386 }
0387 
0388 qint32 VideoWindow::tickInterval() const
0389 {
0390     return m_media->tickInterval();
0391 }
0392 
0393 void VideoWindow::showOSD(const QString & /*message*/)
0394 {
0395     return;
0396 }
0397 
0398 void VideoWindow::resetZoom()
0399 {
0400     TheStream::profile().deleteEntry("Preferred Size");
0401     window()->adjustSize();
0402 }
0403 
0404 qint64 VideoWindow::currentTime() const
0405 {
0406     return m_media->currentTime();
0407 }
0408 
0409 qint64 VideoWindow::length() const
0410 {
0411     return m_media->totalTime();
0412 }
0413 
0414 bool VideoWindow::setupAnalyzer(QObject *analyzer)
0415 {
0416     if (!m_aDataOutput) {
0417         m_aDataOutput = new Phonon::AudioDataOutput(this);
0418         m_audioDataPath = Phonon::createPath(m_media, m_aDataOutput);
0419         connect(m_aDataOutput,
0420                 SIGNAL(dataReady(QMap<Phonon::AudioDataOutput::Channel, QVector<qint16>>)),
0421                 analyzer,
0422                 SLOT(drawFrame(QMap<Phonon::AudioDataOutput::Channel, QVector<qint16>>)));
0423     }
0424 
0425     return m_audioDataPath.isValid();
0426 }
0427 
0428 bool VideoWindow::isDVD() const
0429 {
0430     return m_media->currentSource().discType() == Phonon::Dvd || m_media->currentSource().discType() == Phonon::BluRay;
0431 }
0432 
0433 QWidget *VideoWindow::newPositionSlider()
0434 {
0435     SeekSlider *seekSlider = new SeekSlider();
0436     seekSlider->setIconVisible(false);
0437     seekSlider->setMediaObject(m_media);
0438     seekSlider->setSingleStep(5000);
0439     return seekSlider;
0440 }
0441 
0442 QWidget *VideoWindow::newVolumeSlider()
0443 {
0444     VolumeSlider *volumeSlider = new VolumeSlider();
0445     volumeSlider->setObjectName(QLatin1String("volume"));
0446     volumeSlider->setAudioOutput(m_aOutput);
0447     volumeSlider->setMuteVisible(false);
0448     volumeSlider->setOrientation(Qt::Vertical);
0449     return volumeSlider;
0450 }
0451 
0452 void VideoWindow::stateChanged(Phonon::State currentState, Phonon::State oldstate) // slot
0453 {
0454     qDebug() << "chapters: " << m_controller->availableChapters() << " titles: " << m_controller->availableTitles();
0455     QStringList states;
0456     states << QLatin1String("Loading") << QLatin1String("Stopped") << QLatin1String("Playing") << QLatin1String("Buffering") << QLatin1String("Paused")
0457            << QLatin1String("Error");
0458     qDebug() << "going from " << states.at(oldstate) << " to " << states.at(currentState);
0459 
0460     if (currentState == Phonon::PlayingState && m_initialOffset > 0) {
0461         seek(m_initialOffset);
0462         m_initialOffset = 0;
0463     }
0464 
0465     if (currentState == Phonon::PlayingState && m_media->hasVideo()) {
0466         m_logo->hide();
0467         m_vWidget->show();
0468         updateChannels();
0469     }
0470     Q_EMIT stateUpdated(currentState, oldstate);
0471 }
0472 
0473 void VideoWindow::settingChanged(int setting)
0474 {
0475     const QString name = sender()->objectName();
0476     const double dSetting = static_cast<double>(setting) * 0.01;
0477     qDebug() << "setting " << name << " to " << dSetting;
0478     if (name == QLatin1String("brightnessSlider")) {
0479         m_vWidget->setBrightness(dSetting);
0480     } else if (name == QLatin1String("contrastSlider")) {
0481         m_vWidget->setContrast(dSetting);
0482     } else if (name == QLatin1String("hueSlider")) {
0483         m_vWidget->setHue(dSetting);
0484     } else if (name == QLatin1String("saturationSlider")) {
0485         m_vWidget->setSaturation(dSetting);
0486     }
0487 }
0488 
0489 void VideoWindow::loadSettings()
0490 {
0491     if (TheStream::hasProfile()) {
0492         KConfigGroup profile = TheStream::profile();
0493         m_vWidget->setBrightness(profile.readEntry<double>("Brightness", 0.0));
0494         m_vWidget->setContrast(profile.readEntry<double>("Contrast", 0.0));
0495         m_vWidget->setHue(profile.readEntry<double>("Hue", 0.0));
0496         m_vWidget->setSaturation(profile.readEntry<double>("Saturation", 0.0));
0497         setAudioChannel(profile.readEntry<int>("AudioChannel", -1));
0498         setSubtitle(profile.readEntry<int>("Subtitle", -1));
0499     } else {
0500         m_vWidget->setBrightness(0.0);
0501         m_vWidget->setContrast(0.0);
0502         m_vWidget->setHue(0.0);
0503         m_vWidget->setSaturation(0.0);
0504     }
0505 }
0506 
0507 template<class ChannelDescription, class Func>
0508 void VideoWindow::updateActionGroup(QActionGroup *channelActions, const QList<ChannelDescription> &availableChannels, Func actionSlot)
0509 {
0510     {
0511         QList<QAction *> subActions = channelActions->actions();
0512         while (2 < subActions.size())
0513             delete subActions.takeLast();
0514     }
0515     for (const ChannelDescription &channel : availableChannels) {
0516         QAction *lang = new QAction(channelActions);
0517         qDebug() << "the text is: \"" << channel.name() << "\" and index " << channel.index();
0518         lang->setCheckable(true);
0519         lang->setText(channel.name());
0520         lang->setProperty(TheStream::CHANNEL_PROPERTY, channel.index());
0521         connect(lang, &QAction::triggered, this, actionSlot);
0522     }
0523 }
0524 
0525 void VideoWindow::updateChannels()
0526 {
0527     qDebug() << "Updating channels, subtitle count:" << m_controller->availableSubtitles().count();
0528 
0529     updateActionGroup(m_subLanguages, m_controller->availableSubtitles(), &VideoWindow::slotSetSubtitle);
0530     Q_EMIT subChannelsChanged(m_subLanguages->actions());
0531     updateActionGroup(m_audioLanguages, m_controller->availableAudioChannels(), &VideoWindow::slotSetAudio);
0532     Q_EMIT audioChannelsChanged(m_audioLanguages->actions());
0533 }
0534 
0535 void VideoWindow::hideCursor()
0536 {
0537     if (m_media->hasVideo() && m_vWidget->underMouse())
0538         qApp->setOverrideCursor(Qt::BlankCursor);
0539 }
0540 
0541 void VideoWindow::setSubtitle(int channel)
0542 {
0543     Phonon::SubtitleDescription desc = Phonon::SubtitleDescription::fromIndex(channel);
0544     qDebug() << "using index: " << channel << " returned desc has index: " << desc.index();
0545     if (desc.isValid())
0546         m_controller->setCurrentSubtitle(desc);
0547 }
0548 
0549 void VideoWindow::slotSetSubtitle()
0550 {
0551     if (sender() && sender()->property(TheStream::CHANNEL_PROPERTY).canConvert<int>())
0552         setSubtitle(sender()->property(TheStream::CHANNEL_PROPERTY).toInt());
0553 }
0554 
0555 void VideoWindow::setAudioChannel(int channel)
0556 {
0557     Phonon::AudioChannelDescription desc = Phonon::AudioChannelDescription::fromIndex(channel);
0558     qDebug() << "using index: " << channel << " returned desc has index: " << desc.index();
0559     if (desc.isValid())
0560         m_controller->setCurrentAudioChannel(desc);
0561 }
0562 
0563 void VideoWindow::slotSetAudio()
0564 {
0565     if (sender() && sender()->property(TheStream::CHANNEL_PROPERTY).canConvert<int>())
0566         setAudioChannel(sender()->property(TheStream::CHANNEL_PROPERTY).toInt());
0567 }
0568 
0569 void VideoWindow::toggleDVDMenu()
0570 {
0571 #if PHONON_VERSION >= PHONON_VERSION_CHECK(4, 5, 0)
0572     m_controller->setCurrentMenu(MediaController::RootMenu);
0573 #endif
0574 }
0575 
0576 int VideoWindow::videoSetting(const QString &setting)
0577 {
0578     double dValue = 0.0;
0579     if (setting == QLatin1String("brightnessSlider")) {
0580         dValue = m_vWidget->brightness();
0581     } else if (setting == QLatin1String("contrastSlider")) {
0582         dValue = m_vWidget->contrast();
0583     } else if (setting == QLatin1String("hueSlider")) {
0584         dValue = m_vWidget->hue();
0585     } else if (setting == QLatin1String("saturationSlider")) {
0586         dValue = m_vWidget->saturation();
0587     }
0588     return static_cast<int>(dValue * 100.0);
0589 }
0590 
0591 void VideoWindow::prevChapter()
0592 {
0593     if (TheStream::hasVideo())
0594         m_controller->setCurrentChapter(m_controller->currentChapter() - 1);
0595     else
0596         m_controller->previousTitle();
0597 }
0598 
0599 void VideoWindow::nextChapter()
0600 {
0601     if (TheStream::hasVideo())
0602         m_controller->setCurrentChapter(m_controller->currentChapter() + 1);
0603     else
0604         m_controller->nextTitle();
0605 }
0606 
0607 void VideoWindow::tenPercentBack()
0608 {
0609     const qint64 newTime = m_media->currentTime() - (m_media->totalTime() / 10);
0610     if (newTime > 0)
0611         m_media->seek(newTime);
0612     else
0613         m_media->seek(0);
0614 }
0615 
0616 void VideoWindow::tenPercentForward()
0617 {
0618     const qint64 newTime = m_media->currentTime() + (m_media->totalTime() / 10);
0619     if (newTime < m_media->totalTime())
0620         m_media->seek(newTime);
0621 }
0622 
0623 void VideoWindow::tenSecondsBack()
0624 {
0625     relativeSeek(-10000);
0626 }
0627 
0628 void VideoWindow::tenSecondsForward()
0629 {
0630     relativeSeek(10000);
0631 }
0632 
0633 void VideoWindow::increaseVolume()
0634 {
0635     m_aOutput->setVolume(qMin(qreal(1.0), volume() + qreal(0.10)));
0636 }
0637 
0638 void VideoWindow::decreaseVolume()
0639 {
0640     m_aOutput->setVolume(qMax(qreal(0.0), volume() - qreal(0.10)));
0641 }
0642 
0643 bool VideoWindow::canGoPrev() const
0644 {
0645     return m_controller->currentTitle() > 1;
0646 }
0647 
0648 bool VideoWindow::canGoNext() const
0649 {
0650     return m_controller->currentTitle() < m_controller->availableTitles();
0651 }
0652 
0653 ///////////
0654 /// Protected
0655 ///////////
0656 
0657 bool VideoWindow::event(QEvent *event)
0658 {
0659     switch (event->type()) {
0660     case QEvent::Leave:
0661         m_cursorTimer->stop();
0662         qApp->restoreOverrideCursor();
0663         qDebug() << "stop cursorTimer";
0664         break;
0665     case QEvent::FocusOut:
0666         // if the user summons some dialog via a shortcut or whatever we need to ensure
0667         // the mouse gets shown, because if it is modal, we won't get mouse events after
0668         // it is shown! This works because we are always the focus widget.
0669         // @see MainWindow::MainWindow where we setFocusProxy()
0670     case QEvent::Enter:
0671     case QEvent::MouseMove:
0672     case QEvent::MouseButtonPress:
0673         qApp->restoreOverrideCursor();
0674         m_cursorTimer->start(CURSOR_HIDE_TIMEOUT);
0675         break;
0676     default:
0677         return QWidget::event(event);
0678     }
0679     return false;
0680 }
0681 
0682 void VideoWindow::contextMenuEvent(QContextMenuEvent *event)
0683 {
0684     QMenu menu;
0685     if (mainWindow()) {
0686         qobject_cast<KHamburgerMenu *>(action("hamburger_menu"))->addToMenu(&menu);
0687         menu.addAction(action("play"));
0688         menu.addAction(action("fullscreen"));
0689         menu.addAction(action("reset_zoom"));
0690         if (isDVD()) {
0691             menu.addAction(action("toggle_dvd_menu"));
0692         }
0693     }
0694     menu.exec(event->globalPos());
0695 }
0696 
0697 void VideoWindow::mouseDoubleClickEvent(QMouseEvent *)
0698 {
0699     if (mainWindow()) // TODO: add full screen mode to kpart
0700         action("fullscreen")->toggle();
0701 }
0702 
0703 QSize VideoWindow::sizeHint() const // virtual
0704 {
0705     QSize s = TheStream::profile().readEntry<QSize>("Preferred Size", QSize());
0706 
0707     if (!s.isValid())
0708         s = TheStream::defaultVideoSize();
0709 
0710     if (s.isValid() && !s.isNull())
0711         return s;
0712 
0713     return QWidget::sizeHint();
0714 }
0715 
0716 ///////////
0717 /// Private
0718 ///////////
0719 void VideoWindow::eject()
0720 {
0721     if (m_media->currentSource().type() == Phonon::MediaSource::Invalid)
0722         return;
0723 
0724     if (m_media->currentSource().type() == Phonon::MediaSource::Empty)
0725         return;
0726 
0727     KConfigGroup profile = TheStream::profile(); // the config profile for this video file
0728 
0729     Phonon::State state = m_media->state();
0730     if (((state == Phonon::PlayingState || state == Phonon::PausedState))
0731         && (m_media->remainingTime() > 5000)) // if we are really close to the end, don't remember the position
0732         profile.writeEntry("Position", currentTime());
0733     else
0734         profile.deleteEntry("Position");
0735 
0736     const QSize s = videoWindow()->size();
0737     const QSize defaultSize = TheStream::defaultVideoSize();
0738     if (defaultSize.isValid() && (s.width() == defaultSize.width() || s.height() == defaultSize.height()))
0739         profile.deleteEntry("Preferred Size");
0740     else
0741         profile.writeEntry("Preferred Size", s);
0742 
0743     profile.writeEntry("Contrast", m_vWidget->contrast());
0744     profile.writeEntry("Brightness", m_vWidget->brightness());
0745     profile.writeEntry("Hue", m_vWidget->hue());
0746     profile.writeEntry("Saturation", m_vWidget->saturation());
0747     profile.writeEntry("IsVideo", m_media->hasVideo());
0748     {
0749         // this if clause - is to prevent a crash from bug 162721 (a Phonon bug), remove when fixed
0750         if (m_media->hasVideo()) {
0751             qDebug() << "trying to fetch subtitle information";
0752             const int subtitle = TheStream::subtitleChannel();
0753             const int audio = TheStream::audioChannel();
0754             qDebug() << "fetched subtitle information";
0755 
0756             if (subtitle != -1)
0757                 profile.writeEntry("Subtitle", subtitle);
0758             else
0759                 profile.deleteEntry("Subtitle");
0760 
0761             if (audio != -1)
0762                 profile.writeEntry("AudioChannel", audio);
0763             else
0764                 profile.deleteEntry("AudioChannel");
0765         }
0766     }
0767     profile.writeEntry("Date", QDate::currentDate());
0768     profile.sync();
0769 }
0770 
0771 } // namespace Dragon
0772 
0773 #include "moc_videoWindow.cpp"