X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/832fd0965594f011bf82417a2bea7f60b107eb44..58c48052c38e5bb4f6cf8047dc71e40cfbf04403:/src/panels/information/informationpanelcontent.cpp diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index 6e718f961..134c5e056 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -1,66 +1,59 @@ -/*************************************************************************** - * 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 * - ***************************************************************************/ +/* + * SPDX-FileCopyrightText: 2009 Peter Penz + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ #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 +#include #include #include #include -#include #include "dolphin_informationpanelsettings.h" -#include "phononwidget.h" +#include "mediawidget.h" #include "pixmapviewer.h" -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) +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_mediaWidget(nullptr) + , m_nameLabel(nullptr) + , m_metaDataWidget(nullptr) + , m_metaDataArea(nullptr) + , m_isVideo(false) { parent->installEventFilter(this); @@ -68,12 +61,11 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) : // 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->setInterval(100); m_outdatedPreviewTimer->setSingleShot(true); - connect(m_outdatedPreviewTimer, &QTimer::timeout, - this, &InformationPanelContent::markOutdatedPreview); + connect(m_outdatedPreviewTimer, &QTimer::timeout, this, &InformationPanelContent::markOutdatedPreview); - QVBoxLayout* layout = new QVBoxLayout(this); + QVBoxLayout *layout = new QVBoxLayout(this); // preview const int minPreviewWidth = KIconLoader::SizeEnormous + KIconLoader::SizeMedium; @@ -82,11 +74,11 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) : m_preview->setMinimumWidth(minPreviewWidth); m_preview->setMinimumHeight(KIconLoader::SizeEnormous); - m_phononWidget = new PhononWidget(parent); - m_phononWidget->hide(); - m_phononWidget->setMinimumWidth(minPreviewWidth); - connect(m_phononWidget, &PhononWidget::hasVideoChanged, - this, &InformationPanelContent::slotHasVideoChanged); + m_mediaWidget = new MediaWidget(parent); + m_mediaWidget->hide(); + m_mediaWidget->setMinimumWidth(minPreviewWidth); + m_mediaWidget->setAutoPlay(InformationPanelSettings::previewsAutoPlay()); + connect(m_mediaWidget, &MediaWidget::hasVideoChanged, this, &InformationPanelContent::slotHasVideoChanged); // name m_nameLabel = new QLabel(parent); @@ -96,32 +88,55 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) : m_nameLabel->setTextFormat(Qt::PlainText); m_nameLabel->setAlignment(Qt::AlignHCenter); m_nameLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + m_nameLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); 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); + 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); + Q_EMIT configurationFinished(); + }); + connect(m_configureButtons, &QDialogButtonBox::rejected, this, [this]() { + m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::Cancel); + m_configureButtons->setVisible(false); + m_configureLabel->setVisible(false); + Q_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(); + QWidget *viewport = m_metaDataArea->viewport(); + QScroller::grabGesture(viewport, QScroller::TouchGesture); viewport->installEventFilter(this); layout->addWidget(m_preview); - layout->addWidget(m_phononWidget); + layout->addWidget(m_mediaWidget); 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); + grabGesture(Qt::TapAndHoldGesture); } InformationPanelContent::~InformationPanelContent() @@ -129,12 +144,45 @@ InformationPanelContent::~InformationPanelContent() InformationPanelSettings::self()->save(); } -void InformationPanelContent::showItem(const KFileItem& item) +void InformationPanelContent::showItem(const KFileItem &item) { - m_item = item; + // compares item entries, comparing items only compares urls + if (m_item.entry() != item.entry()) { + m_item = item; + m_preview->stopAnimatedImage(); + refreshMetaData(); + } refreshPreview(); - refreshMetaData(); +} + +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. + m_outdatedPreviewTimer->start(); + + const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings"); + const QStringList plugins = globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins()); + 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() && !m_item.isSlow()); + m_previewJob->setDevicePixelRatio(devicePixelRatioF()); + 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() @@ -145,61 +193,76 @@ void InformationPanelContent::refreshPreview() m_previewJob->kill(); } + m_preview->setCursor(Qt::ArrowCursor); setNameLabelText(m_item.text()); if (InformationPanelSettings::previewsShown()) { - m_preview->show(); - const QUrl itemUrl = m_item.url(); - const bool isSearchUrl = itemUrl.scheme().contains(QStringLiteral("search")) && m_item.localPath().isEmpty(); + const bool isSearchUrl = itemUrl.scheme().contains(QLatin1String("search")) && m_item.localPath().isEmpty(); if (isSearchUrl) { + m_preview->show(); + m_mediaWidget->hide(); + // 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) - ); + m_preview->setPixmap(QIcon::fromTheme(QStringLiteral("baloo")).pixmap(m_preview->height(), m_preview->width())); } 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 usePhonon = mimeType.startsWith(QLatin1String("audio/")) || mimeType.startsWith(QLatin1String("video/")); - if (usePhonon) { - m_phononWidget->show(); - m_phononWidget->setUrl(m_item.targetUrl()); - m_phononWidget->setVideoSize(m_preview->size()); + const bool isAnimatedImage = m_preview->isAnimatedMimeType(mimeType); + m_isVideo = !isAnimatedImage && mimeType.startsWith(QLatin1String("video/")); + bool useMedia = m_isVideo || mimeType.startsWith(QLatin1String("audio/")); + + if (useMedia) { + // change the cursor of the preview + m_preview->setCursor(Qt::PointingHandCursor); + m_preview->installEventFilter(m_mediaWidget); + + m_mediaWidget->show(); + + // if the video is playing, has been paused or stopped + // we don't need to update the preview/media widget states + // unless the previewed file has changed, + // or the setting previewshown has changed + if ((m_mediaWidget->state() != QMediaPlayer::PlayingState && m_mediaWidget->state() != QMediaPlayer::PausedState + && m_mediaWidget->state() != QMediaPlayer::StoppedState) + || m_item.targetUrl() != m_mediaWidget->url() || (!m_preview->isVisible() && !m_mediaWidget->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_mediaWidget->setUrl(m_item.targetUrl(), m_isVideo ? MediaWidget::MediaKind::Video : MediaWidget::MediaKind::Audio); + adjustWidgetSizes(parentWidget()->width()); + } } else { - m_phononWidget->hide(); + if (isAnimatedImage) { + m_preview->setAnimatedImageFileName(itemUrl.toLocalFile()); + } + // When we don't need it, hide the media widget first to avoid flickering + m_mediaWidget->hide(); + m_preview->show(); + m_preview->removeEventFilter(m_mediaWidget); + m_mediaWidget->clearUrl(); } } } else { + m_preview->stopAnimatedImage(); m_preview->hide(); - m_phononWidget->hide(); + m_mediaWidget->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())); @@ -207,7 +270,7 @@ void InformationPanelContent::refreshMetaData() m_metaDataWidget->setItems(KFileItemList() << m_item); } -void InformationPanelContent::showItems(const KFileItemList& items) +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). @@ -215,23 +278,23 @@ void InformationPanelContent::showItems(const KFileItemList& items) m_previewJob->kill(); } - m_preview->setPixmap( - QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) - ); + m_preview->stopAnimatedImage(); + + m_preview->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(m_preview->height(), m_preview->width())); setNameLabelText(i18ncp("@label", "%1 item selected", "%1 items selected", items.count())); m_metaDataWidget->setItems(items); - m_phononWidget->hide(); + m_mediaWidget->hide(); m_item = KFileItem(); } -bool InformationPanelContent::eventFilter(QObject* obj, QEvent* event) +bool InformationPanelContent::eventFilter(QObject *obj, QEvent *event) { switch (event->type()) { case QEvent::Resize: { - QResizeEvent* resizeEvent = static_cast(event); + 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. @@ -257,32 +320,116 @@ bool InformationPanelContent::eventFilter(QObject* obj, QEvent* event) return QWidget::eventFilter(obj, event); } -void InformationPanelContent::showIcon(const KFileItem& item) +bool InformationPanelContent::event(QEvent *event) +{ + if (event->type() == QEvent::Gesture) { + gestureEvent(static_cast(event)); + return true; + } + return QWidget::event(event); +} + +bool InformationPanelContent::gestureEvent(QGestureEvent *event) +{ + if (!underMouse()) { + return false; + } + + QTapAndHoldGesture *tap = static_cast(event->gesture(Qt::TapAndHoldGesture)); + + if (tap) { + if (tap->state() == Qt::GestureFinished) { + Q_EMIT contextMenuRequested(tap->position().toPoint()); + } + event->accept(); + return true; + } + return false; +} + +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); + QIcon icon = QIcon::fromTheme(item.iconName()); + QPixmap pixmap = KIconUtils::addOverlays(icon, item.overlays()).pixmap(m_preview->size(), devicePixelRatioF()); + pixmap.setDevicePixelRatio(devicePixelRatioF()); m_preview->setPixmap(pixmap); } -void InformationPanelContent::showPreview(const KFileItem& item, - const QPixmap& pixmap) +void InformationPanelContent::showPreview(const KFileItem &item, const QPixmap &pixmap) { m_outdatedPreviewTimer->stop(); - Q_UNUSED(item); QPixmap p = pixmap; - KIconLoader::global()->drawOverlays(item.overlays(), p, KIconLoader::Desktop); + if (!item.overlays().isEmpty()) { + // Avoid scaling the images that are smaller than the preview size, to be consistent when there is no overlays + if (pixmap.height() < m_preview->height() && pixmap.width() < m_preview->width()) { + p = QPixmap(m_preview->size() * devicePixelRatioF()); + p.fill(Qt::transparent); + p.setDevicePixelRatio(devicePixelRatioF()); + + QPainter painter(&p); + painter.drawPixmap(QPointF{m_preview->width() / 2.0 - pixmap.width() / pixmap.devicePixelRatioF() / 2, + m_preview->height() / 2.0 - pixmap.height() / pixmap.devicePixelRatioF() / 2} + .toPoint(), + pixmap); + } + p = KIconUtils::addOverlays(p, item.overlays()).pixmap(m_preview->size(), devicePixelRatioF()); + p.setDevicePixelRatio(devicePixelRatioF()); + } + + if (m_isVideo) { + // adds a play arrow overlay + + auto maxDim = qMax(p.width(), p.height()); + auto arrowSize = qMax(PLAY_ARROW_SIZE, maxDim / 8); + + // compute relative pixel positions + const int zeroX = static_cast((p.width() / 2 - arrowSize / 2) / p.devicePixelRatio()); + const int zeroY = static_cast((p.height() / 2 - arrowSize / 2) / p.devicePixelRatio()); + + QPolygon arrow; + arrow << QPoint(zeroX, zeroY); + arrow << QPoint(zeroX, zeroY + arrowSize); + arrow << QPoint(zeroX + arrowSize, zeroY + arrowSize / 2); + + QPainterPath path; + path.addPolygon(arrow); + + QLinearGradient gradient(QPointF(zeroX, zeroY + arrowSize / 2), QPointF(zeroX + arrowSize, zeroY + arrowSize / 2)); + + 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); + if (m_item.isDir()) { + // directory preview can be long + // but since we always have icons to display + // use it until the preview is done + showIcon(m_item); + } else { + QPixmap disabledPixmap = m_preview->pixmap(); + KIconEffect::toDisabled(disabledPixmap); + m_preview->setPixmap(disabledPixmap); + } } KFileItemList InformationPanelContent::items() @@ -293,9 +440,19 @@ KFileItemList InformationPanelContent::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_mediaWidget->setAutoPlay(autoPlay); } -void InformationPanelContent::setNameLabelText(const QString& text) +void InformationPanelContent::setNameLabelText(const QString &text) { QTextOption textOption; textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); @@ -314,7 +471,7 @@ void InformationPanelContent::setNameLabelText(const QString& text) QTextLine line = textLayout.createLine(); while (line.isValid()) { line.setLineWidth(m_nameLabel->width()); - wrappedText += processedText.midRef(line.textStart(), line.textLength()); + wrappedText += QStringView(processedText).mid(line.textStart(), line.textLength()); line = textLayout.createLine(); if (line.isValid()) { @@ -343,9 +500,10 @@ void InformationPanelContent::adjustWidgetSizes(int width) // try to increase the preview as large as possible m_preview->setSizeHint(QSize(maxWidth, maxWidth)); - if (m_phononWidget->isVisible()) { + if (m_mediaWidget->isVisible()) { // assure that the size of the video player is the same as the preview size - m_phononWidget->setVideoSize(QSize(maxWidth, maxWidth)); + m_mediaWidget->setVideoSize(QSize(maxWidth, maxWidth)); } } +#include "moc_informationpanelcontent.cpp"