diff --git a/CMakeLists.txt b/CMakeLists.txt index f447284..a41cb17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,78 +1,78 @@ cmake_minimum_required(VERSION 3.0) # KDE Application Version, managed by release script set (KDE_APPLICATIONS_VERSION_MAJOR "19") set (KDE_APPLICATIONS_VERSION_MINOR "03") set (KDE_APPLICATIONS_VERSION_MICRO "70") set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") project(baloo-widgets VERSION ${KDE_APPLICATIONS_VERSION}) set(QT_MIN_VERSION "5.8.0") -set(KF5_MIN_VERSION "5.43.0") +set(KF5_MIN_VERSION "5.56.0") find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ${ECM_MODULE_PATH}) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Widgets Test) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Config KIO I18n FileMetaData Baloo) include(ECMSetupVersion) include(ECMInstallIcons) include(GenerateExportHeader) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMGenerateHeaders) include(ECMAddTests) include(CMakePackageConfigHelpers) #TODO remove these remove_definitions(-DQT_NO_CAST_FROM_ASCII) remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) ecm_setup_version(PROJECT VARIABLE_PREFIX BALOO_WIDGETS SOVERSION 5 VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/baloowidgets_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5BalooWidgetsConfigVersion.cmake") include_directories( ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src ) add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(test) add_subdirectory(autotests) endif() # Config files set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KF5BalooWidgets") configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5BalooWidgetsConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5BalooWidgetsConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5BalooWidgetsConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5BalooWidgetsConfigVersion.cmake" DESTINATION ${CMAKECONFIG_INSTALL_DIR} COMPONENT devel ) install(EXPORT KF5BalooWidgetsTargets NAMESPACE KF5:: DESTINATION ${CMAKECONFIG_INSTALL_DIR} FILE KF5BalooWidgetsTargets.cmake ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES ) diff --git a/src/widgetfactory.cpp b/src/widgetfactory.cpp index d375778..2784d73 100644 --- a/src/widgetfactory.cpp +++ b/src/widgetfactory.cpp @@ -1,342 +1,336 @@ /* Copyright (C) 2012-2014 Vishesh Handa Code largely copied/adapted from KFileMetadataProvider Copyright (C) 2010 by Peter Penz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "widgetfactory.h" #include "tagwidget.h" #include "kcommentwidget_p.h" #include "KRatingWidget" #include +#include #include #include #include #include #include #include #include #include namespace { static QString plainText(const QString& richText) { QString plainText; plainText.reserve(richText.length()); bool skip = false; for (int i = 0; i < richText.length(); ++i) { const QChar c = richText.at(i); if (c == QLatin1Char('<')) { skip = true; } else if (c == QLatin1Char('>')) { skip = false; } else if (!skip) { plainText.append(c); } } return plainText; } } using namespace Baloo; WidgetFactory::WidgetFactory(QObject* parent) : QObject(parent) , m_readOnly( false ) , m_noLinks( false ) , m_dateFormat(QLocale::LongFormat) { } WidgetFactory::~WidgetFactory() { } // // Widget Creation // static QString toString(const QVariant& value) { switch (value.type()) { case QVariant::Int: return QLocale().toString(value.toInt()); case QVariant::Double: return QLocale().toString(value.toDouble()); case QVariant::StringList: return value.toStringList().join(i18nc("String list separator", ", ")); case QVariant::List: { QStringList list; for (const QVariant& var : value.toList()) { list << toString(var); } return list.join(i18nc("String list separator", ", ")); } default: return value.toString(); } } QWidget* WidgetFactory::createWidget(const QString& prop, const QVariant& value, QWidget* parent) { QWidget* widget = nullptr; if (prop == QLatin1String("rating")) { widget = createRatingWidget( value.toInt(), parent ); } else if (prop == QLatin1String("userComment")) { widget = createCommentWidget( value.toString(), parent ); } else if (prop == QLatin1String("tags")) { QStringList tags = value.toStringList(); QCollator coll; coll.setNumericMode(true); std::sort(tags.begin(), tags.end(), [&](const QString& s1, const QString& s2){ return coll.compare(s1, s2) < 0; }); widget = createTagWidget( tags, parent ); } else { - KFormat form; - // vHanda: FIXME: Add links! Take m_noLinks into consideration QString valueString; - - if (prop == QLatin1String("duration")) { - valueString = form.formatDuration(value.toInt() * 1000); - } else if (prop == QLatin1String("bitRate")) { - valueString = i18nc("@label bitrate (per second)", "%1/s", form.formatByteSize(value.toInt(), 0, KFormat::MetricBinaryDialect)); - } else if (prop == QLatin1String("sampleRate")) { - valueString = i18nc("@label samplerate in kilohertz", "%1 kHz", QLocale().toString(value.toDouble() / 1000)); - } else if (prop == QLatin1String("releaseYear")) { - valueString = value.toString(); - } else if (prop == QLatin1String("originUrl")) { + auto pi = KFileMetaData::PropertyInfo::fromName(prop); + if (pi.name() == QLatin1String("originUrl")) { if (m_noLinks) { valueString = value.toString(); } else { valueString = QStringLiteral("%1").arg(value.toString()); } + } else if (pi.name() != QLatin1String("empty")) { + valueString = pi.formatAsDisplayString(value); } else { // Check if Date/DateTime QDateTime dt = QDateTime::fromString(value.toString(), Qt::ISODate); if (dt.isValid()) { + KFormat form; QTime time = dt.time(); if (!time.hour() && !time.minute() && !time.second()){ valueString = form.formatRelativeDate(dt.date(), m_dateFormat); } else { valueString = form.formatRelativeDateTime(dt, m_dateFormat); } } else { valueString = toString(value); } } widget = createValueWidget(valueString, parent); } widget->setForegroundRole(parent->foregroundRole()); widget->setFont(parent->font()); widget->setObjectName(prop); return widget; } QWidget* WidgetFactory::createTagWidget(const QStringList& tags, QWidget* parent) { TagWidget* tagWidget = new TagWidget(parent); tagWidget->setReadyOnly(m_readOnly); tagWidget->setSelectedTags(tags); connect(tagWidget, &TagWidget::selectionChanged, this, &WidgetFactory::slotTagsChanged); connect(tagWidget, &TagWidget::tagClicked, this, &WidgetFactory::slotTagClicked); m_tagWidget = tagWidget; m_prevTags = tags; return tagWidget; } QWidget* WidgetFactory::createCommentWidget(const QString& comment, QWidget* parent) { KCommentWidget* commentWidget = new KCommentWidget(parent); commentWidget->setText(comment); commentWidget->setReadOnly(m_readOnly); connect(commentWidget, &KCommentWidget::commentChanged, this, &WidgetFactory::slotCommentChanged); m_commentWidget = commentWidget; return commentWidget; } QWidget* WidgetFactory::createRatingWidget(int rating, QWidget* parent) { KRatingWidget* ratingWidget = new KRatingWidget(parent); const Qt::Alignment align = (ratingWidget->layoutDirection() == Qt::LeftToRight) ? Qt::AlignLeft : Qt::AlignRight; ratingWidget->setAlignment(align); ratingWidget->setRating(rating); const QFontMetrics metrics(parent->font()); ratingWidget->setPixmapSize(metrics.height()); connect(ratingWidget, static_cast(&KRatingWidget::ratingChanged), this, &WidgetFactory::slotRatingChanged); m_ratingWidget = ratingWidget; return ratingWidget; } // The default size hint of QLabel tries to return a square size. // This does not work well in combination with layouts that use // heightForWidth(): In this case it is possible that the content // of a label might get clipped. By specifying a size hint // with a maximum width that is necessary to contain the whole text, // using heightForWidth() assures having a non-clipped text. class ValueWidget : public QLabel { public: explicit ValueWidget(QWidget* parent = nullptr); QSize sizeHint() const override; }; ValueWidget::ValueWidget(QWidget* parent) : QLabel(parent) { } QSize ValueWidget::sizeHint() const { QFontMetrics metrics(font()); // TODO: QLabel internally provides already a method sizeForWidth(), // that would be sufficient. However this method is not accessible, so // as workaround the tags from a richtext are removed manually here to // have a proper size hint. return metrics.size(Qt::TextSingleLine, plainText(text())); } QWidget* WidgetFactory::createValueWidget(const QString& value, QWidget* parent) { ValueWidget* valueWidget = new ValueWidget(parent); valueWidget->setWordWrap(true); valueWidget->setAlignment(Qt::AlignTop | Qt::AlignLeft); valueWidget->setText(m_readOnly ? plainText(value) : value); connect(valueWidget, &ValueWidget::linkActivated, this, &WidgetFactory::slotLinkActivated); return valueWidget; } // // Data Synchronization // void WidgetFactory::slotCommentChanged(const QString& comment) { for (const QString& filePath : m_items) { KFileMetaData::UserMetaData md(filePath); md.setUserComment(comment); } emit dataChangeStarted(); emit dataChangeFinished(); } void WidgetFactory::slotRatingChanged(uint rating) { for (const QString& filePath : m_items) { KFileMetaData::UserMetaData md(filePath); md.setRating(rating); } emit dataChangeStarted(); emit dataChangeFinished(); } void WidgetFactory::slotTagsChanged(const QStringList& tags) { if (m_tagWidget) { for (const QString& filePath : m_items) { KFileMetaData::UserMetaData md(filePath); // When multiple tags are selected one doesn't want to loose the old tags // of any of the resources. Unless specifically removed. QStringList newTags = md.tags() + tags; newTags.removeDuplicates(); for (const QString& tag : m_prevTags) { if (!tags.contains(tag)) { newTags.removeAll(tag); } } md.setTags(newTags); } m_prevTags = tags; emit dataChangeStarted(); emit dataChangeFinished(); } } // // Notifications // void WidgetFactory::slotLinkActivated(const QString& url) { emit urlActivated(QUrl::fromUserInput(url)); } void WidgetFactory::slotTagClicked(const QString& tag) { QUrl url; url.setScheme("tags"); url.setPath(tag); emit urlActivated(url); } // // Accessor Methods // void WidgetFactory::setReadOnly(bool value) { m_readOnly = value; } void WidgetFactory::setNoLinks(bool value) { m_noLinks = value; } void WidgetFactory::setItems(const QStringList& items) { m_items = items; } Baloo::DateFormats WidgetFactory::dateFormat() const { return static_cast(m_dateFormat); } void Baloo::WidgetFactory::setDateFormat(const Baloo::DateFormats format) { m_dateFormat = static_cast(format); }