diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index 363ad818d..d54ed66b6 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -1,405 +1,484 @@ /*************************************************************************** * Copyright (C) 2009 by Peter Penz * * * * 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 * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "informationpanelcontent.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include +#include +#include #include "dolphin_informationpanelsettings.h" #include "phononwidget.h" #include "pixmapviewer.h" +const int PLAY_ARROW_SIZE = 24; +const int PLAY_ARROW_BORDER_SIZE = 2; + InformationPanelContent::InformationPanelContent(QWidget* parent) : QWidget(parent), m_item(), m_previewJob(nullptr), m_outdatedPreviewTimer(nullptr), m_preview(nullptr), m_phononWidget(nullptr), m_nameLabel(nullptr), m_metaDataWidget(nullptr), m_metaDataArea(nullptr), - m_placesItemModel(nullptr) + m_placesItemModel(nullptr), + m_isVideo(false) { parent->installEventFilter(this); // Initialize timer for disabling an outdated preview with a small // delay. This prevents flickering if the new preview can be generated // within a very small timeframe. m_outdatedPreviewTimer = new QTimer(this); m_outdatedPreviewTimer->setInterval(300); m_outdatedPreviewTimer->setSingleShot(true); connect(m_outdatedPreviewTimer, &QTimer::timeout, this, &InformationPanelContent::markOutdatedPreview); QVBoxLayout* layout = new QVBoxLayout(this); // preview const int minPreviewWidth = KIconLoader::SizeEnormous + KIconLoader::SizeMedium; m_preview = new PixmapViewer(parent); m_preview->setMinimumWidth(minPreviewWidth); m_preview->setMinimumHeight(KIconLoader::SizeEnormous); m_phononWidget = new PhononWidget(parent); m_phononWidget->hide(); m_phononWidget->setMinimumWidth(minPreviewWidth); m_phononWidget->setAutoPlay(InformationPanelSettings::previewsAutoPlay()); connect(m_phononWidget, &PhononWidget::hasVideoChanged, this, &InformationPanelContent::slotHasVideoChanged); // name m_nameLabel = new QLabel(parent); QFont font = m_nameLabel->font(); font.setBold(true); m_nameLabel->setFont(font); m_nameLabel->setTextFormat(Qt::PlainText); m_nameLabel->setAlignment(Qt::AlignHCenter); m_nameLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); const bool previewsShown = InformationPanelSettings::previewsShown(); m_preview->setVisible(previewsShown); m_metaDataWidget = new Baloo::FileMetaDataWidget(parent); m_metaDataWidget->setDateFormat(static_cast(InformationPanelSettings::dateFormat())); connect(m_metaDataWidget, &Baloo::FileMetaDataWidget::urlActivated, this, &InformationPanelContent::urlActivated); m_metaDataWidget->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); // Configuration m_configureLabel = new QLabel(i18nc("@label::textbox", "Select which data should be shown:"), this); m_configureLabel->setWordWrap(true); m_configureLabel->setVisible(false); m_configureButtons = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel); m_configureButtons->setVisible(false); connect(m_configureButtons, &QDialogButtonBox::accepted, this, [this]() { m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::Accept); m_configureButtons->setVisible(false); m_configureLabel->setVisible(false); emit configurationFinished(); } ); connect(m_configureButtons, &QDialogButtonBox::rejected, this, [this]() { m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::Cancel); m_configureButtons->setVisible(false); m_configureLabel->setVisible(false); emit configurationFinished(); } ); m_metaDataArea = new QScrollArea(parent); m_metaDataArea->setWidget(m_metaDataWidget); m_metaDataArea->setWidgetResizable(true); m_metaDataArea->setFrameShape(QFrame::NoFrame); QWidget* viewport = m_metaDataArea->viewport(); viewport->installEventFilter(this); layout->addWidget(m_preview); layout->addWidget(m_phononWidget); layout->addWidget(m_nameLabel); layout->addWidget(new KSeparator()); layout->addWidget(m_configureLabel); layout->addWidget(m_metaDataArea); layout->addWidget(m_configureButtons); m_placesItemModel = new PlacesItemModel(this); } InformationPanelContent::~InformationPanelContent() { InformationPanelSettings::self()->save(); } void InformationPanelContent::showItem(const KFileItem& item) { if (item != m_item) { m_item = item; refreshMetaData(); } refreshPreview(); } +void InformationPanelContent::refreshPixmapView() +{ + // If there is a preview job, kill it to prevent that we have jobs for + // multiple items running, and thus a race condition (bug 250787). + if (m_previewJob) { + m_previewJob->kill(); + } + + // try to get a preview pixmap from the item... + + // Mark the currently shown preview as outdated. This is done + // with a small delay to prevent a flickering when the next preview + // can be shown within a short timeframe. This timer is not started + // for directories, as directory previews might fail and return the + // same icon. + if (!m_item.isDir()) { + m_outdatedPreviewTimer->start(); + } + + QStringList plugins = KIO::PreviewJob::availablePlugins(); + m_previewJob = new KIO::PreviewJob(KFileItemList() << m_item, + QSize(m_preview->width(), m_preview->height()), + &plugins); + m_previewJob->setScaleType(KIO::PreviewJob::Unscaled); + m_previewJob->setIgnoreMaximumSize(m_item.isLocalFile()); + if (m_previewJob->uiDelegate()) { + KJobWidgets::setWindow(m_previewJob, this); + } + + connect(m_previewJob.data(), &KIO::PreviewJob::gotPreview, + this, &InformationPanelContent::showPreview); + connect(m_previewJob.data(), &KIO::PreviewJob::failed, + this, &InformationPanelContent::showIcon); +} + void InformationPanelContent::refreshPreview() { // If there is a preview job, kill it to prevent that we have jobs for // multiple items running, and thus a race condition (bug 250787). if (m_previewJob) { m_previewJob->kill(); } + m_preview->setCursor(Qt::ArrowCursor); + bool usePhonon = false; setNameLabelText(m_item.text()); if (InformationPanelSettings::previewsShown()) { const QUrl itemUrl = m_item.url(); const bool isSearchUrl = itemUrl.scheme().contains(QStringLiteral("search")) && m_item.localPath().isEmpty(); if (isSearchUrl) { m_preview->show(); // in the case of a search-URL the URL is not readable for humans // (at least not useful to show in the Information Panel) m_preview->setPixmap( QIcon::fromTheme(QStringLiteral("nepomuk")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) ); } else { - // try to get a preview pixmap from the item... - - // Mark the currently shown preview as outdated. This is done - // with a small delay to prevent a flickering when the next preview - // can be shown within a short timeframe. This timer is not started - // for directories, as directory previews might fail and return the - // same icon. - if (!m_item.isDir()) { - m_outdatedPreviewTimer->start(); - } - QStringList plugins = KIO::PreviewJob::availablePlugins(); - m_previewJob = new KIO::PreviewJob(KFileItemList() << m_item, - QSize(m_preview->width(), m_preview->height()), - &plugins); - m_previewJob->setScaleType(KIO::PreviewJob::Unscaled); - m_previewJob->setIgnoreMaximumSize(m_item.isLocalFile()); - if (m_previewJob->uiDelegate()) { - KJobWidgets::setWindow(m_previewJob, this); - } - - connect(m_previewJob.data(), &KIO::PreviewJob::gotPreview, - this, &InformationPanelContent::showPreview); - connect(m_previewJob.data(), &KIO::PreviewJob::failed, - this, &InformationPanelContent::showIcon); + refreshPixmapView(); const QString mimeType = m_item.mimetype(); - const bool isVideo = mimeType.startsWith(QLatin1String("video/")); - const bool usePhonon = mimeType.startsWith(QLatin1String("audio/")) || isVideo; + m_isVideo = mimeType.startsWith(QLatin1String("video/")); + usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/")); if (usePhonon) { - - if (InformationPanelSettings::previewsAutoPlay() && isVideo) { - // hides the preview now to avoid flickering when the autoplay video starts - m_preview->hide(); - } else { - // the video won't play before the preview is displayed - m_preview->show(); + // change the cursor of the preview + m_preview->setCursor(Qt::PointingHandCursor); + m_preview->installEventFilter(m_phononWidget); + + // if the video is playing, has been paused or stopped + // we don't need to update the preview/phonon widget states + // unless the previewed file has changed, + // or the setting previewshown has changed + if ((m_phononWidget->state() != Phonon::State::PlayingState && + m_phononWidget->state() != Phonon::State::PausedState && + m_phononWidget->state() != Phonon::State::StoppedState) || + m_item.targetUrl() != m_phononWidget->url() || + (!m_preview->isVisible() &&! m_phononWidget->isVisible())) { + + if (InformationPanelSettings::previewsAutoPlay() && m_isVideo) { + // hides the preview now to avoid flickering when the autoplay video starts + m_preview->hide(); + } else { + // the video won't play before the preview is displayed + m_preview->show(); + } + + m_phononWidget->show(); + m_phononWidget->setUrl(m_item.targetUrl(), m_isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio); + adjustWidgetSizes(parentWidget()->width()); } - - m_phononWidget->show(); - m_phononWidget->setUrl(m_item.targetUrl(), isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio); - m_phononWidget->setVideoSize(m_preview->size()); } else { // When we don't need it, hide the phonon widget first to avoid flickering m_phononWidget->hide(); m_preview->show(); + m_preview->removeEventFilter(m_phononWidget); + m_phononWidget->clearUrl(); } } } else { m_preview->hide(); m_phononWidget->hide(); } } void InformationPanelContent::configureShownProperties() { m_configureLabel->setVisible(true); m_configureButtons->setVisible(true); m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::ReStart); } void InformationPanelContent::refreshMetaData() { m_metaDataWidget->setDateFormat(static_cast(InformationPanelSettings::dateFormat())); m_metaDataWidget->show(); m_metaDataWidget->setItems(KFileItemList() << m_item); } void InformationPanelContent::showItems(const KFileItemList& items) { // If there is a preview job, kill it to prevent that we have jobs for // multiple items running, and thus a race condition (bug 250787). if (m_previewJob) { m_previewJob->kill(); } m_preview->setPixmap( QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) ); setNameLabelText(i18ncp("@label", "%1 item selected", "%1 items selected", items.count())); m_metaDataWidget->setItems(items); m_phononWidget->hide(); m_item = KFileItem(); } bool InformationPanelContent::eventFilter(QObject* obj, QEvent* event) { switch (event->type()) { case QEvent::Resize: { QResizeEvent* resizeEvent = static_cast(event); if (obj == m_metaDataArea->viewport()) { // The size of the meta text area has changed. Adjust the fixed // width in a way that no horizontal scrollbar needs to be shown. m_metaDataWidget->setFixedWidth(resizeEvent->size().width()); } else if (obj == parent()) { adjustWidgetSizes(resizeEvent->size().width()); } break; } case QEvent::Polish: adjustWidgetSizes(parentWidget()->width()); break; case QEvent::FontChange: m_metaDataWidget->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); break; default: break; } return QWidget::eventFilter(obj, event); } void InformationPanelContent::showIcon(const KFileItem& item) { m_outdatedPreviewTimer->stop(); QPixmap pixmap = QIcon::fromTheme(item.iconName()).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous); KIconLoader::global()->drawOverlays(item.overlays(), pixmap, KIconLoader::Desktop); m_preview->setPixmap(pixmap); } void InformationPanelContent::showPreview(const KFileItem& item, const QPixmap& pixmap) { m_outdatedPreviewTimer->stop(); - Q_UNUSED(item); + Q_UNUSED(item) QPixmap p = pixmap; KIconLoader::global()->drawOverlays(item.overlays(), p, KIconLoader::Desktop); + + if (m_isVideo) { + // adds a play arrow + + // compute relative pixel positions + const int zeroX = static_cast(p.width() / 2 - PLAY_ARROW_SIZE / 2 / devicePixelRatio()); + const int zeroY = static_cast(p.height() / 2 - PLAY_ARROW_SIZE / 2 / devicePixelRatio()); + + QPolygon arrow; + arrow << QPoint(zeroX, zeroY); + arrow << QPoint(zeroX, zeroY + PLAY_ARROW_SIZE); + arrow << QPoint(zeroX + PLAY_ARROW_SIZE, zeroY + PLAY_ARROW_SIZE / 2); + + QPainterPath path; + path.addPolygon(arrow); + + QLinearGradient gradient(QPointF(zeroX, zeroY), + QPointF(zeroX + PLAY_ARROW_SIZE,zeroY + PLAY_ARROW_SIZE)); + + QColor whiteColor = Qt::white; + QColor blackColor = Qt::black; + gradient.setColorAt(0, whiteColor); + gradient.setColorAt(1, blackColor); + + QBrush brush(gradient); + + QPainter painter(&p); + + QPen pen(blackColor, PLAY_ARROW_BORDER_SIZE, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); + painter.setPen(pen); + + painter.setRenderHint(QPainter::Antialiasing); + painter.drawPolygon(arrow); + painter.fillPath(path, brush); + } + m_preview->setPixmap(p); } void InformationPanelContent::markOutdatedPreview() { KIconEffect *iconEffect = KIconLoader::global()->iconEffect(); QPixmap disabledPixmap = iconEffect->apply(m_preview->pixmap(), KIconLoader::Desktop, KIconLoader::DisabledState); m_preview->setPixmap(disabledPixmap); } KFileItemList InformationPanelContent::items() { return m_metaDataWidget->items(); } void InformationPanelContent::slotHasVideoChanged(bool hasVideo) { m_preview->setVisible(InformationPanelSettings::previewsShown() && !hasVideo); + if (m_preview->isVisible() && m_preview->size().width() != m_preview->pixmap().size().width()) { + // in case the information panel has been resized when the preview was not displayed + // we need to refresh its content + refreshPixmapView(); + } } void InformationPanelContent::setPreviewAutoPlay(bool autoPlay) { m_phononWidget->setAutoPlay(autoPlay); } void InformationPanelContent::setNameLabelText(const QString& text) { QTextOption textOption; textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); const QString processedText = Qt::mightBeRichText(text) ? text : KStringHandler::preProcessWrap(text); QTextLayout textLayout(processedText); textLayout.setFont(m_nameLabel->font()); textLayout.setTextOption(textOption); QString wrappedText; wrappedText.reserve(processedText.length()); // wrap the text to fit into the width of m_nameLabel textLayout.beginLayout(); QTextLine line = textLayout.createLine(); while (line.isValid()) { line.setLineWidth(m_nameLabel->width()); wrappedText += processedText.midRef(line.textStart(), line.textLength()); line = textLayout.createLine(); if (line.isValid()) { wrappedText += QChar::LineSeparator; } } textLayout.endLayout(); m_nameLabel->setText(wrappedText); } void InformationPanelContent::adjustWidgetSizes(int width) { // If the text inside the name label or the info label cannot // get wrapped, then the maximum width of the label is increased // so that the width of the information panel gets increased. // To prevent this, the maximum width is adjusted to // the current width of the panel. const int maxWidth = width - style()->layoutSpacing(QSizePolicy::DefaultType, QSizePolicy::DefaultType, Qt::Horizontal) * 4; m_nameLabel->setMaximumWidth(maxWidth); // The metadata widget also contains a text widget which may return // a large preferred width. m_metaDataWidget->setMaximumWidth(maxWidth); // try to increase the preview as large as possible m_preview->setSizeHint(QSize(maxWidth, maxWidth)); if (m_phononWidget->isVisible()) { // assure that the size of the video player is the same as the preview size m_phononWidget->setVideoSize(QSize(maxWidth, maxWidth)); } } diff --git a/src/panels/information/informationpanelcontent.h b/src/panels/information/informationpanelcontent.h index 0d838b268..8daeb95b0 100644 --- a/src/panels/information/informationpanelcontent.h +++ b/src/panels/information/informationpanelcontent.h @@ -1,159 +1,165 @@ /*************************************************************************** * Copyright (C) 2009-2010 by Peter Penz * * * * 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 * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef INFORMATIONPANELCONTENT_H #define INFORMATIONPANELCONTENT_H #include #include #include #include #include class KFileItemList; class PhononWidget; class PixmapViewer; class PlacesItemModel; class QPixmap; class QDialogButtonBox; class QString; class QLabel; class QScrollArea; namespace KIO { class PreviewJob; } namespace Baloo { class FileMetaDataWidget; } /** * @brief Manages the widgets that display the meta information * for file items of the Information Panel. */ class InformationPanelContent : public QWidget { Q_OBJECT public: explicit InformationPanelContent(QWidget* parent = nullptr); ~InformationPanelContent() override; /** * Shows the meta information for the item \p item. * The preview of the item is generated asynchronously, * the other meta information are fetched synchronously. */ void showItem(const KFileItem& item); /** * Shows the meta information for the items \p items and its preview */ void showItems(const KFileItemList& items); KFileItemList items(); /** * Refreshes the preview display, hiding it if needed */ void refreshPreview(); /** * Switch the metadatawidget into configuration mode */ void configureShownProperties(); /* * Set the auto play media mode for the file previewed * Eventually starting media playback when turning it on * But not stopping it when turning it off */ void setPreviewAutoPlay(bool autoPlay); signals: void urlActivated( const QUrl& url ); void configurationFinished(); public slots: /** * Is invoked after the file meta data configuration dialog has been * closed and refreshes the displayed meta data by the panel. */ void refreshMetaData(); protected: /** @see QObject::eventFilter() */ bool eventFilter(QObject* obj, QEvent* event) override; private slots: /** * Is invoked if no preview is available for the item. In this * case the icon will be shown. */ void showIcon(const KFileItem& item); /** * Is invoked if a preview is available for the item. The preview * \a pixmap is shown inside the info page. */ void showPreview(const KFileItem& item, const QPixmap& pixmap); /** * Marks the currently shown preview as outdated * by greying the content. */ void markOutdatedPreview(); void slotHasVideoChanged(bool hasVideo); private: /** * Sets the text for the label \a m_nameLabel and assures that the * text is split in a way that it can be wrapped within the * label width (QLabel::setWordWrap() does not work if the * text represents one extremely long word). */ void setNameLabelText(const QString& text); /** * Adjusts the sizes of the widgets dependent on the available * width given by \p width. */ void adjustWidgetSizes(int width); + /** + * Refreshes the image in the PixmapViewer + */ + void refreshPixmapView(); + private: KFileItem m_item; QPointer m_previewJob; QTimer* m_outdatedPreviewTimer; PixmapViewer* m_preview; PhononWidget* m_phononWidget; QLabel* m_nameLabel; Baloo::FileMetaDataWidget* m_metaDataWidget; QScrollArea* m_metaDataArea; QLabel* m_configureLabel; QDialogButtonBox* m_configureButtons; PlacesItemModel* m_placesItemModel; + bool m_isVideo; }; #endif // INFORMATIONPANELCONTENT_H diff --git a/src/panels/information/phononwidget.cpp b/src/panels/information/phononwidget.cpp index 4ea2e6666..e301df270 100644 --- a/src/panels/information/phononwidget.cpp +++ b/src/panels/information/phononwidget.cpp @@ -1,246 +1,275 @@ /* This file is part of the KDE project Copyright (C) 2007 Matthias Kretz 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 along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "phononwidget.h" #include #include #include #include #include #include #include #include #include class EmbeddedVideoPlayer : public Phonon::VideoWidget { Q_OBJECT public: EmbeddedVideoPlayer(QWidget *parent = nullptr) : Phonon::VideoWidget(parent) { } void setSizeHint(const QSize& size) { m_sizeHint = size; updateGeometry(); } QSize sizeHint() const override { return m_sizeHint.isValid() ? m_sizeHint : Phonon::VideoWidget::sizeHint(); } private: QSize m_sizeHint; }; PhononWidget::PhononWidget(QWidget *parent) : QWidget(parent), m_url(), m_playButton(nullptr), m_stopButton(nullptr), m_topLayout(nullptr), m_media(nullptr), m_seekSlider(nullptr), m_audioOutput(nullptr), m_videoPlayer(nullptr) { } void PhononWidget::setUrl(const QUrl &url, MediaKind kind) { if (m_url != url) { m_url = url; m_isVideo = kind == MediaKind::Video; } if (m_autoPlay) { play(); } else { stop(); } } void PhononWidget::setAutoPlay(bool autoPlay) { m_autoPlay = autoPlay; if (!m_url.isEmpty() && (m_media == nullptr || m_media->state() != Phonon::State::PlayingState) && m_autoPlay && isVisible()) { play(); } } QUrl PhononWidget::url() const { return m_url; } +void PhononWidget::clearUrl() +{ + m_url.clear(); +} + +bool PhononWidget::eventFilter(QObject *object, QEvent *event) +{ + Q_UNUSED(object) + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + // toggle playback + if (m_media && m_media->state() == Phonon::State::PlayingState) { + m_media->pause(); + } else { + play(); + } + return true; + } + } + return false; +} + void PhononWidget::setVideoSize(const QSize& size) { if (m_videoSize != size) { m_videoSize = size; applyVideoSize(); } } QSize PhononWidget::videoSize() const { return m_videoSize; } void PhononWidget::showEvent(QShowEvent *event) { if (event->spontaneous()) { QWidget::showEvent(event); return; } if (!m_topLayout) { m_topLayout = new QVBoxLayout(this); m_topLayout->setContentsMargins(0, 0, 0, 0); QHBoxLayout *controlsLayout = new QHBoxLayout(); controlsLayout->setContentsMargins(0, 0, 0, 0); controlsLayout->setSpacing(0); m_playButton = new QToolButton(this); m_stopButton = new QToolButton(this); m_seekSlider = new Phonon::SeekSlider(this); controlsLayout->addWidget(m_playButton); controlsLayout->addWidget(m_stopButton); controlsLayout->addWidget(m_seekSlider); m_topLayout->addLayout(controlsLayout); const int smallIconSize = IconSize(KIconLoader::Small); const QSize buttonSize(smallIconSize, smallIconSize); m_playButton->setToolTip(i18n("play")); m_playButton->setIconSize(buttonSize); m_playButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); m_playButton->setAutoRaise(true); connect(m_playButton, &QToolButton::clicked, this, &PhononWidget::play); m_stopButton->setToolTip(i18n("stop")); m_stopButton->setIconSize(buttonSize); m_stopButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop"))); m_stopButton->setAutoRaise(true); m_stopButton->hide(); connect(m_stopButton, &QToolButton::clicked, this, &PhononWidget::stop); m_seekSlider->setIconVisible(false); // Creating an audio player or video player instance might take up to // 2 seconds when doing it the first time. To prevent that the user // interface gets noticeable blocked, the creation is delayed until // the play button has been pressed (see PhononWidget::play()). } } void PhononWidget::hideEvent(QHideEvent *event) { QWidget::hideEvent(event); if (!event->spontaneous()) { stop(); } } void PhononWidget::stateChanged(Phonon::State newstate) { setUpdatesEnabled(false); switch (newstate) { case Phonon::PlayingState: case Phonon::BufferingState: - m_stopButton->show(); m_playButton->hide(); + m_stopButton->show(); break; default: m_stopButton->hide(); m_playButton->show(); break; } setUpdatesEnabled(true); } void PhononWidget::play() { if (!m_media) { m_media = new Phonon::MediaObject(this); connect(m_media, &Phonon::MediaObject::stateChanged, this, &PhononWidget::stateChanged); connect(m_media, &Phonon::MediaObject::finished, this, &PhononWidget::finished); m_seekSlider->setMediaObject(m_media); } if (!m_videoPlayer) { m_videoPlayer = new EmbeddedVideoPlayer(this); + m_videoPlayer->setCursor(Qt::PointingHandCursor); m_videoPlayer->installEventFilter(this); m_topLayout->insertWidget(0, m_videoPlayer); Phonon::createPath(m_media, m_videoPlayer); applyVideoSize(); } if (!m_audioOutput) { m_audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this); Phonon::createPath(m_media, m_audioOutput); } if (m_isVideo) { emit hasVideoChanged(true); } if (m_url != m_media->currentSource().url()) { m_media->setCurrentSource(m_url); } m_media->play(); m_videoPlayer->setVisible(m_isVideo); } void PhononWidget::finished() { if (m_isVideo) { m_videoPlayer->hide(); emit hasVideoChanged(false); } } +Phonon::State PhononWidget::state() const +{ + return m_media == nullptr ? Phonon::State::StoppedState : m_media->state(); +} + void PhononWidget::stop() { if (m_media) { m_media->stop(); m_videoPlayer->hide(); emit hasVideoChanged(false); } } void PhononWidget::applyVideoSize() { if ((m_videoPlayer) && m_videoSize.isValid()) { m_videoPlayer->setSizeHint(m_videoSize); } } #include "phononwidget.moc" diff --git a/src/panels/information/phononwidget.h b/src/panels/information/phononwidget.h index b9e7d4f05..90a1d1e5a 100644 --- a/src/panels/information/phononwidget.h +++ b/src/panels/information/phononwidget.h @@ -1,104 +1,107 @@ /* This file is part of the KDE project Copyright (C) 2007 Matthias Kretz 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 along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PHONONWIDGET_H #define PHONONWIDGET_H #include #include #include #include namespace Phonon { class AudioOutput; class MediaObject; class SeekSlider; class VideoPlayer; } // namespace Phonon class EmbeddedVideoPlayer; class QToolButton; class QVBoxLayout; class PhononWidget : public QWidget { Q_OBJECT public: enum MediaKind { Video, Audio }; explicit PhononWidget(QWidget *parent = nullptr); void setUrl(const QUrl &url, MediaKind kind); QUrl url() const; + void clearUrl(); void setVideoSize(const QSize& size); QSize videoSize() const; + Phonon::State state() const; void setAutoPlay(bool autoPlay); + bool eventFilter(QObject *object, QEvent *event) override; signals: /** * Is emitted whenever the video-state * has changed: If true is returned, a video * including control-buttons will be shown. * If false is returned, no video is shown * and the control-buttons are available for * audio only. */ void hasVideoChanged(bool hasVideo); public slots: void play(); protected: void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; private slots: void stateChanged(Phonon::State newstate); void stop(); void finished(); private: void applyVideoSize(); private: QUrl m_url; QSize m_videoSize; QToolButton *m_playButton; QToolButton *m_stopButton; QVBoxLayout *m_topLayout; Phonon::MediaObject *m_media; Phonon::SeekSlider *m_seekSlider; Phonon::AudioOutput *m_audioOutput; EmbeddedVideoPlayer *m_videoPlayer; bool m_autoPlay; bool m_isVideo; }; #endif // PHONONWIDGET_H