diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 267da87..286592a 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,18 +1,19 @@ include(ECMMarkAsTest) ecm_add_test(extractortest.cpp TEST_NAME "extractortest" LINK_LIBRARIES Qt5::Test ) ecm_add_tests( filemetadatawidgettest.cpp filemetadataitemcounttest.cpp + filemetadatadatedisplaytest.cpp LINK_LIBRARIES KF5::KIOCore KF5::KIOWidgets KF5::KIOFileWidgets KF5::ConfigCore KF5::BalooWidgets KF5::FileMetaData Qt5::Test Qt5::Widgets ) diff --git a/autotests/filemetadatadatedisplaytest.cpp b/autotests/filemetadatadatedisplaytest.cpp new file mode 100644 index 0000000..552cda0 --- /dev/null +++ b/autotests/filemetadatadatedisplaytest.cpp @@ -0,0 +1,110 @@ +/* + * This file is part of the KDE Baloo Project + * Copyright 2018 Michael Heidelbach + * + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + */ + +#include "filemetadatadatedisplaytest.h" +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +void initLocale() +{ + QLocale::setDefault(QLocale("en_US")); +} +Q_CONSTRUCTOR_FUNCTION(initLocale) + +QTEST_MAIN(FileMetadataDateDisplayTest) + +void FileMetadataDateDisplayTest::initTestCase() +{ + qRegisterMetaType("KFileItemList"); + QStandardPaths::setTestModeEnabled(true); + + auto yesterday = QDateTime::currentDateTime().addDays(-1); + QFile* file(new QFile(QFINDTESTDATA("samplefiles/testtagged.m4a"))); + file->open(QIODevice::ReadOnly); + QVERIFY(file->setFileTime(yesterday, QFileDevice::FileModificationTime)); + file->close(); + delete file; + auto ancient = QDateTime::currentDateTime().addYears(-10); + file = new QFile(QFINDTESTDATA("samplefiles/testtagged.mp3")); + file->open(QIODevice::ReadOnly); + QVERIFY(file->setFileTime(ancient, QFileDevice::FileModificationTime)); + file->close(); +} + +void FileMetadataDateDisplayTest::shouldDisplayLongAndShortDates_data() +{ + QTest::addColumn("format"); + QTest::addColumn("file"); + QTest::addColumn("regex"); + + QTest::addRow("Short date, long ago") + << Baloo::DateFormats::ShortFormat + << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.mp3")) + << QRegularExpression("(?:[1-3][0-9]|[1-9]) (?:[1-2][0-9]|[1-9]):[0-5][0-9] [AP]M") + ; + + QTest::addRow("Short date, yesterday") + << Baloo::DateFormats::ShortFormat + << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.m4a")) + << QRegularExpression("Yesterday, (?:[1-2][0-9]|[1-9]):[0-5][0-9] [AP]M") + ; + + QTest::addRow("Long date, long ago") + << Baloo::DateFormats::LongFormat + << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.mp3")) + << QRegularExpression("[A-Z][a-z]+, [A-Z][a-z]+ (?:[1-3][0-9]|[1-9]), 20[0-9]{2} (?:1[0-2]|[1-9]):[0-5][0-9]:[0-5][0-9] [AP]M [A-Z]{3,4}") + ; + + QTest::addRow("Long date, yesterday") + << Baloo::DateFormats::LongFormat + << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.m4a")) + << QRegularExpression("Yesterday, (?:1[0-2]|[1-9]):[0-5][0-9]:[0-5][0-9] [AP]M [A-Z]{3,4}") + ; + +} + +void FileMetadataDateDisplayTest::shouldDisplayLongAndShortDates() +{ + QFETCH(Baloo::DateFormats, format); + QFETCH(QUrl, file); + QFETCH(QRegularExpression, regex); + + const auto widget = new Baloo::FileMetaDataWidget(); + widget->setDateFormat(format); + QSignalSpy spy(widget, &Baloo::FileMetaDataWidget::metaDataRequestFinished); + widget->setItems(KFileItemList() << file); + QVERIFY(spy.wait()); + + QLabel* dateWidget = widget->findChild(QStringLiteral("kfileitem#modified"), Qt::FindDirectChildrenOnly); + QVERIFY2(dateWidget, "Date widget not found"); + QVERIFY2(regex.match(dateWidget->text()).hasMatch(), + qPrintable(QStringLiteral("\"%1\" did not match %2").arg(dateWidget->text(), regex.pattern())) + ); +} diff --git a/autotests/filemetadatadatedisplaytest.h b/autotests/filemetadatadatedisplaytest.h new file mode 100644 index 0000000..2cc3636 --- /dev/null +++ b/autotests/filemetadatadatedisplaytest.h @@ -0,0 +1,38 @@ +/* + * This file is part of the KDE Baloo Project + * Copyright 2018 Michael Heidelbach + * + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + */ + +#ifndef FILEMETADATADATEDISPLAYTEST_H +#define FILEMETADATADATEDISPLAYTEST_H + +#include + +class FileMetadataDateDisplayTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + + void shouldDisplayLongAndShortDates(); + void shouldDisplayLongAndShortDates_data(); + +}; + +#endif // FILEMETADATADATEDISPLAYTEST_H diff --git a/autotests/filemetadatawidgettest.cpp b/autotests/filemetadatawidgettest.cpp index e137e17..3100fd1 100644 --- a/autotests/filemetadatawidgettest.cpp +++ b/autotests/filemetadatawidgettest.cpp @@ -1,202 +1,202 @@ /* * This file is part of the KDE Baloo Project * Copyright 2018 Michael Heidelbach - * + * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. - * + * * 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, see . */ #include "filemetadatawidgettest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include void initLocale() { QLocale::setDefault(QLocale("en_US")); } Q_CONSTRUCTOR_FUNCTION(initLocale) QTEST_MAIN(FileMetadataWidgetTest) void FileMetadataWidgetTest::initTestCase() { qRegisterMetaType("KFileItemList"); - + QStandardPaths::setTestModeEnabled(true); KConfig balooConfig("baloofilerc", KConfig::NoGlobals); KConfigGroup balooSettings = balooConfig.group("General"); // If we use .writePathEntry here, the test will fail. balooSettings.writeEntry(QStringLiteral("folders"), QString()); - + // Ensure show configuration KConfig config("baloofileinformationrc", KConfig::NoGlobals); KConfigGroup settings = config.group("Show"); const auto keys = settings.keyList(); for (const auto &key: keys) { settings.writeEntry(key, true); } const QString exe = QStandardPaths::findExecutable(QStringLiteral("setfattr")); if (exe.isEmpty()) { return; } - + const QStringList args = {QStringLiteral("--name=user.baloo.rating"), QStringLiteral("--value=5") , QFINDTESTDATA("samplefiles/testtagged.mp3"), QFINDTESTDATA("samplefiles/testtagged.m4a")}; QProcess process; process.start(exe, args); if (!process.waitForFinished(10000)) { qDebug() << "setfattr timed out"; return; } if (process.exitStatus() == QProcess::NormalExit) { m_mayTestRating = true; } else { qDebug() << "setfattr err:" << process.readAllStandardError(); } } void FileMetadataWidgetTest::init() { m_widget = new Baloo::FileMetaDataWidget; } void FileMetadataWidgetTest::cleanup() { delete m_widget; } void FileMetadataWidgetTest::shouldSignalOnceWithoutFile() { QSignalSpy spy(m_widget, &Baloo::FileMetaDataWidget::metaDataRequestFinished); m_widget->setItems(KFileItemList() << QUrl()); QCOMPARE(spy.count(), 1); QCOMPARE(m_widget->items().count(), 0); } void FileMetadataWidgetTest::shouldSignalOnceWithEmptyFile() { QSignalSpy spy(m_widget, &Baloo::FileMetaDataWidget::metaDataRequestFinished); m_widget->setItems(KFileItemList()); QCOMPARE(spy.count(), 1); QCOMPARE(m_widget->items().count(), 0); } void FileMetadataWidgetTest::shouldSignalOnceFile() { QSignalSpy spy(m_widget, &Baloo::FileMetaDataWidget::metaDataRequestFinished); - m_widget->setItems(KFileItemList() + m_widget->setItems(KFileItemList() << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.m4a")) ); QVERIFY(spy.wait()); QCOMPARE(spy.count(), 1); QCOMPARE(m_widget->items().count(), 1); - + } void FileMetadataWidgetTest::shouldSignalOnceFiles() { QSignalSpy spy(m_widget, &Baloo::FileMetaDataWidget::metaDataRequestFinished); - m_widget->setItems(KFileItemList() + m_widget->setItems(KFileItemList() << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/test.mp3")) << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.mp3")) << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.m4a")) ); QVERIFY(spy.wait()); QCOMPARE(spy.count(), 1); QCOMPARE(m_widget->items().count(), 3); } void FileMetadataWidgetTest::shouldShowProperties() { QSignalSpy spy(m_widget, &Baloo::FileMetaDataWidget::metaDataRequestFinished); - m_widget->setItems(KFileItemList() + m_widget->setItems(KFileItemList() << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.mp3")) ); - + QVERIFY(spy.wait()); QCOMPARE(spy.count(), 1); - + // simple property QLabel* valueWidget = m_widget->findChild("kfileitem#type"); QVERIFY2(valueWidget, "Type data missing"); QCOMPARE(valueWidget->text(), QLatin1String("MP3 audio")); - + if (m_mayTestRating) { // editable property KRatingWidget* ratingWidget = m_widget->findChild("rating"); QVERIFY2(ratingWidget, "Rating data missing"); QCOMPARE(ratingWidget->rating(), 5u); } else { qDebug() << "Skipped 'Rating' test"; } // async property valueWidget = m_widget->findChild("albumArtist"); QVERIFY2(valueWidget, "albumArtist data was not found"); QCOMPARE(valueWidget->text(), QLatin1String("Bill Laswell")); - + } void FileMetadataWidgetTest::shouldShowCommonProperties() { QSignalSpy spy(m_widget, &Baloo::FileMetaDataWidget::metaDataRequestFinished); - m_widget->setItems(KFileItemList() + m_widget->setItems(KFileItemList() << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.mp3")) << QUrl::fromLocalFile(QFINDTESTDATA("samplefiles/testtagged.m4a")) ); QVERIFY(spy.wait()); QCOMPARE(spy.count(), 1); - + // simple property QLabel* valueWidget = m_widget->findChild("kfileitem#type"); QVERIFY(!valueWidget); - + valueWidget = m_widget->findChild("kfileitem#totalSize"); // circumvent i18n formatting QCOMPARE(valueWidget->text().left(3), QLatin1String("153")); - + // editable property if (m_mayTestRating) { KRatingWidget* ratingWidget = m_widget->findChild("rating"); QVERIFY2(ratingWidget, "Rating data missing"); QCOMPARE(ratingWidget->rating(), 5u); } else { qDebug() << "Skipped 'Rating' test"; } // async property // FIXME: Make this pass // QCOMPARE( map->value("Album Artist:"), QLatin1String("Bill Laswell")); } diff --git a/src/filemetadataprovider.cpp b/src/filemetadataprovider.cpp index ef90e19..2da6973 100644 --- a/src/filemetadataprovider.cpp +++ b/src/filemetadataprovider.cpp @@ -1,522 +1,521 @@ /***************************************************************************** * Copyright (C) 2010 by Peter Penz * * Copyright (C) 2012 by Vishesh Handa * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *****************************************************************************/ #include "filemetadataprovider.h" #include "indexeddataretriever.h" #include "filefetchjob.h" #include #include #include #include #include // Required includes for subDirectoriesCount(): #ifdef Q_OS_WIN #include #else #include #include #endif using namespace Baloo; namespace { QVariant intersect(const QVariant& v1, const QVariant& v2) { if (!v1.isValid() || !v2.isValid()) { return QVariant(); } // List and String if (v1.type() == QVariant::StringList && v2.type() == QVariant::String) { QStringList list = v1.toStringList(); QString str = v2.toString(); if (!list.contains(str)) { list << str; } return QVariant(list); } // String and List if (v1.type() == QVariant::String && v2.type() == QVariant::StringList) { QStringList list = v2.toStringList(); QString str = v1.toString(); if (!list.contains(str)) { list << str; } return QVariant(list); } // List and List if (v1.type() == QVariant::StringList && v2.type() == QVariant::StringList) { QSet s1 = v1.toStringList().toSet(); QSet s2 = v2.toStringList().toSet(); return QVariant(s1.intersect(s2).toList()); } if (v1 == v2) { return v1; } return QVariant(); } /** * The standard QMap::unite will contain the key multiple times if both \p v1 and \p v2 * contain the same key. * * This will only take the key from \p v2 into account */ QVariantMap unite(const QVariantMap& v1, const QVariantMap& v2) { QVariantMap v(v1); QMapIterator it(v2); while (it.hasNext()) { it.next(); v[it.key()] = it.value(); } return v; } } void FileMetaDataProvider::totalPropertyAndInsert(const QString& prop, const QList& resources, QSet& allProperties) { if (allProperties.contains(prop)) { int total = 0; foreach (const QVariantMap& map, resources) { QVariantMap::const_iterator it = map.constFind(prop); if (it == map.constEnd()) { total = 0; break; } else { total += it.value().toInt(); } } if (total) { m_data.insert (prop, QVariant(total)); } allProperties.remove(prop); } } void FileMetaDataProvider::slotFileFetchFinished(KJob* job) { FileFetchJob* fetchJob = static_cast(job); QList files = fetchJob->data(); Q_ASSERT(!files.isEmpty()); if (files.size() > 1) { insertCommonData(files); } else { m_data = files.first(); insertSingleFileBasicData(); } insertEditableData(); emit loadingFinished(); } void FileMetaDataProvider::slotLoadingFinished(KJob* job) { IndexedDataRetriever* ret = dynamic_cast(job); m_data = unite(m_data, ret->data()); emit loadingFinished(); } void FileMetaDataProvider::insertSingleFileBasicData() { // TODO: Handle case if remote URLs are used properly. isDir() does // not work, the modification date needs also to be adjusted... Q_ASSERT(m_fileItems.count() <= 1); if (m_fileItems.count() == 1) { KFormat format; const KFileItem& item = m_fileItems.first(); if (item.isDir()) { const int count = subDirectoriesCount(item.url().path()); if (count == -1) { m_data.insert("kfileitem#size", i18nc("unknown file size", "Unknown")); } else { const QString itemCountString = i18ncp("@item:intable", "%1 item", "%1 items", count); m_data.insert("kfileitem#size", itemCountString); } } else { m_data.insert("kfileitem#size", format.formatByteSize(item.size())); } m_data.insert("kfileitem#type", item.mimeComment()); - m_data.insert("kfileitem#modified", - format.formatRelativeDateTime(item.time(KFileItem::ModificationTime), QLocale::LongFormat)); + m_data.insert("kfileitem#modified", item.time(KFileItem::ModificationTime)); m_data.insert("kfileitem#owner", item.user()); m_data.insert("kfileitem#group", item.group()); m_data.insert("kfileitem#permissions", item.permissionsString()); } } void FileMetaDataProvider::insertFilesListBasicData() { KFormat format; // If all directories Q_ASSERT(m_fileItems.count() > 1); bool allDirectories = true; for (const KFileItem& item : m_fileItems) { allDirectories &= item.isDir(); if (!allDirectories) { break; } } if (allDirectories) { int count = 0; for (const KFileItem& item : m_fileItems) { count += subDirectoriesCount(item.url().path()); } if (count) { const QString itemCountString = i18ncp("@item:intable", "%1 item", "%1 items", count); m_data.insert("kfileitem#totalSize", itemCountString); } } else { // Calculate the size of all items quint64 totalSize = 0; for (const KFileItem& item : m_fileItems) { if (!item.isDir() && !item.isLink()) { totalSize += item.size(); } } m_data.insert("kfileitem#totalSize", format.formatByteSize(totalSize)); } } void FileMetaDataProvider::insertBasicData() { if (m_fileItems.count() > 1) { insertFilesListBasicData(); } else { insertSingleFileBasicData(); } } void FileMetaDataProvider::insertEditableData() { if (!m_readOnly) { if (!m_data.contains("tags")) { m_data.insert("tags", QVariant()); } if (!m_data.contains("rating")) { m_data.insert("rating", 0); } if (!m_data.contains("userComment")) { m_data.insert("userComment", QVariant()); } } } void FileMetaDataProvider::insertCommonData(const QList& files) { // // Only report the stuff that is common to all the files // QSet allProperties; QList propertyList; foreach (const QVariantMap& fileData, files) { propertyList << fileData; allProperties.unite(fileData.uniqueKeys().toSet()); } // Special handling for certain properties totalPropertyAndInsert("duration", propertyList, allProperties); totalPropertyAndInsert("characterCount", propertyList, allProperties); totalPropertyAndInsert("wordCount", propertyList, allProperties); totalPropertyAndInsert("lineCount", propertyList, allProperties); foreach (const QString& propUri, allProperties) { foreach (const QVariantMap& map, propertyList) { QVariantMap::const_iterator it = map.find(propUri); if (it == map.constEnd()) { m_data.remove(propUri); break; } QVariantMap::iterator dit = m_data.find(it.key()); if (dit == m_data.end()) { m_data.insert(propUri, it.value()); } else { QVariant finalValue = intersect(it.value(), dit.value()); if (finalValue.isValid()) { m_data[propUri] = finalValue; } else { m_data.remove(propUri); break; } } } } } FileMetaDataProvider::FileMetaDataProvider(QObject* parent) : QObject(parent) , m_readOnly(false) , m_realTimeIndexing(false) { } FileMetaDataProvider::~FileMetaDataProvider() { } void FileMetaDataProvider::setFileItem() { // There are 3 code paths - // Remote file // Single local file - // * Not Indexed // * Indexed // const QUrl url = m_fileItems.first().targetUrl(); if (!url.isLocalFile()) { insertBasicData(); emit loadingFinished(); return; } // Not Indexed const QString filePath = url.toLocalFile(); if (!m_config.fileIndexingEnabled() || !m_config.shouldBeIndexed(filePath)) { m_realTimeIndexing = true; insertBasicData(); insertEditableData(); IndexedDataRetriever *ret = new IndexedDataRetriever(filePath, this); connect(ret, SIGNAL(finished(KJob*)), this, SLOT(slotLoadingFinished(KJob*))); ret->start(); } else { FileFetchJob* job = new FileFetchJob(QStringList() << filePath, this); connect(job, SIGNAL(finished(KJob*)), this, SLOT(slotFileFetchFinished(KJob*))); job->start(); } } void FileMetaDataProvider::setFileItems() { // Multiple Files - // * Not Indexed // * Indexed QStringList urls; // Only extract data from indexed files, // it would be too expensive otherwise. Q_FOREACH (const KFileItem& item, m_fileItems) { const QUrl url = item.targetUrl(); if (url.isLocalFile()) { urls << url.toLocalFile(); } } if (!urls.isEmpty()) { insertBasicData(); FileFetchJob* job = new FileFetchJob(urls, this); connect(job, SIGNAL(finished(KJob*)), this, SLOT(slotFileFetchFinished(KJob*))); job->start(); } else { insertBasicData(); emit loadingFinished(); } } void FileMetaDataProvider::setItems(const KFileItemList& items) { m_fileItems = items; m_data.clear(); m_realTimeIndexing = false; if (items.isEmpty()) { emit loadingFinished(); } else if (items.size() == 1) { setFileItem(); } else { setFileItems(); } } QString FileMetaDataProvider::label(const QString& metaDataLabel) const { struct TranslationItem { const char* const key; const char* const context; const char* const value; }; static const TranslationItem translations[] = { { "kfileitem#comment", I18N_NOOP2_NOSTRIP("@label", "Comment") }, { "kfileitem#modified", I18N_NOOP2_NOSTRIP("@label", "Modified") }, { "kfileitem#owner", I18N_NOOP2_NOSTRIP("@label", "Owner") }, { "kfileitem#group", I18N_NOOP2_NOSTRIP("@label", "Group") }, { "kfileitem#permissions", I18N_NOOP2_NOSTRIP("@label", "Permissions") }, { "kfileitem#rating", I18N_NOOP2_NOSTRIP("@label", "Rating") }, { "kfileitem#size", I18N_NOOP2_NOSTRIP("@label", "Size") }, { "kfileitem#tags", I18N_NOOP2_NOSTRIP("@label", "Tags") }, { "kfileitem#totalSize", I18N_NOOP2_NOSTRIP("@label", "Total Size") }, { "kfileitem#type", I18N_NOOP2_NOSTRIP("@label", "Type") }, { "tags", I18N_NOOP2_NOSTRIP("@label", "Tags") }, { "rating", I18N_NOOP2_NOSTRIP("@label", "Rating") }, { "userComment", I18N_NOOP2_NOSTRIP("@label", "Comment") }, { "originUrl", I18N_NOOP2_NOSTRIP("@label", "Downloaded From") }, { 0, 0, 0} // Mandatory last entry }; static QHash hash; if (hash.isEmpty()) { const TranslationItem* item = &translations[0]; while (item->key != 0) { hash.insert(item->key, i18nc(item->context, item->value)); ++item; } } QString value = hash.value(metaDataLabel); if (value.isEmpty()) { value = KFileMetaData::PropertyInfo::fromName(metaDataLabel).displayName(); } return value; } QString FileMetaDataProvider::group(const QString& label) const { static QHash uriGrouper; if (uriGrouper.isEmpty()) { // KFileItem Data uriGrouper.insert(QLatin1String("kfileitem#type"), QLatin1String("0FileItemA")); uriGrouper.insert(QLatin1String("kfileitem#size"), QLatin1String("0FileItemB")); uriGrouper.insert(QLatin1String("kfileitem#totalSize"), QLatin1String("0FileItemB")); uriGrouper.insert(QLatin1String("kfileitem#modified"), QLatin1String("0FileItemC")); uriGrouper.insert(QLatin1String("kfileitem#owner"), QLatin1String("0FileItemD")); uriGrouper.insert(QLatin1String("kfileitem#group"), QLatin1String("0FileItemE")); uriGrouper.insert(QLatin1String("kfileitem#permissions"), QLatin1String("0FileItemF")); // Editable Data uriGrouper.insert(QLatin1String("tags"), QLatin1String("1EditableDataA")); uriGrouper.insert(QLatin1String("rating"), QLatin1String("1EditableDataB")); uriGrouper.insert(QLatin1String("userComment"), QLatin1String("1EditableDataC")); // Image Data uriGrouper.insert(QLatin1String("width"), QLatin1String("2SizA")); uriGrouper.insert(QLatin1String("height"), QLatin1String("2SizeB")); // Music Data uriGrouper.insert("title", QLatin1String("3MusicA")); uriGrouper.insert("artist", QLatin1String("3MusicB")); uriGrouper.insert("album", QLatin1String("3MusicC")); uriGrouper.insert("genre", QLatin1String("3MusicD")); uriGrouper.insert("trackNumber", QLatin1String("3MusicE")); // Audio Data uriGrouper.insert("duration", QLatin1String("4AudioA")); uriGrouper.insert("sampleRate", QLatin1String("4AudioB")); uriGrouper.insert("sampleCount", QLatin1String("4AudioC")); // Miscellaneous Data uriGrouper.insert("originUrl", QLatin1String("5MiscA")); } const QString val = uriGrouper.value(label); if (val.isEmpty()) { return "lastGroup"; } return val; } KFileItemList FileMetaDataProvider::items() const { return m_fileItems; } void FileMetaDataProvider::setReadOnly(bool readOnly) { m_readOnly = readOnly; } bool FileMetaDataProvider::isReadOnly() const { return m_readOnly; } QVariantMap FileMetaDataProvider::data() const { return m_data; } int FileMetaDataProvider::subDirectoriesCount(const QString& path) { #ifdef Q_OS_WIN QDir dir(path); return dir.entryList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::System).count(); #else // Taken from kdelibs/kio/kio/kdirmodel.cpp // Copyright (C) 2006 David Faure int count = -1; DIR* dir = ::opendir(QFile::encodeName(path)); if (dir) { count = 0; struct dirent *dirEntry = 0; while ((dirEntry = ::readdir(dir))) { // krazy:exclude=syscalls if (dirEntry->d_name[0] == '.') { if (dirEntry->d_name[1] == '\0') { // Skip "." continue; } if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') { // Skip ".." continue; } } ++count; } ::closedir(dir); } return count; #endif } bool FileMetaDataProvider::realTimeIndexing() { return m_realTimeIndexing; } diff --git a/src/filemetadatawidget.cpp b/src/filemetadatawidget.cpp index 320ae6f..4c15d95 100644 --- a/src/filemetadatawidget.cpp +++ b/src/filemetadatawidget.cpp @@ -1,314 +1,323 @@ /* Copyright (C) 2012-2013 Vishesh Handa Adapated from KFileMetadataWidget Copyright (C) 2008 by Sebastian Trueg Copyright (C) 2009-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 "filemetadatawidget.h" #include "metadatafilter.h" #include "widgetfactory.h" #include "filemetadataprovider.h" #include #include #include #include #include #include #include #include #include #include #include using namespace Baloo; class Baloo::FileMetaDataWidget::Private { public: struct Row { QLabel* label; QWidget* value; }; Private(FileMetaDataWidget* parent); ~Private(); /** * Parses the configuration file "kmetainformationrc" and * updates the visibility of all rows that got their data * from KFileItem. */ void updateFileItemRowsVisibility(); void deleteRows(); void slotLoadingFinished(); void slotLinkActivated(const QString& link); void slotDataChangeStarted(); void slotDataChangeFinished(); QStringList sortedKeys(const QVariantMap& data) const; QLabel* createLabel(const QString &key, const QString& itemLabel, FileMetaDataWidget* parent); QList m_rows; FileMetaDataProvider* m_provider; QGridLayout* m_gridLayout; MetadataFilter* m_filter; WidgetFactory* m_widgetFactory; private: FileMetaDataWidget* const q; }; FileMetaDataWidget::Private::Private(FileMetaDataWidget* parent) : m_rows() , m_provider(0) , m_gridLayout(0) , q(parent) { m_filter = new MetadataFilter(q); m_widgetFactory = new WidgetFactory(q); connect(m_widgetFactory, &WidgetFactory::urlActivated, q, &FileMetaDataWidget::urlActivated); // TODO: If KFileMetaDataProvider might get a public class in future KDE releases, // the following code should be moved into KFileMetaDataWidget::setModel(): m_provider = new FileMetaDataProvider(q); connect(m_provider, SIGNAL(loadingFinished()), q, SLOT(slotLoadingFinished())); } FileMetaDataWidget::Private::~Private() { } void FileMetaDataWidget::Private::deleteRows() { foreach (const Row& row, m_rows) { delete row.label; row.value->deleteLater(); } m_rows.clear(); } QLabel* FileMetaDataWidget::Private::createLabel(const QString &key, const QString& itemLabel, FileMetaDataWidget* parent) { QLabel* label = new QLabel(itemLabel + QLatin1Char(':'), parent); label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); label->setForegroundRole(parent->foregroundRole()); label->setFont(parent->font()); label->setWordWrap(true); label->setAlignment(Qt::AlignTop | Qt::AlignRight); label->setObjectName(QStringLiteral("L_%1").arg(key)); return label; } void FileMetaDataWidget::Private::slotLoadingFinished() { deleteRows(); if (m_gridLayout == 0) { m_gridLayout = new QGridLayout(q); m_gridLayout->setMargin(0); m_gridLayout->setSpacing(q->fontMetrics().height() / 4); } QVariantMap data = m_filter->filter( m_provider->data() ); m_widgetFactory->setNoLinks( m_provider->realTimeIndexing() ); int rowIndex = 0; // Iterate through all remaining items. // Embed the label and the value as new row in the widget const QStringList keys = sortedKeys(data); for (const auto key: keys) { Row row; const int spacerWidth = QFontMetrics(q->font()).size(Qt::TextSingleLine, " ").width(); m_gridLayout->addItem(new QSpacerItem(spacerWidth, 1), rowIndex, 1); row.label = createLabel(key, m_provider->label(key), q); m_gridLayout->addWidget(row.label, rowIndex, 0, Qt::AlignRight); row.value = m_widgetFactory->createWidget(key, data[key], q); m_gridLayout->addWidget(row.value, rowIndex, 2, Qt::AlignLeft); // Remember the label and value-widget as row m_rows.append(row); ++rowIndex; } q->updateGeometry(); emit q->metaDataRequestFinished(m_provider->items()); } void FileMetaDataWidget::Private::slotLinkActivated(const QString& link) { const QUrl url = QUrl::fromUserInput(link); if (url.isValid()) { emit q->urlActivated(url); } } void FileMetaDataWidget::Private::slotDataChangeStarted() { q->setEnabled(false); } void FileMetaDataWidget::Private::slotDataChangeFinished() { q->setEnabled(true); } QStringList FileMetaDataWidget::Private::sortedKeys(const QVariantMap& data) const { // Create a map, where the translated label prefixed with the // sort priority acts as key. The data of each entry is the URI // of the data. By this the all URIs are sorted by the sort priority // and sub sorted by the translated labels. QMap map; QVariantMap::const_iterator hashIt = data.constBegin(); while (hashIt != data.constEnd()) { const QString propName = hashIt.key(); QString key = m_provider->group(propName); key += m_provider->label(propName); map.insertMulti(key, propName); ++hashIt; } // Apply the URIs from the map to the list that will get returned. // The list will then be alphabetically ordered by the translated labels of the URIs. QStringList list; QMap::const_iterator mapIt = map.constBegin(); while (mapIt != map.constEnd()) { list.append(mapIt.value()); ++mapIt; } return list; } FileMetaDataWidget::FileMetaDataWidget(QWidget* parent) : QWidget(parent) , d(new Private(this)) { } FileMetaDataWidget::~FileMetaDataWidget() { delete d; } void FileMetaDataWidget::setItems(const KFileItemList& items) { KFileItemList localItemsList; QStringList list; bool xAttrSuppored = true; foreach(const KFileItem& item, items) { QUrl url = item.targetUrl(); if (url.isLocalFile()) { localItemsList << item; QString path = url.toLocalFile(); list << path; KFileMetaData::UserMetaData md(path); xAttrSuppored &= md.isSupported(); } } setReadOnly(!xAttrSuppored); d->m_provider->setItems(localItemsList); d->m_widgetFactory->setItems(list); setReadOnly(!xAttrSuppored); } KFileItemList FileMetaDataWidget::items() const { return d->m_provider->items(); } void FileMetaDataWidget::setReadOnly(bool readOnly) { d->m_provider->setReadOnly(readOnly); d->m_widgetFactory->setReadOnly(readOnly); } bool FileMetaDataWidget::isReadOnly() const { return d->m_provider->isReadOnly(); } +void FileMetaDataWidget::setDateFormat(const DateFormats format) +{ + d->m_widgetFactory->setDateFormat(format); +} + +DateFormats FileMetaDataWidget::dateFormat() const +{ + return d->m_widgetFactory->dateFormat(); +} QSize FileMetaDataWidget::sizeHint() const { if (d->m_gridLayout == 0) { return QWidget::sizeHint(); } // Calculate the required width for the labels and values int leftWidthMax = 0; int rightWidthMax = 0; int rightWidthAverage = 0; foreach (const Private::Row& row, d->m_rows) { const QWidget* valueWidget = row.value; const int rightWidth = valueWidget->sizeHint().width(); rightWidthAverage += rightWidth; if (rightWidth > rightWidthMax) { rightWidthMax = rightWidth; } const int leftWidth = row.label->sizeHint().width(); if (leftWidth > leftWidthMax) { leftWidthMax = leftWidth; } } // Some value widgets might return a very huge width for the size hint. // Limit the maximum width to the double width of the overall average // to assure a less messed layout. if (d->m_rows.count() > 1) { rightWidthAverage /= d->m_rows.count(); if (rightWidthMax > rightWidthAverage * 2) { rightWidthMax = rightWidthAverage * 2; } } // Based on the available width calculate the required height int height = d->m_gridLayout->margin() * 2 + d->m_gridLayout->spacing() * (d->m_rows.count() - 1); foreach (const Private::Row& row, d->m_rows) { const QWidget* valueWidget = row.value; const int rowHeight = qMax(row.label->heightForWidth(leftWidthMax), valueWidget->heightForWidth(rightWidthMax)); height += rowHeight; } const int width = d->m_gridLayout->margin() * 2 + leftWidthMax + d->m_gridLayout->spacing() + rightWidthMax; return QSize(width, height); } #include "moc_filemetadatawidget.cpp" diff --git a/src/filemetadatawidget.h b/src/filemetadatawidget.h index b60efd0..f3c8307 100644 --- a/src/filemetadatawidget.h +++ b/src/filemetadatawidget.h @@ -1,89 +1,108 @@ /* Copyright (C) 2012-2013 Vishesh Handa Adapated from KFileMetadataWidget Copyright (C) 2008 by Sebastian Trueg Copyright (C) 2009-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 */ #ifndef _BALOO_FILE_METADATAWIDGET_H #define _BALOO_FILE_METADATAWIDGET_H #include #include #include +#include #include "widgets_export.h" namespace Baloo { +/** + * Modify format of date display + */ +enum class DateFormats { + LongFormat = QLocale::LongFormat, ///< @see QLocale::LongFormat + ShortFormat = QLocale::ShortFormat ///< @see QLocale::ShortFormat +}; + class BALOO_WIDGETS_EXPORT FileMetaDataWidget : public QWidget { Q_OBJECT Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + Q_PROPERTY(DateFormats dateFormat READ dateFormat WRITE setDateFormat) public: explicit FileMetaDataWidget(QWidget* parent = 0); virtual ~FileMetaDataWidget(); /** * Sets the items for which the meta data should be shown. * The signal metaDataRequestFinished() will be emitted, * as soon as the meta data for the items has been received. */ void setItems(const KFileItemList& items); KFileItemList items() const; /** * If set to true, data such as the comment, tag or rating cannot be * changed by the user. Per default read-only is disabled. */ void setReadOnly(bool readOnly); bool isReadOnly() const; + /** + * Set date display format. + * Per Default format is Long = @see QLocale::LongFormat + */ + void setDateFormat(const DateFormats format); + DateFormats dateFormat() const; + /** @see QWidget::sizeHint() */ virtual QSize sizeHint() const Q_DECL_OVERRIDE; Q_SIGNALS: /** * Is emitted, if a meta data represents an URL that has * been clicked by the user. */ void urlActivated(const QUrl& url); /** * Is emitted after the meta data has been received for the items * set by KFileMetaDataWidget::setItems(). * @since 4.6 */ void metaDataRequestFinished(const KFileItemList& items); private: class Private; Private* d; Q_PRIVATE_SLOT(d, void slotLoadingFinished()) Q_PRIVATE_SLOT(d, void slotLinkActivated(QString)) Q_PRIVATE_SLOT(d, void slotDataChangeStarted()) Q_PRIVATE_SLOT(d, void slotDataChangeFinished()) }; } +Q_DECLARE_METATYPE(Baloo::DateFormats) + #endif // _BALOO_FILE_METADATAWIDGET_H diff --git a/src/widgetfactory.cpp b/src/widgetfactory.cpp index 533a085..d38d906 100644 --- a/src/widgetfactory.cpp +++ b/src/widgetfactory.cpp @@ -1,331 +1,341 @@ /* 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 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::List: { QStringList list; for (const QVariant& var : value.toList()) { list << toString(var); } return list.join(", "); } default: return value.toString(); } } QWidget* WidgetFactory::createWidget(const QString& prop, const QVariant& value, QWidget* parent) { QWidget* widget = 0; 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")) { if (m_noLinks) { valueString = value.toString(); } else { valueString = QStringLiteral("%1").arg(value.toString()); } } else { // Check if Date/DateTime - QDateTime dt = QDateTime::fromString(value.toString(), Qt::ISODate); if (dt.isValid()) { QTime time = dt.time(); if (!time.hour() && !time.minute() && !time.second()){ - valueString = form.formatRelativeDate(dt.date(), QLocale::LongFormat); + valueString = form.formatRelativeDate(dt.date(), m_dateFormat); } else { - valueString = form.formatRelativeDateTime(dt, QLocale::LongFormat); + 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 = 0); virtual QSize sizeHint() const Q_DECL_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); +} + diff --git a/src/widgetfactory.h b/src/widgetfactory.h index 107a240..8363379 100644 --- a/src/widgetfactory.h +++ b/src/widgetfactory.h @@ -1,81 +1,87 @@ /* Copyright (C) 2012 Vishesh Handa 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 */ #ifndef WIDGETFACTORY_H #define WIDGETFACTORY_H +#include "filemetadatawidget.h" + #include #include class KJob; class QUrl; class KCommentWidget; class KRatingWidget; namespace Baloo { class Tag; class TagWidget; class WidgetFactory : public QObject { Q_OBJECT public: explicit WidgetFactory(QObject* parent = 0); virtual ~WidgetFactory(); void setItems(const QStringList& items); void setReadOnly(bool value); void setNoLinks(bool value); + void setDateFormat(const DateFormats format); + DateFormats dateFormat() const; + QWidget* createWidget(const QString& prop, const QVariant& value, QWidget* parent); Q_SIGNALS: void urlActivated(const QUrl& url); void dataChangeStarted(); void dataChangeFinished(); private Q_SLOTS: void slotTagsChanged(const QStringList& tags); void slotCommentChanged(const QString& comment); void slotRatingChanged(uint rating); void slotTagClicked(const QString& tag); void slotLinkActivated(const QString& url); private: QWidget* createRatingWidget(int rating, QWidget* parent); QWidget* createTagWidget(const QStringList& tags, QWidget* parent); QWidget* createCommentWidget(const QString& comment, QWidget* parent); QWidget* createValueWidget(const QString& value, QWidget* parent); TagWidget* m_tagWidget; KRatingWidget* m_ratingWidget; KCommentWidget* m_commentWidget; QStringList m_items; QStringList m_prevTags; bool m_readOnly; bool m_noLinks; + QLocale::FormatType m_dateFormat; }; } #endif // WIDGETFACTORY_H