KIO
kfilemetadataprovider.cpp
Go to the documentation of this file.
00001 /***************************************************************************** 00002 * Copyright (C) 2010 by Peter Penz <peter.penz@gmx.at> * 00003 * * 00004 * This library is free software; you can redistribute it and/or * 00005 * modify it under the terms of the GNU Library General Public * 00006 * License as published by the Free Software Foundation; either * 00007 * version 2 of the License, or (at your option) any later version. * 00008 * * 00009 * This library is distributed in the hope that it will be useful, * 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 00012 * Library General Public License for more details. * 00013 * * 00014 * You should have received a copy of the GNU Library General Public License * 00015 * along with this library; see the file COPYING.LIB. If not, write to * 00016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 00017 * Boston, MA 02110-1301, USA. * 00018 *****************************************************************************/ 00019 00020 #include "kfilemetadataprovider_p.h" 00021 00022 #include <kfileitem.h> 00023 #include "knfotranslator_p.h" 00024 #include <klocale.h> 00025 #include <kurl.h> 00026 00027 #ifndef KIO_NO_NEPOMUK 00028 #define DISABLE_NEPOMUK_LEGACY 00029 #include "nepomukmassupdatejob.h" 00030 #include "tagwidget.h" 00031 #include "kratingwidget.h" 00032 #include "resource.h" 00033 #include "resourcemanager.h" 00034 00035 #include "kcommentwidget_p.h" 00036 #include "kloadfilemetadatathread_p.h" 00037 #else 00038 namespace Nepomuk 00039 { 00040 typedef int Tag; 00041 } 00042 #endif 00043 00044 #include <QEvent> 00045 #include <QLabel> 00046 00047 namespace { 00048 static QString plainText(const QString& richText) 00049 { 00050 QString plainText; 00051 plainText.reserve(richText.length()); 00052 00053 bool skip = false; 00054 for (int i = 0; i < richText.length(); ++i) { 00055 const QChar c = richText.at(i); 00056 if (c == QLatin1Char('<')) { 00057 skip = true; 00058 } else if (c == QLatin1Char('>')) { 00059 skip = false; 00060 } else if (!skip) { 00061 plainText.append(c); 00062 } 00063 } 00064 00065 return plainText; 00066 } 00067 } 00068 00069 // The default size hint of QLabel tries to return a square size. 00070 // This does not work well in combination with layouts that use 00071 // heightForWidth(): In this case it is possible that the content 00072 // of a label might get clipped. By specifying a size hint 00073 // with a maximum width that is necessary to contain the whole text, 00074 // using heightForWidth() assures having a non-clipped text. 00075 class ValueWidget : public QLabel 00076 { 00077 public: 00078 explicit ValueWidget(QWidget* parent = 0); 00079 virtual QSize sizeHint() const; 00080 }; 00081 00082 ValueWidget::ValueWidget(QWidget* parent) : 00083 QLabel(parent) 00084 { 00085 } 00086 00087 QSize ValueWidget::sizeHint() const 00088 { 00089 QFontMetrics metrics(font()); 00090 // TODO: QLabel internally provides already a method sizeForWidth(), 00091 // that would be sufficient. However this method is not accessible, so 00092 // as workaround the tags from a richtext are removed manually here to 00093 // have a proper size hint. 00094 return metrics.size(Qt::TextSingleLine, plainText(text())); 00095 } 00096 00097 00098 00099 class KFileMetaDataProvider::Private 00100 { 00101 00102 public: 00103 Private(KFileMetaDataProvider* parent); 00104 ~Private(); 00105 00106 void slotLoadingFinished(QThread* finishedThread); 00107 00108 void slotRatingChanged(unsigned int rating); 00109 void slotTagsChanged(const QList<Nepomuk::Tag>& tags); 00110 void slotCommentChanged(const QString& comment); 00111 00112 void slotMetaDataUpdateDone(); 00113 void slotTagClicked(const Nepomuk::Tag& tag); 00114 void slotLinkActivated(const QString& link); 00115 00121 void startChangeDataJob(KJob* job); 00122 00123 #ifndef KIO_NO_NEPOMUK 00124 QList<Nepomuk::Resource> resourceList() const; 00125 QWidget* createRatingWidget(int rating, QWidget* parent); 00126 QWidget* createTagWidget(const QList<Nepomuk::Tag>& tags, QWidget* parent); 00127 QWidget* createCommentWidget(const QString& comment, QWidget* parent); 00128 #endif 00129 QWidget* createValueWidget(const QString& value, QWidget* parent); 00130 00131 bool m_readOnly; 00132 bool m_nepomukActivated; 00133 QList<KFileItem> m_fileItems; 00134 00135 #ifndef KIO_NO_NEPOMUK 00136 QHash<KUrl, Nepomuk::Variant> m_data; 00137 00138 QList<KLoadFileMetaDataThread*> m_metaDataThreads; 00139 KLoadFileMetaDataThread* m_latestMetaDataThread; 00140 00141 QWeakPointer<KRatingWidget> m_ratingWidget; 00142 QWeakPointer<Nepomuk::TagWidget> m_tagWidget; 00143 QWeakPointer<KCommentWidget> m_commentWidget; 00144 #endif 00145 00146 private: 00147 KFileMetaDataProvider* const q; 00148 }; 00149 00150 KFileMetaDataProvider::Private::Private(KFileMetaDataProvider* parent) : 00151 m_readOnly(false), 00152 m_nepomukActivated(false), 00153 m_fileItems(), 00154 #ifndef KIO_NO_NEPOMUK 00155 m_data(), 00156 m_metaDataThreads(), 00157 m_latestMetaDataThread(0), 00158 m_ratingWidget(), 00159 m_tagWidget(), 00160 m_commentWidget(), 00161 #endif 00162 q(parent) 00163 { 00164 #ifndef KIO_NO_NEPOMUK 00165 m_nepomukActivated = (Nepomuk::ResourceManager::instance()->init() == 0); 00166 #endif 00167 } 00168 00169 KFileMetaDataProvider::Private::~Private() 00170 { 00171 #ifndef KIO_NO_NEPOMUK 00172 foreach (KLoadFileMetaDataThread* thread, m_metaDataThreads) { 00173 disconnect(thread, SIGNAL(finished(QThread*)), 00174 q, SLOT(slotLoadingFinished(QThread*))); 00175 thread->wait(); 00176 } 00177 #endif 00178 } 00179 00180 void KFileMetaDataProvider::Private::slotLoadingFinished(QThread* finishedThread) 00181 { 00182 #ifndef KIO_NO_NEPOMUK 00183 // The thread that has emitted the finished() signal 00184 // will get deleted and removed from m_metaDataThreads. 00185 const int threadsCount = m_metaDataThreads.count(); 00186 for (int i = 0; i < threadsCount; ++i) { 00187 KLoadFileMetaDataThread* thread = m_metaDataThreads[i]; 00188 if (thread == finishedThread) { 00189 m_metaDataThreads.removeAt(i); 00190 if (thread != m_latestMetaDataThread) { 00191 // Ignore data of older threads, as the data got 00192 // obsolete by m_latestMetaDataThread. 00193 thread->deleteLater(); 00194 return; 00195 } 00196 } 00197 } 00198 00199 m_data = m_latestMetaDataThread->data(); 00200 m_latestMetaDataThread->deleteLater(); 00201 00202 if (m_fileItems.count() == 1) { 00203 // TODO: Handle case if remote URLs are used properly. isDir() does 00204 // not work, the modification date needs also to be adjusted... 00205 const KFileItem& item = m_fileItems.first(); 00206 00207 m_data.insert(KUrl("kfileitem#size"), KIO::convertSize(item.size())); 00208 m_data.insert(KUrl("kfileitem#type"), item.mimeComment()); 00209 m_data.insert(KUrl("kfileitem#modified"), KGlobal::locale()->formatDateTime(item.time(KFileItem::ModificationTime), KLocale::FancyLongDate)); 00210 m_data.insert(KUrl("kfileitem#owner"), item.user()); 00211 m_data.insert(KUrl("kfileitem#permissions"), item.permissionsString()); 00212 } else if (m_fileItems.count() > 1) { 00213 // Calculate the size of all items 00214 quint64 totalSize = 0; 00215 foreach (const KFileItem& item, m_fileItems) { 00216 if (!item.isDir() && !item.isLink()) { 00217 totalSize += item.size(); 00218 } 00219 } 00220 m_data.insert(KUrl("kfileitem#totalSize"), KIO::convertSize(totalSize)); 00221 } 00222 #else 00223 Q_UNUSED(finishedThread) 00224 #endif 00225 00226 emit q->loadingFinished(); 00227 } 00228 00229 00230 void KFileMetaDataProvider::Private::slotRatingChanged(unsigned int rating) 00231 { 00232 #ifndef KIO_NO_NEPOMUK 00233 Nepomuk::MassUpdateJob* job = Nepomuk::MassUpdateJob::rateResources(resourceList(), rating); 00234 startChangeDataJob(job); 00235 #else 00236 Q_UNUSED(rating); 00237 #endif 00238 } 00239 00240 void KFileMetaDataProvider::Private::slotTagsChanged(const QList<Nepomuk::Tag>& tags) 00241 { 00242 #ifndef KIO_NO_NEPOMUK 00243 if (!m_tagWidget.isNull()) { 00244 m_tagWidget.data()->setSelectedTags(tags); 00245 00246 Nepomuk::MassUpdateJob* job = Nepomuk::MassUpdateJob::tagResources(resourceList(), tags); 00247 startChangeDataJob(job); 00248 } 00249 #else 00250 Q_UNUSED(tags); 00251 #endif 00252 } 00253 00254 void KFileMetaDataProvider::Private::slotCommentChanged(const QString& comment) 00255 { 00256 #ifndef KIO_NO_NEPOMUK 00257 Nepomuk::MassUpdateJob* job = Nepomuk::MassUpdateJob::commentResources(resourceList(), comment); 00258 startChangeDataJob(job); 00259 #else 00260 Q_UNUSED(comment); 00261 #endif 00262 } 00263 00264 void KFileMetaDataProvider::Private::slotTagClicked(const Nepomuk::Tag& tag) 00265 { 00266 #ifndef KIO_NO_NEPOMUK 00267 emit q->urlActivated(tag.resourceUri()); 00268 #else 00269 Q_UNUSED(tag); 00270 #endif 00271 } 00272 00273 void KFileMetaDataProvider::Private::slotLinkActivated(const QString& link) 00274 { 00275 emit q->urlActivated(KUrl(link)); 00276 } 00277 00278 void KFileMetaDataProvider::Private::startChangeDataJob(KJob* job) 00279 { 00280 connect(job, SIGNAL(result(KJob*)), 00281 q, SIGNAL(dataChangeFinished())); 00282 emit q->dataChangeStarted(); 00283 job->start(); 00284 } 00285 00286 #ifndef KIO_NO_NEPOMUK 00287 QList<Nepomuk::Resource> KFileMetaDataProvider::Private::resourceList() const 00288 { 00289 QList<Nepomuk::Resource> list; 00290 foreach (const KFileItem& item, m_fileItems) { 00291 const KUrl url = item.nepomukUri(); 00292 if(url.isValid()) 00293 list.append(Nepomuk::Resource(url)); 00294 } 00295 return list; 00296 } 00297 00298 QWidget* KFileMetaDataProvider::Private::createRatingWidget(int rating, QWidget* parent) 00299 { 00300 KRatingWidget* ratingWidget = new KRatingWidget(parent); 00301 const Qt::Alignment align = (ratingWidget->layoutDirection() == Qt::LeftToRight) ? 00302 Qt::AlignLeft : Qt::AlignRight; 00303 ratingWidget->setAlignment(align); 00304 ratingWidget->setRating(rating); 00305 const QFontMetrics metrics(parent->font()); 00306 ratingWidget->setPixmapSize(metrics.height()); 00307 00308 connect(ratingWidget, SIGNAL(ratingChanged(unsigned int)), 00309 q, SLOT(slotRatingChanged(unsigned int))); 00310 00311 m_ratingWidget = ratingWidget; 00312 00313 return ratingWidget; 00314 } 00315 00316 QWidget* KFileMetaDataProvider::Private::createTagWidget(const QList<Nepomuk::Tag>& tags, QWidget* parent) 00317 { 00318 Nepomuk::TagWidget* tagWidget = new Nepomuk::TagWidget(parent); 00319 tagWidget->setModeFlags(Nepomuk::TagWidget::MiniMode); 00320 tagWidget->setSelectedTags(tags); 00321 tagWidget->setModeFlags(m_readOnly 00322 ? Nepomuk::TagWidget::MiniMode | Nepomuk::TagWidget::ReadOnly 00323 : Nepomuk::TagWidget::MiniMode); 00324 00325 connect(tagWidget, SIGNAL(selectionChanged(QList<Nepomuk::Tag>)), 00326 q, SLOT(slotTagsChanged(QList<Nepomuk::Tag>))); 00327 connect(tagWidget, SIGNAL(tagClicked(Nepomuk::Tag)), 00328 q, SLOT(slotTagClicked(Nepomuk::Tag))); 00329 00330 m_tagWidget = tagWidget; 00331 00332 return tagWidget; 00333 } 00334 00335 QWidget* KFileMetaDataProvider::Private::createCommentWidget(const QString& comment, QWidget* parent) 00336 { 00337 KCommentWidget* commentWidget = new KCommentWidget(parent); 00338 commentWidget->setText(comment); 00339 commentWidget->setReadOnly(m_readOnly); 00340 00341 connect(commentWidget, SIGNAL(commentChanged(const QString&)), 00342 q, SLOT(slotCommentChanged(const QString&))); 00343 00344 m_commentWidget = commentWidget; 00345 00346 return commentWidget; 00347 } 00348 #endif 00349 00350 QWidget* KFileMetaDataProvider::Private::createValueWidget(const QString& value, QWidget* parent) 00351 { 00352 ValueWidget* valueWidget = new ValueWidget(parent); 00353 valueWidget->setWordWrap(true); 00354 valueWidget->setAlignment(Qt::AlignTop | Qt::AlignLeft); 00355 valueWidget->setText(m_readOnly ? plainText(value) : value); 00356 connect(valueWidget, SIGNAL(linkActivated(QString)), q, SLOT(slotLinkActivated(QString))); 00357 return valueWidget; 00358 } 00359 00360 KFileMetaDataProvider::KFileMetaDataProvider(QObject* parent) : 00361 QObject(parent), 00362 d(new Private(this)) 00363 { 00364 } 00365 00366 KFileMetaDataProvider::~KFileMetaDataProvider() 00367 { 00368 delete d; 00369 } 00370 00371 void KFileMetaDataProvider::setItems(const KFileItemList& items) 00372 { 00373 d->m_fileItems = items; 00374 00375 #ifndef KIO_NO_NEPOMUK 00376 if (items.isEmpty()) { 00377 return; 00378 } 00379 Q_PRIVATE_SLOT(d, void slotDataChangeStarted()) 00380 Q_PRIVATE_SLOT(d, void slotDataChangeFinished()) 00381 QList<KUrl> urls; 00382 foreach (const KFileItem& item, items) { 00383 const KUrl url = item.nepomukUri(); 00384 if (url.isValid()) { 00385 urls.append(url); 00386 } 00387 } 00388 00389 // Cancel all threads that have not emitted a finished() signal. 00390 // The deleting of those threads is done in slotLoadingFinished(). 00391 foreach (KLoadFileMetaDataThread* thread, d->m_metaDataThreads) { 00392 thread->cancel(); 00393 } 00394 00395 // Create a new thread that will provide the meta data for the items 00396 d->m_latestMetaDataThread = new KLoadFileMetaDataThread(); 00397 connect(d->m_latestMetaDataThread, SIGNAL(finished(QThread*)), 00398 this, SLOT(slotLoadingFinished(QThread*))); 00399 d->m_latestMetaDataThread->load(urls); 00400 d->m_metaDataThreads.append(d->m_latestMetaDataThread); 00401 #endif 00402 } 00403 00404 QString KFileMetaDataProvider::label(const KUrl& metaDataUri) const 00405 { 00406 struct TranslationItem { 00407 const char* const key; 00408 const char* const context; 00409 const char* const value; 00410 }; 00411 00412 static const TranslationItem translations[] = { 00413 { "kfileitem#comment", I18N_NOOP2_NOSTRIP("@label", "Comment") }, 00414 { "kfileitem#modified", I18N_NOOP2_NOSTRIP("@label", "Modified") }, 00415 { "kfileitem#owner", I18N_NOOP2_NOSTRIP("@label", "Owner") }, 00416 { "kfileitem#permissions", I18N_NOOP2_NOSTRIP("@label", "Permissions") }, 00417 { "kfileitem#rating", I18N_NOOP2_NOSTRIP("@label", "Rating") }, 00418 { "kfileitem#size", I18N_NOOP2_NOSTRIP("@label", "Size") }, 00419 { "kfileitem#tags", I18N_NOOP2_NOSTRIP("@label", "Tags") }, 00420 { "kfileitem#totalSize", I18N_NOOP2_NOSTRIP("@label", "Total Size") }, 00421 { "kfileitem#type", I18N_NOOP2_NOSTRIP("@label", "Type") }, 00422 { 0, 0, 0} // Mandatory last entry 00423 }; 00424 00425 static QHash<QString, QString> hash; 00426 if (hash.isEmpty()) { 00427 const TranslationItem* item = &translations[0]; 00428 while (item->key != 0) { 00429 hash.insert(item->key, i18nc(item->context, item->value)); 00430 ++item; 00431 } 00432 } 00433 00434 QString value = hash.value(metaDataUri.url()); 00435 if (value.isEmpty()) { 00436 value = KNfoTranslator::instance().translation(metaDataUri); 00437 } 00438 00439 return value; 00440 } 00441 00442 QString KFileMetaDataProvider::group(const KUrl& metaDataUri) const 00443 { 00444 QString group; // return value 00445 00446 const QString uri = metaDataUri.url(); 00447 if (uri == QLatin1String("kfileitem#type")) { 00448 group = QLatin1String("0FileItemA"); 00449 } else if (uri == QLatin1String("kfileitem#size")) { 00450 group = QLatin1String("0FileItemB"); 00451 } else if (uri == QLatin1String("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width")) { 00452 group = QLatin1String("0SizeA"); 00453 } else if (uri == QLatin1String("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height")) { 00454 group = QLatin1String("0SizeB"); 00455 } 00456 00457 return group; 00458 } 00459 00460 KFileItemList KFileMetaDataProvider::items() const 00461 { 00462 return d->m_fileItems; 00463 } 00464 00465 void KFileMetaDataProvider::setReadOnly(bool readOnly) 00466 { 00467 d->m_readOnly = readOnly; 00468 } 00469 00470 bool KFileMetaDataProvider::isReadOnly() const 00471 { 00472 return d->m_readOnly; 00473 } 00474 00475 #ifndef KIO_NO_NEPOMUK 00476 QHash<KUrl, Nepomuk::Variant> KFileMetaDataProvider::data() const 00477 { 00478 return d->m_data; 00479 } 00480 00481 QWidget* KFileMetaDataProvider::createValueWidget(const KUrl& metaDataUri, 00482 const Nepomuk::Variant& value, 00483 QWidget* parent) const 00484 { 00485 Q_ASSERT(parent != 0); 00486 QWidget* widget = 0; 00487 00488 if (d->m_nepomukActivated) { 00489 const QString uri = metaDataUri.url(); 00490 if (uri == QLatin1String("kfileitem#rating")) { 00491 widget = d->createRatingWidget(value.toInt(), parent); 00492 } else if (uri == QLatin1String("kfileitem#tags")) { 00493 const QList<Nepomuk::Variant> variants = value.toVariantList(); 00494 QList<Nepomuk::Tag> tags; 00495 foreach (const Nepomuk::Variant& variant, variants) { 00496 const Nepomuk::Resource resource = variant.toResource(); 00497 tags.append(static_cast<Nepomuk::Tag>(resource)); 00498 } 00499 00500 widget = d->createTagWidget(tags, parent); 00501 } else if (uri == QLatin1String("kfileitem#comment")) { 00502 widget = d->createCommentWidget(value.toString(), parent); 00503 } 00504 } 00505 00506 if (widget == 0) { 00507 widget = d->createValueWidget(value.toString(), parent); 00508 } 00509 00510 widget->setForegroundRole(parent->foregroundRole()); 00511 widget->setFont(parent->font()); 00512 00513 return widget; 00514 } 00515 #endif 00516 00517 #include "kfilemetadataprovider_p.moc"
KDE 4.6 API Reference