From a22f9f73aa16447870c5d548596dd93b05038222 Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Sun, 3 May 2015 16:49:26 +0000 Subject: [PATCH] kio: bring back the metadata helpers (without Nepomuk) --- kio/kfile/kcommentwidget_p.h | 69 ++++ .../kfilemetadataconfigurationwidget.cpp | 21 +- kio/kfile/kfilemetadataprovider.cpp | 381 ++++++++++++++++++ kio/kfile/kfilemetadataprovider_p.h | 127 ++++++ kio/kfile/kfilemetadatareader.cpp | 146 +++++++ kio/kfile/kfilemetadatareader_p.h | 90 +++++ kio/kfile/kfilemetadatareaderprocess.cpp | 153 +++++++ kio/kfile/kfilemetadatawidget.cpp | 53 ++- kio/kfile/kfilemetadatawidget.h | 16 +- 9 files changed, 1031 insertions(+), 25 deletions(-) create mode 100644 kio/kfile/kcommentwidget_p.h create mode 100644 kio/kfile/kfilemetadataprovider.cpp create mode 100644 kio/kfile/kfilemetadataprovider_p.h create mode 100644 kio/kfile/kfilemetadatareader.cpp create mode 100644 kio/kfile/kfilemetadatareader_p.h create mode 100644 kio/kfile/kfilemetadatareaderprocess.cpp diff --git a/kio/kfile/kcommentwidget_p.h b/kio/kfile/kcommentwidget_p.h new file mode 100644 index 00000000..95488ea0 --- /dev/null +++ b/kio/kfile/kcommentwidget_p.h @@ -0,0 +1,69 @@ +/***************************************************************************** + * Copyright (C) 2008 by Sebastian Trueg * + * Copyright (C) 2009 by Peter Penz * + * * + * 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. * + *****************************************************************************/ + +#ifndef KCOMMENT_WIDGET +#define KCOMMENT_WIDGET + +#include +#include + +class QLabel; + +/** + * @brief Allows to edit and show a comment as part of KMetaDataWidget. + */ +class KCommentWidget : public QWidget +{ + Q_OBJECT + +public: + explicit KCommentWidget(QWidget* parent = 0); + virtual ~KCommentWidget(); + + void setText(const QString& comment); + QString text() const; + + /** + * If set to true, the comment cannot be changed by the user. + * Per default read-only is disabled. + */ + // TODO: provide common interface class for metadatawidgets + void setReadOnly(bool readOnly); + bool isReadOnly() const; + + virtual QSize sizeHint() const; + +signals: + void commentChanged(const QString& comment); + +protected: + virtual bool event(QEvent* event); + +private slots: + void slotLinkActivated(const QString& link); + +private: + bool m_readOnly; + QLabel* m_label; + QLabel* m_sizeHintHelper; // see comment in KCommentWidget::sizeHint() + QString m_comment; +}; + +#endif diff --git a/kio/kfile/kfilemetadataconfigurationwidget.cpp b/kio/kfile/kfilemetadataconfigurationwidget.cpp index 6b4d9c1a..84262689 100644 --- a/kio/kfile/kfilemetadataconfigurationwidget.cpp +++ b/kio/kfile/kfilemetadataconfigurationwidget.cpp @@ -25,6 +25,7 @@ #include #include "knfotranslator_p.h" #include +#include "kfilemetadataprovider_p.h" #include @@ -51,6 +52,7 @@ public: int m_visibleDataTypes; KFileItemList m_fileItems; + KFileMetaDataProvider* m_provider; QListWidget* m_metaDataList; private: @@ -60,6 +62,7 @@ private: KFileMetaDataConfigurationWidget::Private::Private(KFileMetaDataConfigurationWidget* parent) : m_visibleDataTypes(0), m_fileItems(), + m_provider(0), m_metaDataList(0), q(parent) { @@ -70,6 +73,7 @@ KFileMetaDataConfigurationWidget::Private::Private(KFileMetaDataConfigurationWid QVBoxLayout* layout = new QVBoxLayout(q); layout->addWidget(m_metaDataList); + m_provider = new KFileMetaDataProvider(q); } KFileMetaDataConfigurationWidget::Private::~Private() @@ -78,6 +82,9 @@ KFileMetaDataConfigurationWidget::Private::~Private() void KFileMetaDataConfigurationWidget::Private::loadMetaData() { + m_provider->setItems(m_fileItems); + connect(m_provider, SIGNAL(loadingFinished()), + q, SLOT(slotLoadingFinished())); } void KFileMetaDataConfigurationWidget::Private::addItem(const KUrl& uri) @@ -110,7 +117,9 @@ void KFileMetaDataConfigurationWidget::Private::addItem(const KUrl& uri) KConfig config("kmetainformationrc", KConfig::NoGlobals); KConfigGroup settings = config.group("Show"); - const QString label = KNfoTranslator::instance().translation(uri); + const QString label = (m_provider == 0) + ? KNfoTranslator::instance().translation(uri) + : m_provider->label(uri); QListWidgetItem* item = new QListWidgetItem(label, m_metaDataList); item->setData(Qt::UserRole, key); @@ -120,6 +129,16 @@ void KFileMetaDataConfigurationWidget::Private::addItem(const KUrl& uri) void KFileMetaDataConfigurationWidget::Private::slotLoadingFinished() { + // Get all meta information labels that are available for + // the currently shown file item and add them to the list. + Q_ASSERT(m_provider != 0); + + const QHash data = m_provider->data(); + QHash::const_iterator it = data.constBegin(); + while (it != data.constEnd()) { + addItem(it.key()); + ++it; + } } KFileMetaDataConfigurationWidget::KFileMetaDataConfigurationWidget(QWidget* parent) : diff --git a/kio/kfile/kfilemetadataprovider.cpp b/kio/kfile/kfilemetadataprovider.cpp new file mode 100644 index 00000000..58875ad6 --- /dev/null +++ b/kio/kfile/kfilemetadataprovider.cpp @@ -0,0 +1,381 @@ +/***************************************************************************** + * 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 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 "kfilemetadataprovider_p.h" + +#include +#include +#include "knfotranslator_p.h" +#include +#include +#include + +#include +#include + +// Required includes for subDirectoriesCount(): +#ifdef Q_WS_WIN + #include +#else + #include + #include +#endif + +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; + } +} + +// 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; +}; + +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())); +} + + + +class KFileMetaDataProvider::Private +{ + +public: + Private(KFileMetaDataProvider* parent); + ~Private(); + + void slotLoadingFinished(); + + void slotMetaDataUpdateDone(); + void slotLinkActivated(const QString& link); + + /** + * Disables the metadata widget and starts the job that + * changes the meta data asynchronously. After the job + * has been finished, the metadata widget gets enabled again. + */ + void startChangeDataJob(KJob* job); + + QList resourceList() const; + QWidget* createValueWidget(const QString& value, QWidget* parent); + + /* + * @return The number of subdirectories for the directory \a path. + */ + static int subDirectoriesCount(const QString &path); + + QList m_fileItems; + + QHash m_data; + + QList m_metaDataReaders; + KFileMetaDataReader* m_latestMetaDataReader; + +private: + KFileMetaDataProvider* const q; +}; + +KFileMetaDataProvider::Private::Private(KFileMetaDataProvider* parent) : + m_fileItems(), + m_data(), + m_metaDataReaders(), + m_latestMetaDataReader(0), + q(parent) +{ +} + +KFileMetaDataProvider::Private::~Private() +{ + qDeleteAll(m_metaDataReaders); +} + +void KFileMetaDataProvider::Private::slotLoadingFinished() +{ + KFileMetaDataReader* finishedMetaDataReader = qobject_cast(q->sender()); + // The process that has emitted the finished() signal + // will get deleted and removed from m_metaDataReaders. + for (int i = 0; i < m_metaDataReaders.count(); ++i) { + KFileMetaDataReader* metaDataReader = m_metaDataReaders[i]; + if (metaDataReader == finishedMetaDataReader) { + m_metaDataReaders.removeAt(i); + if (metaDataReader != m_latestMetaDataReader) { + // Ignore data of older processs, as the data got + // obsolete by m_latestMetaDataReader. + metaDataReader->deleteLater(); + return; + } + } + } + + m_data = m_latestMetaDataReader->metaData(); + m_latestMetaDataReader->deleteLater(); + + if (m_fileItems.count() == 1) { + // TODO: Handle case if remote URLs are used properly. isDir() does + // not work, the modification date needs also to be adjusted... + const KFileItem& item = m_fileItems.first(); + + if (item.isDir()) { + const int count = subDirectoriesCount(item.url().pathOrUrl()); + if (count == -1) { + m_data.insert(KUrl("kfileitem#size"), QString("Unknown")); + } else { + const QString itemCountString = i18ncp("@item:intable", "%1 item", "%1 items", count); + m_data.insert(KUrl("kfileitem#size"), itemCountString); + } + } else { + m_data.insert(KUrl("kfileitem#size"), KIO::convertSize(item.size())); + } + m_data.insert(KUrl("kfileitem#type"), item.mimeComment()); + m_data.insert(KUrl("kfileitem#modified"), KGlobal::locale()->formatDateTime(item.time(KFileItem::ModificationTime), KLocale::FancyLongDate)); + m_data.insert(KUrl("kfileitem#owner"), item.user()); + m_data.insert(KUrl("kfileitem#permissions"), item.permissionsString()); + } else if (m_fileItems.count() > 1) { + // Calculate the size of all items + quint64 totalSize = 0; + foreach (const KFileItem& item, m_fileItems) { + if (!item.isDir() && !item.isLink()) { + totalSize += item.size(); + } + } + m_data.insert(KUrl("kfileitem#totalSize"), KIO::convertSize(totalSize)); + } + + emit q->loadingFinished(); +} + +void KFileMetaDataProvider::Private::slotLinkActivated(const QString& link) +{ + emit q->urlActivated(KUrl(link)); +} + +void KFileMetaDataProvider::Private::startChangeDataJob(KJob* job) +{ + connect(job, SIGNAL(result(KJob*)), + q, SIGNAL(dataChangeFinished())); + emit q->dataChangeStarted(); + job->start(); +} + +QList KFileMetaDataProvider::Private::resourceList() const +{ + QList list; + foreach (const KFileItem& item, m_fileItems) { + const KUrl url = item.url(); + if(url.isValid()) + list.append(url.prettyUrl()); + } + return list; +} + +QWidget* KFileMetaDataProvider::Private::createValueWidget(const QString& value, QWidget* parent) +{ + ValueWidget* valueWidget = new ValueWidget(parent); + valueWidget->setWordWrap(true); + valueWidget->setAlignment(Qt::AlignTop | Qt::AlignLeft); + valueWidget->setText(plainText(value)); + connect(valueWidget, SIGNAL(linkActivated(QString)), q, SLOT(slotLinkActivated(QString))); + return valueWidget; +} + +KFileMetaDataProvider::KFileMetaDataProvider(QObject* parent) : + QObject(parent), + d(new Private(this)) +{ +} + +KFileMetaDataProvider::~KFileMetaDataProvider() +{ + delete d; +} + +void KFileMetaDataProvider::setItems(const KFileItemList& items) +{ + d->m_fileItems = items; + + if (items.isEmpty()) { + return; + } + Q_PRIVATE_SLOT(d,void slotDataChangeStarted()) + Q_PRIVATE_SLOT(d,void slotDataChangeFinished()) + QList urls; + foreach (const KFileItem& item, items) { + const KUrl url = item.url(); + if (url.isValid()) { + urls.append(url); + } + } + + d->m_latestMetaDataReader = new KFileMetaDataReader(urls); + d->m_latestMetaDataReader->setReadContextData(false); + connect(d->m_latestMetaDataReader, SIGNAL(finished()), this, SLOT(slotLoadingFinished())); + d->m_metaDataReaders.append(d->m_latestMetaDataReader); + d->m_latestMetaDataReader->start(); +} + +QString KFileMetaDataProvider::label(const KUrl& metaDataUri) const +{ + struct TranslationItem { + const char* const key; + const char* const context; + const char* const value; + }; + + static const TranslationItem translations[] = { + { "kfileitem#modified", I18N_NOOP2_NOSTRIP("@label", "Modified") }, + { "kfileitem#owner", I18N_NOOP2_NOSTRIP("@label", "Owner") }, + { "kfileitem#permissions", I18N_NOOP2_NOSTRIP("@label", "Permissions") }, + { "kfileitem#size", I18N_NOOP2_NOSTRIP("@label", "Size") }, + { "kfileitem#totalSize", I18N_NOOP2_NOSTRIP("@label", "Total Size") }, + { "kfileitem#type", I18N_NOOP2_NOSTRIP("@label", "Type") }, + { 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(metaDataUri.url()); + if (value.isEmpty()) { + value = KNfoTranslator::instance().translation(metaDataUri); + } + + return value; +} + +QString KFileMetaDataProvider::group(const KUrl& metaDataUri) const +{ + QString group; // return value + + const QString uri = metaDataUri.url(); + if (uri == QLatin1String("kfileitem#type")) { + group = QLatin1String("0FileItemA"); + } else if (uri == QLatin1String("kfileitem#size")) { + group = QLatin1String("0FileItemB"); + } else if (uri == QLatin1String("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width")) { + group = QLatin1String("0SizeA"); + } else if (uri == QLatin1String("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height")) { + group = QLatin1String("0SizeB"); + } + + return group; +} + +KFileItemList KFileMetaDataProvider::items() const +{ + return d->m_fileItems; +} + +QHash KFileMetaDataProvider::data() const +{ + return d->m_data; +} + +QWidget* KFileMetaDataProvider::createValueWidget(const KUrl& metaDataUri, + const QVariant& value, + QWidget* parent) const +{ + Q_ASSERT(parent != 0); + QWidget* widget = 0; + + if (widget == 0) { + widget = d->createValueWidget(value.toString(), parent); + } + + widget->setForegroundRole(parent->foregroundRole()); + widget->setFont(parent->font()); + + return widget; +} + +int KFileMetaDataProvider::Private::subDirectoriesCount(const QString& path) +{ +#ifdef Q_WS_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 +} + +#include "moc_kfilemetadataprovider_p.cpp" diff --git a/kio/kfile/kfilemetadataprovider_p.h b/kio/kfile/kfilemetadataprovider_p.h new file mode 100644 index 00000000..d8e18d57 --- /dev/null +++ b/kio/kfile/kfilemetadataprovider_p.h @@ -0,0 +1,127 @@ +/***************************************************************************** + * 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 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. * + *****************************************************************************/ + +#ifndef KFILEMETADATAMODEL_H +#define KFILEMETADATAMODEL_H + +#include + +#include +#include +#include + +#include + +class KFileItemList; +class KProcess; +class KUrl; +class QWidget; + +/** + * @brief Provides the data for the KMetaDataWidget. + * + * The default implementation provides all meta data + * that are available due to Strigi and Nepomuk. If custom + * meta data should be added, the method KFileMetaDataProvider::loadData() + * must be overwritten. + * + * @see KFileMetaDataWidget + */ +class KFileMetaDataProvider : public QObject +{ + Q_OBJECT + +public: + explicit KFileMetaDataProvider(QObject* parent = 0); + virtual ~KFileMetaDataProvider(); + + /** + * Sets the items, where the meta data should be + * requested. The loading of the meta data is done + * asynchronously. The signal loadingFinished() is + * emitted, as soon as the loading has been finished. + * The meta data can be retrieved by + * KFileMetaDataProvider::data() afterwards. The label for + * each item can be retrieved by KFileMetaDataProvider::label(). + */ + void setItems(const KFileItemList& items); + KFileItemList items() const; + + /** + * @return Translated string for the label of the meta data represented + * by \p metaDataUri. If no custom translation is provided, the + * base implementation must be invoked. + */ + virtual QString label(const KUrl& metaDataUri) const; + + /** + * Meta data items are sorted alphabetically by their translated + * label per default. However it is possible to provide an internal + * prefix to the label, so that specific items are grouped together. + * For example it makes sense that the meta data for 'width' and 'height' + * of an image are shown below each other. By adding a common prefix, + * a grouping is done. + * @return Returns the name of the group the meta data indicated + * by \p metaDataUri belongs to. Per default an empty string + * is returned. + */ + virtual QString group(const KUrl& metaDataUri) const; + + /** + * @return Meta data for the items that have been set by + * KFileMetaDataProvider::setItems(). The method should + * be invoked after the signal loadingFinished() has + * been received (otherwise no data will be returned). + */ + virtual QHash data() const; + + /** + * @return Factory method that returns a widget that should be used + * to show the meta data represented by \p metaDataUri. If + * no custom value widget is used for the given URI, the base + * implementation must be invoked. Per default an instance + * of QLabel will be returned. + */ + virtual QWidget* createValueWidget(const KUrl& metaDataUri, + const QVariant& value, + QWidget* parent) const; + +Q_SIGNALS: + /** + * Is emitted after the loading triggered by KFileMetaDataProvider::setItems() + * has been finished. + */ + void loadingFinished(); + + void urlActivated(const KUrl& url); + + void dataChangeStarted(); + void dataChangeFinished(); + +private: + class Private; + Private* const d; + + Q_PRIVATE_SLOT(d, void slotLoadingFinished()) + Q_PRIVATE_SLOT(d, void slotLinkActivated(const QString&)) + + friend class KLoadMetaDataThread; // invokes KMetaDataObject::loadData() +}; + +#endif diff --git a/kio/kfile/kfilemetadatareader.cpp b/kio/kfile/kfilemetadatareader.cpp new file mode 100644 index 00000000..fd761444 --- /dev/null +++ b/kio/kfile/kfilemetadatareader.cpp @@ -0,0 +1,146 @@ +/***************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * 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 "kfilemetadatareader_p.h" + +#include +#include + +class KFileMetaDataReader::Private +{ +public: + Private(KFileMetaDataReader* parent); + ~Private(); + + void slotLoadingFinished(int exitCode, QProcess::ExitStatus exitStatus); + + bool m_readContextData; + KProcess* m_process; + QHash m_metaData; + +private: + KFileMetaDataReader* const q; +}; + +KFileMetaDataReader::Private::Private(KFileMetaDataReader* parent) : + m_readContextData(true), + m_process(new KProcess()), + m_metaData(), + q(parent) +{ +} + +KFileMetaDataReader::Private::~Private() +{ + delete m_process; +} + +void KFileMetaDataReader::Private::slotLoadingFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitCode); + Q_UNUSED(exitStatus); + + QDataStream in(QByteArray::fromBase64(m_process->readLine())); + + KUrl key; + QVariant value; + while (!in.atEnd()) { + in >> key; + + // Unlike QVariant no streaming operators are implemented for QVariant. + // So it is required to manually decode the variant from the stream. See + // function sendMetaData() in kfilemetadatareaderprocess.cpp for the encoding + // counterpart. + int streamType; + in >> streamType; + + switch (streamType) { + case 0: { + QStringList stringList; + in >> stringList; + value = stringList; + break; + } + case 1: { + QString resource; + in >> resource; + value = resource; + break; + } + + default: + QVariant variant; + in >> variant; + value = QVariant(variant); + } + + m_metaData.insert(key, value); + } + + emit q->finished(); +} + +KFileMetaDataReader::KFileMetaDataReader(const QList& urls, QObject* parent) : + QObject(parent), + d(new Private(this)) +{ + const QString fileMetaDataReaderExe = KStandardDirs::findExe(QLatin1String("kfilemetadatareader")); + (*d->m_process) << fileMetaDataReaderExe; + + foreach (const KUrl& url, urls) { + (*d->m_process) << url.url(); + } + + d->m_process->setOutputChannelMode(KProcess::OnlyStdoutChannel); + d->m_process->setNextOpenMode(QIODevice::ReadOnly); + connect(d->m_process, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(slotLoadingFinished(int,QProcess::ExitStatus))); +} + +KFileMetaDataReader::~KFileMetaDataReader() +{ + delete d; +} + +void KFileMetaDataReader::setReadContextData(bool read) +{ + d->m_readContextData = read; +} + +bool KFileMetaDataReader::readContextData() const +{ + return d->m_readContextData; +} + +void KFileMetaDataReader::start() +{ + if (d->m_process->state() == QProcess::NotRunning) { + if (!d->m_readContextData) { + (*d->m_process) << "--file"; + } + d->m_process->start(); + } +} + +QHash KFileMetaDataReader::metaData() const +{ + return d->m_metaData; +} + +#include "moc_kfilemetadatareader_p.cpp" diff --git a/kio/kfile/kfilemetadatareader_p.h b/kio/kfile/kfilemetadatareader_p.h new file mode 100644 index 00000000..4402dbed --- /dev/null +++ b/kio/kfile/kfilemetadatareader_p.h @@ -0,0 +1,90 @@ +/***************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * 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. * + *****************************************************************************/ + +#ifndef KFILEMETADATAREADER_H +#define KFILEMETADATAREADER_H + +#include + +#include +#include +#include +#include +#include + +/** + * @brief Provides metadata extracted from files. + * + * The reading of the metadata is done asynchronously in a process. + * This assures that the caller won't get blocked and also prevents + * that the caller crashes in case if a metadata-analyzer plugin is instable. + * + * @since 4.7 + * @internal + */ +class KFileMetaDataReader : public QObject +{ + Q_OBJECT + +public: + /** + * @param urls List of files where the metadata should be extracted from. + * @param parent Parent object. + */ + explicit KFileMetaDataReader(const QList& urls, QObject* parent = 0); + virtual ~KFileMetaDataReader(); + + /** + * If \p read is set to true also metadata that is persisted outside the + * files itself (like e.g. rating, comments or tags) are read. Per + * default the reading of context data is enabled. Pass false if only the metadata + * persisted inside the file should be read. + */ + void setReadContextData(bool read); + bool readContextData() const; + + /** + * Starts the reading of the metadata inside a custom process. + * The signal finished() will get emitted if the reading has been finished. + * Use metaData() to access the read metadata. + */ + void start(); + + /** + * @return The read metadata of the given files. The method provides valid values + * after the signal finished() has been emitted. If it is invoked before + * an empty hash-table will be returned. + */ + QHash metaData() const; + +Q_SIGNALS: + /** + * Is emitted if the reading of the metadata inside a custom process has been finished. + * The method metaData() can be used afterwards to access the metadata. + */ + void finished(); + +private: + class Private; + Private* d; + + Q_PRIVATE_SLOT(d, void slotLoadingFinished(int, QProcess::ExitStatus)) +}; + +#endif diff --git a/kio/kfile/kfilemetadatareaderprocess.cpp b/kio/kfile/kfilemetadatareaderprocess.cpp new file mode 100644 index 00000000..73e1a151 --- /dev/null +++ b/kio/kfile/kfilemetadatareaderprocess.cpp @@ -0,0 +1,153 @@ +/***************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; + +class KFileMetaDataReaderApplication : public QCoreApplication +{ + Q_OBJECT + +public: + KFileMetaDataReaderApplication(int& argc, char** argv); + +private Q_SLOTS: + void readAndSendMetaData(); + +private: + void sendMetaData(const QHash& data); + QHash readFileMetaData(const QList& urls) const; + QHash readFileAndContextMetaData(const QList& urls) const; +}; + + + +KFileMetaDataReaderApplication::KFileMetaDataReaderApplication(int& argc, char** argv) : + QCoreApplication(argc, argv) +{ + QTimer::singleShot(0, this, SLOT(readAndSendMetaData())); +} + +void KFileMetaDataReaderApplication::readAndSendMetaData() +{ + const KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + KUrl::List urls; + for (int i = 0; i < args->count(); ++i) { + urls.append(KUrl(args->arg(i))); + } + + QHash metaData; + if (args->isSet("file")) { + metaData = readFileMetaData(urls); + } else { + metaData = readFileAndContextMetaData(urls); + } + + sendMetaData(metaData); + + quit(); +} + +void KFileMetaDataReaderApplication::sendMetaData(const QHash& data) +{ + QByteArray byteArray; + QDataStream out(&byteArray, QIODevice::WriteOnly); + + QHashIterator it(data); + while (it.hasNext()) { + it.next(); + + out << it.key(); + + // Unlike QVariant no streaming operators are implemented for QVariant. + // So it is required to manually encode the variant for the stream. + // The decoding counterpart is located in KFileMetaDataReader. + const QVariant& variant = it.value(); + out << 2 << variant; + } + + cout << byteArray.toBase64().constData(); +} + +QHash KFileMetaDataReaderApplication::readFileMetaData(const QList& urls) const +{ + QHash data; + + // Currently only the meta-data of one file is supported. + // It might be an option to read all meta-data and show + // ranges for each key. + if (urls.count() == 1) { + const QString path = urls.first().toLocalFile(); + KFileMetaInfo metaInfo(path, QString(), KFileMetaInfo::Fastest); + const QHash metaInfoItems = metaInfo.items(); + foreach (const KFileMetaInfoItem& metaInfoItem, metaInfoItems) { + const QString uriString = metaInfoItem.name(); + const QVariant value(metaInfoItem.value()); + // FIXME: Nepomuk::Utils::formatPropertyValue + data.insert(uriString, value); + } + } + + return data; +} + +QHash KFileMetaDataReaderApplication::readFileAndContextMetaData(const QList& urls) const +{ + return readFileMetaData(urls); +} + +int main(int argc, char *argv[]) +{ + KAboutData aboutData("kfilemetadatareader", "kio4", ki18n("KFileMetaDataReader"), + "1.0", + ki18n("KFileMetaDataReader can be used to read metadata from a file"), + KAboutData::License_GPL, + ki18n("(C) 2011, Peter Penz")); + aboutData.addAuthor(ki18n("Peter Penz"), ki18n("Current maintainer"), "peter.penz19@gmail.com"); + KComponentData compData(&aboutData); + + KCmdLineArgs::init(argc, argv, &aboutData); + + KCmdLineOptions options; + options.add("file", ki18n("Only the meta data that is part of the file is read")); + options.add("+[arg]", ki18n("List of URLs where the meta-data should be read from")); + + KCmdLineArgs::addCmdLineOptions(options); + + KFileMetaDataReaderApplication app(argc, argv); + return app.exec(); +} + +#include "kfilemetadatareaderprocess.moc" diff --git a/kio/kfile/kfilemetadatawidget.cpp b/kio/kfile/kfilemetadatawidget.cpp index 01156157..9fef52ec 100644 --- a/kio/kfile/kfilemetadatawidget.cpp +++ b/kio/kfile/kfilemetadatawidget.cpp @@ -24,6 +24,7 @@ #include #include #include +#include "kfilemetadataprovider_p.h" #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include @@ -67,8 +69,10 @@ public: void slotDataChangeStarted(); void slotDataChangeFinished(); + QList sortedKeys(const QHash& data) const; QList m_rows; + KFileMetaDataProvider* m_provider; QGridLayout* m_gridLayout; private: @@ -77,11 +81,17 @@ private: KFileMetaDataWidget::Private::Private(KFileMetaDataWidget* parent) : m_rows(), + m_provider(0), m_gridLayout(0), q(parent) { initMetaInfoSettings(); + // TODO: If KFileMetaDataProvider might get a public class in future KDE releases, + // the following code should be moved into KFileMetaDataWidget::setModel(): + m_provider = new KFileMetaDataProvider(q); + connect(m_provider, SIGNAL(loadingFinished()), q, SLOT(slotLoadingFinished())); + connect(m_provider, SIGNAL(urlActivated(KUrl)), q, SIGNAL(urlActivated(KUrl))); } KFileMetaDataWidget::Private::~Private() @@ -156,8 +166,10 @@ void KFileMetaDataWidget::Private::deleteRows() void KFileMetaDataWidget::Private::slotLoadingFinished() { + deleteRows(); q->updateGeometry(); + emit q->metaDataRequestFinished(m_provider->items()); } void KFileMetaDataWidget::Private::slotLinkActivated(const QString& link) @@ -178,6 +190,35 @@ void KFileMetaDataWidget::Private::slotDataChangeFinished() q->setEnabled(true); } +QList KFileMetaDataWidget::Private::sortedKeys(const QHash& 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; + QHash::const_iterator hashIt = data.constBegin(); + while (hashIt != data.constEnd()) { + const KUrl uri = hashIt.key(); + + QString key = m_provider->group(uri); + key += m_provider->label(uri); + + map.insert(key, uri); + ++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. + QList list; + QMap::const_iterator mapIt = map.constBegin(); + while (mapIt != map.constEnd()) { + list.append(mapIt.value()); + ++mapIt; + } + + return list; +} KFileMetaDataWidget::KFileMetaDataWidget(QWidget* parent) : QWidget(parent), @@ -192,20 +233,12 @@ KFileMetaDataWidget::~KFileMetaDataWidget() void KFileMetaDataWidget::setItems(const KFileItemList& items) { + d->m_provider->setItems(items); } KFileItemList KFileMetaDataWidget::items() const { - return KFileItemList(); -} - -void KFileMetaDataWidget::setReadOnly(bool readOnly) -{ -} - -bool KFileMetaDataWidget::isReadOnly() const -{ - return true; + return d->m_provider->items(); } QSize KFileMetaDataWidget::sizeHint() const diff --git a/kio/kfile/kfilemetadatawidget.h b/kio/kfile/kfilemetadatawidget.h index 50ddce9e..ddf96c59 100644 --- a/kio/kfile/kfilemetadatawidget.h +++ b/kio/kfile/kfilemetadatawidget.h @@ -32,19 +32,14 @@ class KUrl; /** * @brief Shows the meta data of one or more file items. * - * Meta data like name, size, rating, comment, ... are - * shown as several rows containing a description and - * the meta data value. It is possible for the user - * to change specific meta data like rating, tags and - * comment. The changes are stored automatically by the - * meta data widget. + * Meta data like name, size, ... are shown as several + * rows containing a description and the meta data value. * * @since 4.5 */ class KIO_EXPORT KFileMetaDataWidget : public QWidget { Q_OBJECT - Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) public: explicit KFileMetaDataWidget(QWidget* parent = 0); @@ -58,13 +53,6 @@ public: 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; - /** @see QWidget::sizeHint() */ virtual QSize sizeHint() const;