From 565bd3bf23dfb9b139de089d3c4f8b57e124f7ee Mon Sep 17 00:00:00 2001 From: Peter Penz Date: Wed, 4 Nov 2009 19:14:39 +0000 Subject: [PATCH] Don't call QThread::wait() in the destructor - in the worst case an issue in Nepomuk will block Dolphin for several seconds. Refactored the load-meta-data-thread in a way that the meta data widget does not require to wait for a result of the thread. svn path=/trunk/KDE/kdebase/apps/; revision=1044848 --- src/CMakeLists.txt | 2 + .../information/kloadmetadatathread.cpp | 168 +++++++++++ .../information/kloadmetadatathread_p.h | 97 +++++++ src/panels/information/kmetadatawidget.cpp | 269 +++++------------- src/panels/information/kmetadatawidget.h | 3 + src/tooltips/tooltipmanager.cpp | 2 +- 6 files changed, 345 insertions(+), 196 deletions(-) create mode 100644 src/panels/information/kloadmetadatathread.cpp create mode 100644 src/panels/information/kloadmetadatathread_p.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0796e67c1..fb5a88413 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,6 +57,7 @@ set(dolphinprivate_LIB_SRCS ${dolphinprivate_LIB_SRCS} panels/information/kcommentwidget.cpp panels/information/kedittagsdialog.cpp + panels/information/kloadmetadatathread.cpp panels/information/nepomukmassupdatejob.cpp panels/information/ktaggingwidget.cpp ) @@ -168,6 +169,7 @@ set(dolphin_SRCS ${dolphin_SRCS} panels/information/kcommentwidget.cpp panels/information/kedittagsdialog.cpp + panels/information/kloadmetadatathread.cpp panels/information/nepomukmassupdatejob.cpp panels/information/ktaggingwidget.cpp ) diff --git a/src/panels/information/kloadmetadatathread.cpp b/src/panels/information/kloadmetadatathread.cpp new file mode 100644 index 000000000..20575ac9b --- /dev/null +++ b/src/panels/information/kloadmetadatathread.cpp @@ -0,0 +1,168 @@ +/***************************************************************************** + * 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 version 2 as published by the Free Software Foundation. * + * * + * 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 "kloadmetadatathread_p.h" + +#include +#include + +#include + +KLoadMetaDataThread::KLoadMetaDataThread() : + m_rating(0), + m_comment(), + m_tags(), + m_metaInfoLabels(), + m_metaInfoValues(), + m_files(), + m_urls(), + m_canceled(false) +{ +} + +KLoadMetaDataThread::~KLoadMetaDataThread() +{ +} + +void KLoadMetaDataThread::load(const KUrl::List& urls) +{ + m_urls = urls; + m_canceled = false; + start(); +} + +void KLoadMetaDataThread::cancelAndDelete() +{ + connect(this, SIGNAL(finished()), this, SLOT(slotFinished())); + m_canceled = true; + // Setting m_canceled to true will cancel KLoadMetaDataThread::run() + // as soon as possible. Afterwards the thread will delete itself + // asynchronously inside slotFinished(). +} + +void KLoadMetaDataThread::run() +{ + KConfig config("kmetainformationrc", KConfig::NoGlobals); + KConfigGroup settings = config.group("Show"); + + bool first = true; + unsigned int rating = 0; + foreach (const KUrl& url, m_urls) { + if (m_canceled) { + return; + } + + Nepomuk::Resource file(url); + m_files.insert(url, file); + + if (!first && (rating != file.rating())) { + rating = 0; // reset rating + } else if (first) { + rating = file.rating(); + } + + if (!first && (m_comment != file.description())) { + m_comment.clear(); // reset comment + } else if (first) { + m_comment = file.description(); + } + + if (!first && (m_tags != file.tags())) { + m_tags.clear(); // reset tags + } else if (first) { + m_tags = file.tags(); + } + + if (first && (m_urls.count() == 1)) { + // TODO: show shared meta information instead + // of not showing anything on multiple selections + QHash properties = file.properties(); + QHash::const_iterator it = properties.constBegin(); + while (it != properties.constEnd()) { + Nepomuk::Types::Property prop(it.key()); + if (settings.readEntry(prop.name(), true)) { + // TODO #1: use Nepomuk::formatValue(res, prop) if available + // instead of it.value().toString() + // TODO #2: using tunedLabel() is a workaround for KDE 4.3 (4.4?) until + // we get translated labels + m_metaInfoLabels.append(tunedLabel(prop.label())); + m_metaInfoValues.append(it.value().toString()); + } + ++it; + } + } + + first = false; + } +} + +int KLoadMetaDataThread::rating() const +{ + return m_rating; +} + +QString KLoadMetaDataThread::comment() const +{ + return m_comment; +} + +QList KLoadMetaDataThread::tags() const +{ + return m_tags; +} + +QList KLoadMetaDataThread::metaInfoLabels() const +{ + return m_metaInfoLabels; +} + +QList KLoadMetaDataThread::metaInfoValues() const +{ + return m_metaInfoValues; +} + +QMap KLoadMetaDataThread::files() const +{ + return m_files; +} + +void KLoadMetaDataThread::slotFinished() +{ + deleteLater(); +} + +QString KLoadMetaDataThread::tunedLabel(const QString& label) const +{ + QString tunedLabel; + const int labelLength = label.length(); + if (labelLength > 0) { + tunedLabel.reserve(labelLength); + tunedLabel = label[0].toUpper(); + for (int i = 1; i < labelLength; ++i) { + if (label[i].isUpper() && !label[i - 1].isSpace() && !label[i - 1].isUpper()) { + tunedLabel += ' '; + tunedLabel += label[i].toLower(); + } else { + tunedLabel += label[i]; + } + } + } + return tunedLabel + ':'; +} + +#include "kloadmetadatathread_p.moc" diff --git a/src/panels/information/kloadmetadatathread_p.h b/src/panels/information/kloadmetadatathread_p.h new file mode 100644 index 000000000..e229766d6 --- /dev/null +++ b/src/panels/information/kloadmetadatathread_p.h @@ -0,0 +1,97 @@ +/***************************************************************************** + * 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 version 2 as published by the Free Software Foundation. * + * * + * 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 KLOADMETADATATHREAD_H +#define KLOADMETADATATHREAD_H + +#define DISABLE_NEPOMUK_LEGACY +#include +#include + +#include +#include +#include + +/** + * Loads the meta data of files that are + * required by the widget KMetaDataWidget. + */ +class KLoadMetaDataThread : public QThread +{ + Q_OBJECT + +public: + KLoadMetaDataThread(); + virtual ~KLoadMetaDataThread(); + + /** + * Starts the thread and loads the meta data for + * the files given by \p urls. After receiving + * the signal finished(), the methods + * rating(), comment(), tags(), metaInfoLabels(), + * metaInfoValues() and files() return valid data. + */ + void load(const KUrl::List& urls); + + /** + * Cancels the thread and assures that the thread deletes + * itself as soon as the cancelling has been successful. In + * opposite to QThread::wait() the caller of cancelAndDelete() + * will not be blocked. + */ + void cancelAndDelete(); + + /** @see QThread::run() */ + virtual void run(); + + int rating() const; + QString comment() const; + QList tags() const; + QList metaInfoLabels() const; + QList metaInfoValues() const; + QMap files() const; + +private slots: + void slotFinished(); + +private: + /** + * Assures that the settings for the meta information + * are initialized with proper default values. + */ + void initMetaInfoSettings(KConfigGroup& group); + + /** + * Temporary helper method for KDE 4.3 as we currently don't get + * translated labels for Nepmok literals: Replaces camelcase labels + * like "fileLocation" by "File Location:". + */ + QString tunedLabel(const QString& label) const; + +private: + int m_rating; + QString m_comment; + QList m_tags; + QList m_metaInfoLabels; + QList m_metaInfoValues; + QMap m_files; + + KUrl::List m_urls; + bool m_canceled; +}; +#endif diff --git a/src/panels/information/kmetadatawidget.cpp b/src/panels/information/kmetadatawidget.cpp index 98dc180c8..f644d2ae0 100644 --- a/src/panels/information/kmetadatawidget.cpp +++ b/src/panels/information/kmetadatawidget.cpp @@ -36,18 +36,25 @@ #define DISABLE_NEPOMUK_LEGACY #include "kcommentwidget_p.h" + #include "kloadmetadatathread_p.h" #include "ktaggingwidget_p.h" #include #include #include #include + #include #include #include "nepomukmassupdatejob_p.h" #include #include #include +#else + namespace Nepomuk + { + typedef int Tag; + } #endif class KMetaDataWidget::Private @@ -80,9 +87,11 @@ public: void updateRowsVisibility(); void slotLoadingFinished(); + void slotRatingChanged(unsigned int rating); void slotTagsChanged(const QList& tags); void slotCommentChanged(const QString& comment); + void slotMetaDataUpdateDone(); /** @@ -111,54 +120,9 @@ public: KTaggingWidget* m_taggingWidget; KCommentWidget* m_commentWidget; - // shared data between the GUI-thread and - // the loader-thread (see LoadFilesThread): - QMutex m_mutex; - struct SharedData - { - int rating; - QString comment; - QList tags; - QList metaInfoLabels; - QList metaInfoValues; - QMap files; - } m_sharedData; - - /** - * Loads the meta data of files and writes - * the result into a shared data pool that - * can be used by the widgets in the GUI thread. - */ - class LoadFilesThread : public QThread - { - public: - LoadFilesThread(SharedData* m_sharedData, QMutex* m_mutex); - virtual ~LoadFilesThread(); - void loadFiles(const KUrl::List& urls); - virtual void run(); - - private: - /** - * Assures that the settings for the meta information - * are initialized with proper default values. - */ - void initMetaInfoSettings(KConfigGroup& group); - - /** - * Temporary helper method for KDE 4.3 as we currently don't get - * translated labels for Nepmok literals: Replaces camelcase labels - * like "fileLocation" by "File Location:". - */ - QString tunedLabel(const QString& label) const; - - private: - SharedData* m_sharedData; - QMutex* m_mutex; - KUrl::List m_urls; - bool m_canceled; - }; + QMap m_files; - LoadFilesThread* m_loadFilesThread; + KLoadMetaDataThread* m_loadMetaDataThread; #endif private: @@ -182,7 +146,8 @@ KMetaDataWidget::Private::Private(KMetaDataWidget* parent) : m_ratingWidget(0), m_taggingWidget(0), m_commentWidget(0), - m_loadFilesThread(0), + m_files(), + m_loadMetaDataThread(0), #endif q(parent) { @@ -221,12 +186,7 @@ KMetaDataWidget::Private::Private(KMetaDataWidget* parent) : addRow(new QLabel(i18nc("@label", "Rating:"), parent), m_ratingWidget); addRow(new QLabel(i18nc("@label", "Tags:"), parent), m_taggingWidget); addRow(new QLabel(i18nc("@label", "Comment:"), parent), m_commentWidget); - - m_loadFilesThread = new LoadFilesThread(&m_sharedData, &m_mutex); - connect(m_loadFilesThread, SIGNAL(finished()), q, SLOT(slotLoadingFinished())); } - - m_sharedData.rating = 0; #endif initMetaInfoSettings(); @@ -236,7 +196,11 @@ KMetaDataWidget::Private::Private(KMetaDataWidget* parent) : KMetaDataWidget::Private::~Private() { #ifdef HAVE_NEPOMUK - delete m_loadFilesThread; + if (m_loadMetaDataThread != 0) { + disconnect(m_loadMetaDataThread, SIGNAL(finished()), q, SLOT(slotLoadingFinished())); + m_loadMetaDataThread->cancelAndDelete(); + m_loadMetaDataThread = 0; + } #endif } @@ -360,10 +324,10 @@ void KMetaDataWidget::Private::updateRowsVisibility() void KMetaDataWidget::Private::slotLoadingFinished() { #ifdef HAVE_NEPOMUK - QMutexLocker locker(&m_mutex); - m_ratingWidget->setRating(m_sharedData.rating); - m_commentWidget->setText(m_sharedData.comment); - m_taggingWidget->setTags(m_sharedData.tags); + Q_ASSERT(m_loadMetaDataThread != 0); + m_ratingWidget->setRating(m_loadMetaDataThread->rating()); + m_commentWidget->setText(m_loadMetaDataThread->comment()); + m_taggingWidget->setTags(m_loadMetaDataThread->tags()); // Show the remaining meta information as text. The number // of required rows may very. Existing rows are reused to @@ -372,19 +336,21 @@ void KMetaDataWidget::Private::slotLoadingFinished() const int rowCount = m_rows.count(); Q_ASSERT(rowCount >= index); - Q_ASSERT(m_sharedData.metaInfoLabels.count() == m_sharedData.metaInfoValues.count()); - const int metaInfoCount = m_sharedData.metaInfoLabels.count(); + Q_ASSERT(m_loadMetaDataThread->metaInfoLabels().count() == m_loadMetaDataThread->metaInfoValues().count()); + const int metaInfoCount = m_loadMetaDataThread->metaInfoLabels().count(); for (int i = 0; i < metaInfoCount; ++i) { + const QList metaInfoLabels = m_loadMetaDataThread->metaInfoLabels(); + const QList metaInfoValues = m_loadMetaDataThread->metaInfoValues(); if (index < rowCount) { // adjust texts of the current row - m_rows[index].label->setText(m_sharedData.metaInfoLabels[i]); + m_rows[index].label->setText(metaInfoLabels[i]); QLabel* infoValueLabel = qobject_cast(m_rows[index].infoWidget); Q_ASSERT(infoValueLabel != 0); - infoValueLabel->setText(m_sharedData.metaInfoValues[i]); + infoValueLabel->setText(metaInfoValues[i]); } else { // create new row - QLabel* infoLabel = new QLabel(m_sharedData.metaInfoLabels[i], q); - QLabel* infoValue = new QLabel(m_sharedData.metaInfoValues[i], q); + QLabel* infoLabel = new QLabel(metaInfoLabels[i], q); + QLabel* infoValue = new QLabel(metaInfoValues[i], q); addRow(infoLabel, infoValue); } ++index; @@ -399,18 +365,24 @@ void KMetaDataWidget::Private::slotLoadingFinished() delete m_rows[i].infoWidget; m_rows.pop_back(); } + + m_files = m_loadMetaDataThread->files(); + + delete m_loadMetaDataThread; + m_loadMetaDataThread = 0; #endif + q->updateGeometry(); } void KMetaDataWidget::Private::slotRatingChanged(unsigned int rating) { #ifdef HAVE_NEPOMUK - QMutexLocker locker(&m_mutex); Nepomuk::MassUpdateJob* job = - Nepomuk::MassUpdateJob::rateResources(m_sharedData.files.values(), rating); - locker.unlock(); + Nepomuk::MassUpdateJob::rateResources(m_files.values(), rating); startChangeDataJob(job); +#else + Q_UNUSED(rating); #endif } @@ -419,30 +391,33 @@ void KMetaDataWidget::Private::slotTagsChanged(const QList& tags) #ifdef HAVE_NEPOMUK m_taggingWidget->setTags(tags); - QMutexLocker locker(&m_mutex); Nepomuk::MassUpdateJob* job = - Nepomuk::MassUpdateJob::tagResources(m_sharedData.files.values(), tags); - locker.unlock(); + Nepomuk::MassUpdateJob::tagResources(m_files.values(), tags); startChangeDataJob(job); +#else + Q_UNUSED(tags); #endif } void KMetaDataWidget::Private::slotCommentChanged(const QString& comment) { #ifdef HAVE_NEPOMUK - QMutexLocker locker(&m_mutex); Nepomuk::MassUpdateJob* job = - Nepomuk::MassUpdateJob::commentResources(m_sharedData.files.values(), comment); - locker.unlock(); + Nepomuk::MassUpdateJob::commentResources(m_files.values(), comment); startChangeDataJob(job); +#else + Q_UNUSED(comment); #endif } void KMetaDataWidget::Private::slotMetaDataUpdateDone() { +#ifdef HAVE_NEPOMUK q->setEnabled(true); +#endif } +#ifdef HAVE_NEPOMUK void KMetaDataWidget::Private::startChangeDataJob(KJob* job) { connect(job, SIGNAL(result(KJob*)), @@ -450,127 +425,7 @@ void KMetaDataWidget::Private::startChangeDataJob(KJob* job) q->setEnabled(false); // no updates during execution job->start(); } - -#ifdef HAVE_NEPOMUK -KMetaDataWidget::Private::LoadFilesThread::LoadFilesThread( - KMetaDataWidget::Private::SharedData* m_sharedData, - QMutex* m_mutex) : - m_sharedData(m_sharedData), - m_mutex(m_mutex), - m_urls(), - m_canceled(false) -{ -} - -KMetaDataWidget::Private::LoadFilesThread::~LoadFilesThread() -{ - // This thread may very well be deleted during execution. We need - // to protect it from crashes here. - m_canceled = true; - wait(); -} - -void KMetaDataWidget::Private::LoadFilesThread::loadFiles(const KUrl::List& urls) -{ - QMutexLocker locker(m_mutex); - m_urls = urls; - m_canceled = false; - start(); -} - -void KMetaDataWidget::Private::LoadFilesThread::run() -{ - QMutexLocker locker(m_mutex); - const KUrl::List urls = m_urls; - locker.unlock(); - - KConfig config("kmetainformationrc", KConfig::NoGlobals); - KConfigGroup settings = config.group("Show"); - - bool first = true; - unsigned int rating = 0; - QString comment; - QList tags; - QList metaInfoLabels; - QList metaInfoValues; - QMap files; - foreach (const KUrl& url, urls) { - if (m_canceled) { - return; - } - - Nepomuk::Resource file(url); - files.insert(url, file); - - if (!first && (rating != file.rating())) { - rating = 0; // reset rating - } else if (first) { - rating = file.rating(); - } - - if (!first && (comment != file.description())) { - comment.clear(); // reset comment - } else if (first) { - comment = file.description(); - } - - if (!first && (tags != file.tags())) { - tags.clear(); // reset tags - } else if (first) { - tags = file.tags(); - } - - if (first && (urls.count() == 1)) { - // TODO: show shared meta information instead - // of not showing anything on multiple selections - QHash properties = file.properties(); - QHash::const_iterator it = properties.constBegin(); - while (it != properties.constEnd()) { - Nepomuk::Types::Property prop(it.key()); - if (settings.readEntry(prop.name(), true)) { - // TODO #1: use Nepomuk::formatValue(res, prop) if available - // instead of it.value().toString() - // TODO #2: using tunedLabel() is a workaround for KDE 4.3 (4.4?) until - // we get translated labels - metaInfoLabels.append(tunedLabel(prop.label())); - metaInfoValues.append(it.value().toString()); - } - ++it; - } - } - - first = false; - } - - locker.relock(); - m_sharedData->rating = rating; - m_sharedData->comment = comment; - m_sharedData->tags = tags; - m_sharedData->metaInfoLabels = metaInfoLabels; - m_sharedData->metaInfoValues = metaInfoValues; - m_sharedData->files = files; -} - -QString KMetaDataWidget::Private::LoadFilesThread::tunedLabel(const QString& label) const -{ - QString tunedLabel; - const int labelLength = label.length(); - if (labelLength > 0) { - tunedLabel.reserve(labelLength); - tunedLabel = label[0].toUpper(); - for (int i = 1; i < labelLength; ++i) { - if (label[i].isUpper() && !label[i - 1].isSpace() && !label[i - 1].isUpper()) { - tunedLabel += ' '; - tunedLabel += label[i].toLower(); - } else { - tunedLabel += label[i]; - } - } - } - return tunedLabel + ':'; -} - -#endif // HAVE_NEPOMUK +#endif KMetaDataWidget::KMetaDataWidget(QWidget* parent) : QWidget(parent), @@ -631,7 +486,15 @@ void KMetaDataWidget::setItems(const KFileItemList& items) urls.append(url); } } - d->m_loadFilesThread->loadFiles(urls); + + if (d->m_loadMetaDataThread != 0) { + disconnect(d->m_loadMetaDataThread, SIGNAL(finished()), this, SLOT(slotLoadingFinished())); + d->m_loadMetaDataThread->cancelAndDelete(); + } + + d->m_loadMetaDataThread = new KLoadMetaDataThread(); + connect(d->m_loadMetaDataThread, SIGNAL(finished()), this, SLOT(slotLoadingFinished())); + d->m_loadMetaDataThread->load(urls); } #endif } @@ -670,4 +533,20 @@ KMetaDataWidget::MetaDataTypes KMetaDataWidget::visibleDataTypes() const return d->m_visibleDataTypes; } +QSize KMetaDataWidget::sizeHint() const +{ + const int fixedWidth = 200; + int height = 0; + foreach (const Private::Row& row, d->m_rows) { + if (row.infoWidget != 0) { + int rowHeight = row.infoWidget->heightForWidth(fixedWidth / 2); + if (rowHeight <= 0) { + rowHeight = row.infoWidget->sizeHint().height(); + } + height += rowHeight; + } + } + return QSize(fixedWidth, height); +} + #include "kmetadatawidget.moc" diff --git a/src/panels/information/kmetadatawidget.h b/src/panels/information/kmetadatawidget.h index ee724da5b..182d3a0c8 100644 --- a/src/panels/information/kmetadatawidget.h +++ b/src/panels/information/kmetadatawidget.h @@ -112,6 +112,9 @@ public: */ MetaDataTypes visibleDataTypes() const; + /** @see QWidget::sizeHint() */ + virtual QSize sizeHint() const; + private: class Private; Private* d; diff --git a/src/tooltips/tooltipmanager.cpp b/src/tooltips/tooltipmanager.cpp index 8cf6ec3b5..baefee095 100644 --- a/src/tooltips/tooltipmanager.cpp +++ b/src/tooltips/tooltipmanager.cpp @@ -249,7 +249,7 @@ QWidget* ToolTipManager::createTipContent(const QPixmap& pixmap) const KMetaDataWidget* metaDataWidget = new KMetaDataWidget(tipContent); metaDataWidget->setItem(m_item); - metaDataWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum); + metaDataWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QHBoxLayout* tipLayout = new QHBoxLayout(tipContent); tipLayout->setMargin(0); -- 2.47.3