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 <kfilemetadatareader_p.h> 00024 #include "knfotranslator_p.h" 00025 #include <klocale.h> 00026 #include <kstandarddirs.h> 00027 #include <kurl.h> 00028 00029 #ifndef KIO_NO_NEPOMUK 00030 #define DISABLE_NEPOMUK_LEGACY 00031 #include "nepomukmassupdatejob.h" 00032 #include "tagwidget.h" 00033 #include "tag.h" 00034 #include "kratingwidget.h" 00035 #include "resource.h" 00036 #include "resourcemanager.h" 00037 00038 #include "kcommentwidget_p.h" 00039 #else 00040 namespace Nepomuk 00041 { 00042 typedef int Tag; 00043 } 00044 #endif 00045 00046 #include <QEvent> 00047 #include <QLabel> 00048 00049 // Required includes for subDirectoriesCount(): 00050 #ifdef Q_WS_WIN 00051 #include <QDir> 00052 #else 00053 #include <dirent.h> 00054 #include <QFile> 00055 #endif 00056 00057 namespace { 00058 static QString plainText(const QString& richText) 00059 { 00060 QString plainText; 00061 plainText.reserve(richText.length()); 00062 00063 bool skip = false; 00064 for (int i = 0; i < richText.length(); ++i) { 00065 const QChar c = richText.at(i); 00066 if (c == QLatin1Char('<')) { 00067 skip = true; 00068 } else if (c == QLatin1Char('>')) { 00069 skip = false; 00070 } else if (!skip) { 00071 plainText.append(c); 00072 } 00073 } 00074 00075 return plainText; 00076 } 00077 } 00078 00079 // The default size hint of QLabel tries to return a square size. 00080 // This does not work well in combination with layouts that use 00081 // heightForWidth(): In this case it is possible that the content 00082 // of a label might get clipped. By specifying a size hint 00083 // with a maximum width that is necessary to contain the whole text, 00084 // using heightForWidth() assures having a non-clipped text. 00085 class ValueWidget : public QLabel 00086 { 00087 public: 00088 explicit ValueWidget(QWidget* parent = 0); 00089 virtual QSize sizeHint() const; 00090 }; 00091 00092 ValueWidget::ValueWidget(QWidget* parent) : 00093 QLabel(parent) 00094 { 00095 } 00096 00097 QSize ValueWidget::sizeHint() const 00098 { 00099 QFontMetrics metrics(font()); 00100 // TODO: QLabel internally provides already a method sizeForWidth(), 00101 // that would be sufficient. However this method is not accessible, so 00102 // as workaround the tags from a richtext are removed manually here to 00103 // have a proper size hint. 00104 return metrics.size(Qt::TextSingleLine, plainText(text())); 00105 } 00106 00107 00108 00109 class KFileMetaDataProvider::Private 00110 { 00111 00112 public: 00113 Private(KFileMetaDataProvider* parent); 00114 ~Private(); 00115 00116 void slotLoadingFinished(); 00117 00118 void slotRatingChanged(unsigned int rating); 00119 void slotTagsChanged(const QList<Nepomuk::Tag>& tags); 00120 void slotCommentChanged(const QString& comment); 00121 00122 void slotMetaDataUpdateDone(); 00123 void slotTagClicked(const Nepomuk::Tag& tag); 00124 void slotLinkActivated(const QString& link); 00125 00131 void startChangeDataJob(KJob* job); 00132 00133 #ifndef KIO_NO_NEPOMUK 00134 QList<Nepomuk::Resource> resourceList() const; 00135 QWidget* createRatingWidget(int rating, QWidget* parent); 00136 QWidget* createTagWidget(const QList<Nepomuk::Tag>& tags, QWidget* parent); 00137 QWidget* createCommentWidget(const QString& comment, QWidget* parent); 00138 #endif 00139 QWidget* createValueWidget(const QString& value, QWidget* parent); 00140 00141 /* 00142 * @return The number of subdirectories for the directory \a path. 00143 */ 00144 static int subDirectoriesCount(const QString &path); 00145 00146 bool m_readOnly; 00147 bool m_nepomukActivated; 00148 QList<KFileItem> m_fileItems; 00149 00150 #ifndef KIO_NO_NEPOMUK 00151 QHash<KUrl, Nepomuk::Variant> m_data; 00152 00153 QList<KFileMetaDataReader*> m_metaDataReaders; 00154 KFileMetaDataReader* m_latestMetaDataReader; 00155 00156 QWeakPointer<KRatingWidget> m_ratingWidget; 00157 QWeakPointer<Nepomuk::TagWidget> m_tagWidget; 00158 QWeakPointer<KCommentWidget> m_commentWidget; 00159 #endif 00160 00161 private: 00162 KFileMetaDataProvider* const q; 00163 }; 00164 00165 KFileMetaDataProvider::Private::Private(KFileMetaDataProvider* parent) : 00166 m_readOnly(false), 00167 m_nepomukActivated(false), 00168 m_fileItems(), 00169 #ifndef KIO_NO_NEPOMUK 00170 m_data(), 00171 m_metaDataReaders(), 00172 m_latestMetaDataReader(0), 00173 m_ratingWidget(), 00174 m_tagWidget(), 00175 m_commentWidget(), 00176 #endif 00177 q(parent) 00178 { 00179 #ifndef KIO_NO_NEPOMUK 00180 m_nepomukActivated = (Nepomuk::ResourceManager::instance()->init() == 0); 00181 #endif 00182 } 00183 00184 KFileMetaDataProvider::Private::~Private() 00185 { 00186 #ifndef KIO_NO_NEPOMUK 00187 qDeleteAll(m_metaDataReaders); 00188 #endif 00189 } 00190 00191 void KFileMetaDataProvider::Private::slotLoadingFinished() 00192 { 00193 #ifndef KIO_NO_NEPOMUK 00194 KFileMetaDataReader* finishedMetaDataReader = qobject_cast<KFileMetaDataReader*>(q->sender()); 00195 // The process that has emitted the finished() signal 00196 // will get deleted and removed from m_metaDataReaders. 00197 for (int i = 0; i < m_metaDataReaders.count(); ++i) { 00198 KFileMetaDataReader* metaDataReader = m_metaDataReaders[i]; 00199 if (metaDataReader == finishedMetaDataReader) { 00200 m_metaDataReaders.removeAt(i); 00201 if (metaDataReader != m_latestMetaDataReader) { 00202 // Ignore data of older processs, as the data got 00203 // obsolete by m_latestMetaDataReader. 00204 metaDataReader->deleteLater(); 00205 return; 00206 } 00207 } 00208 } 00209 00210 m_data = m_latestMetaDataReader->metaData(); 00211 m_latestMetaDataReader->deleteLater(); 00212 00213 if (m_fileItems.count() == 1) { 00214 // TODO: Handle case if remote URLs are used properly. isDir() does 00215 // not work, the modification date needs also to be adjusted... 00216 const KFileItem& item = m_fileItems.first(); 00217 00218 if (item.isDir()) { 00219 const int count = subDirectoriesCount(item.url().pathOrUrl()); 00220 if (count == -1) { 00221 m_data.insert(KUrl("kfileitem#size"), QString("Unknown")); 00222 } else { 00223 const QString itemCountString = i18ncp("@item:intable", "%1 item", "%1 items", count); 00224 m_data.insert(KUrl("kfileitem#size"), itemCountString); 00225 } 00226 } else { 00227 m_data.insert(KUrl("kfileitem#size"), KIO::convertSize(item.size())); 00228 } 00229 m_data.insert(KUrl("kfileitem#type"), item.mimeComment()); 00230 m_data.insert(KUrl("kfileitem#modified"), KGlobal::locale()->formatDateTime(item.time(KFileItem::ModificationTime), KLocale::FancyLongDate)); 00231 m_data.insert(KUrl("kfileitem#owner"), item.user()); 00232 m_data.insert(KUrl("kfileitem#permissions"), item.permissionsString()); 00233 } else if (m_fileItems.count() > 1) { 00234 // Calculate the size of all items 00235 quint64 totalSize = 0; 00236 foreach (const KFileItem& item, m_fileItems) { 00237 if (!item.isDir() && !item.isLink()) { 00238 totalSize += item.size(); 00239 } 00240 } 00241 m_data.insert(KUrl("kfileitem#totalSize"), KIO::convertSize(totalSize)); 00242 } 00243 #endif 00244 00245 emit q->loadingFinished(); 00246 } 00247 00248 void KFileMetaDataProvider::Private::slotRatingChanged(unsigned int rating) 00249 { 00250 #ifndef KIO_NO_NEPOMUK 00251 Nepomuk::MassUpdateJob* job = Nepomuk::MassUpdateJob::rateResources(resourceList(), rating); 00252 startChangeDataJob(job); 00253 #else 00254 Q_UNUSED(rating); 00255 #endif 00256 } 00257 00258 void KFileMetaDataProvider::Private::slotTagsChanged(const QList<Nepomuk::Tag>& tags) 00259 { 00260 #ifndef KIO_NO_NEPOMUK 00261 if (!m_tagWidget.isNull()) { 00262 m_tagWidget.data()->setSelectedTags(tags); 00263 00264 Nepomuk::MassUpdateJob* job = Nepomuk::MassUpdateJob::tagResources(resourceList(), tags); 00265 startChangeDataJob(job); 00266 } 00267 #else 00268 Q_UNUSED(tags); 00269 #endif 00270 } 00271 00272 void KFileMetaDataProvider::Private::slotCommentChanged(const QString& comment) 00273 { 00274 #ifndef KIO_NO_NEPOMUK 00275 Nepomuk::MassUpdateJob* job = Nepomuk::MassUpdateJob::commentResources(resourceList(), comment); 00276 startChangeDataJob(job); 00277 #else 00278 Q_UNUSED(comment); 00279 #endif 00280 } 00281 00282 void KFileMetaDataProvider::Private::slotTagClicked(const Nepomuk::Tag& tag) 00283 { 00284 #ifndef KIO_NO_NEPOMUK 00285 emit q->urlActivated(tag.resourceUri()); 00286 #else 00287 Q_UNUSED(tag); 00288 #endif 00289 } 00290 00291 void KFileMetaDataProvider::Private::slotLinkActivated(const QString& link) 00292 { 00293 emit q->urlActivated(KUrl(link)); 00294 } 00295 00296 void KFileMetaDataProvider::Private::startChangeDataJob(KJob* job) 00297 { 00298 connect(job, SIGNAL(result(KJob*)), 00299 q, SIGNAL(dataChangeFinished())); 00300 emit q->dataChangeStarted(); 00301 job->start(); 00302 } 00303 00304 #ifndef KIO_NO_NEPOMUK 00305 QList<Nepomuk::Resource> KFileMetaDataProvider::Private::resourceList() const 00306 { 00307 QList<Nepomuk::Resource> list; 00308 foreach (const KFileItem& item, m_fileItems) { 00309 const KUrl url = item.nepomukUri(); 00310 if(url.isValid()) 00311 list.append(Nepomuk::Resource(url)); 00312 } 00313 return list; 00314 } 00315 00316 QWidget* KFileMetaDataProvider::Private::createRatingWidget(int rating, QWidget* parent) 00317 { 00318 KRatingWidget* ratingWidget = new KRatingWidget(parent); 00319 const Qt::Alignment align = (ratingWidget->layoutDirection() == Qt::LeftToRight) ? 00320 Qt::AlignLeft : Qt::AlignRight; 00321 ratingWidget->setAlignment(align); 00322 ratingWidget->setRating(rating); 00323 const QFontMetrics metrics(parent->font()); 00324 ratingWidget->setPixmapSize(metrics.height()); 00325 00326 connect(ratingWidget, SIGNAL(ratingChanged(unsigned int)), 00327 q, SLOT(slotRatingChanged(unsigned int))); 00328 00329 m_ratingWidget = ratingWidget; 00330 00331 return ratingWidget; 00332 } 00333 00334 QWidget* KFileMetaDataProvider::Private::createTagWidget(const QList<Nepomuk::Tag>& tags, QWidget* parent) 00335 { 00336 Nepomuk::TagWidget* tagWidget = new Nepomuk::TagWidget(parent); 00337 tagWidget->setModeFlags(m_readOnly 00338 ? Nepomuk::TagWidget::MiniMode | Nepomuk::TagWidget::ReadOnly 00339 : Nepomuk::TagWidget::MiniMode); 00340 tagWidget->setSelectedTags(tags); 00341 00342 connect(tagWidget, SIGNAL(selectionChanged(QList<Nepomuk::Tag>)), 00343 q, SLOT(slotTagsChanged(QList<Nepomuk::Tag>))); 00344 connect(tagWidget, SIGNAL(tagClicked(Nepomuk::Tag)), 00345 q, SLOT(slotTagClicked(Nepomuk::Tag))); 00346 00347 m_tagWidget = tagWidget; 00348 00349 return tagWidget; 00350 } 00351 00352 QWidget* KFileMetaDataProvider::Private::createCommentWidget(const QString& comment, QWidget* parent) 00353 { 00354 KCommentWidget* commentWidget = new KCommentWidget(parent); 00355 commentWidget->setText(comment); 00356 commentWidget->setReadOnly(m_readOnly); 00357 00358 connect(commentWidget, SIGNAL(commentChanged(const QString&)), 00359 q, SLOT(slotCommentChanged(const QString&))); 00360 00361 m_commentWidget = commentWidget; 00362 00363 return commentWidget; 00364 } 00365 #endif 00366 00367 QWidget* KFileMetaDataProvider::Private::createValueWidget(const QString& value, QWidget* parent) 00368 { 00369 ValueWidget* valueWidget = new ValueWidget(parent); 00370 valueWidget->setWordWrap(true); 00371 valueWidget->setAlignment(Qt::AlignTop | Qt::AlignLeft); 00372 valueWidget->setText(m_readOnly ? plainText(value) : value); 00373 connect(valueWidget, SIGNAL(linkActivated(QString)), q, SLOT(slotLinkActivated(QString))); 00374 return valueWidget; 00375 } 00376 00377 KFileMetaDataProvider::KFileMetaDataProvider(QObject* parent) : 00378 QObject(parent), 00379 d(new Private(this)) 00380 { 00381 } 00382 00383 KFileMetaDataProvider::~KFileMetaDataProvider() 00384 { 00385 delete d; 00386 } 00387 00388 void KFileMetaDataProvider::setItems(const KFileItemList& items) 00389 { 00390 d->m_fileItems = items; 00391 00392 #ifndef KIO_NO_NEPOMUK 00393 if (items.isEmpty()) { 00394 return; 00395 } 00396 Q_PRIVATE_SLOT(d, void slotDataChangeStarted()) 00397 Q_PRIVATE_SLOT(d, void slotDataChangeFinished()) 00398 QList<KUrl> urls; 00399 foreach (const KFileItem& item, items) { 00400 const KUrl url = item.nepomukUri(); 00401 if (url.isValid()) { 00402 urls.append(url); 00403 } 00404 } 00405 00406 d->m_latestMetaDataReader = new KFileMetaDataReader(urls); 00407 connect(d->m_latestMetaDataReader, SIGNAL(finished()), this, SLOT(slotLoadingFinished())); 00408 d->m_metaDataReaders.append(d->m_latestMetaDataReader); 00409 d->m_latestMetaDataReader->start(); 00410 #endif 00411 } 00412 00413 QString KFileMetaDataProvider::label(const KUrl& metaDataUri) const 00414 { 00415 struct TranslationItem { 00416 const char* const key; 00417 const char* const context; 00418 const char* const value; 00419 }; 00420 00421 static const TranslationItem translations[] = { 00422 { "kfileitem#comment", I18N_NOOP2_NOSTRIP("@label", "Comment") }, 00423 { "kfileitem#modified", I18N_NOOP2_NOSTRIP("@label", "Modified") }, 00424 { "kfileitem#owner", I18N_NOOP2_NOSTRIP("@label", "Owner") }, 00425 { "kfileitem#permissions", I18N_NOOP2_NOSTRIP("@label", "Permissions") }, 00426 { "kfileitem#rating", I18N_NOOP2_NOSTRIP("@label", "Rating") }, 00427 { "kfileitem#size", I18N_NOOP2_NOSTRIP("@label", "Size") }, 00428 { "kfileitem#tags", I18N_NOOP2_NOSTRIP("@label", "Tags") }, 00429 { "kfileitem#totalSize", I18N_NOOP2_NOSTRIP("@label", "Total Size") }, 00430 { "kfileitem#type", I18N_NOOP2_NOSTRIP("@label", "Type") }, 00431 { 0, 0, 0} // Mandatory last entry 00432 }; 00433 00434 static QHash<QString, QString> hash; 00435 if (hash.isEmpty()) { 00436 const TranslationItem* item = &translations[0]; 00437 while (item->key != 0) { 00438 hash.insert(item->key, i18nc(item->context, item->value)); 00439 ++item; 00440 } 00441 } 00442 00443 QString value = hash.value(metaDataUri.url()); 00444 if (value.isEmpty()) { 00445 value = KNfoTranslator::instance().translation(metaDataUri); 00446 } 00447 00448 return value; 00449 } 00450 00451 QString KFileMetaDataProvider::group(const KUrl& metaDataUri) const 00452 { 00453 QString group; // return value 00454 00455 const QString uri = metaDataUri.url(); 00456 if (uri == QLatin1String("kfileitem#type")) { 00457 group = QLatin1String("0FileItemA"); 00458 } else if (uri == QLatin1String("kfileitem#size")) { 00459 group = QLatin1String("0FileItemB"); 00460 } else if (uri == QLatin1String("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width")) { 00461 group = QLatin1String("0SizeA"); 00462 } else if (uri == QLatin1String("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height")) { 00463 group = QLatin1String("0SizeB"); 00464 } 00465 00466 return group; 00467 } 00468 00469 KFileItemList KFileMetaDataProvider::items() const 00470 { 00471 return d->m_fileItems; 00472 } 00473 00474 void KFileMetaDataProvider::setReadOnly(bool readOnly) 00475 { 00476 d->m_readOnly = readOnly; 00477 } 00478 00479 bool KFileMetaDataProvider::isReadOnly() const 00480 { 00481 return d->m_readOnly; 00482 } 00483 00484 #ifndef KIO_NO_NEPOMUK 00485 QHash<KUrl, Nepomuk::Variant> KFileMetaDataProvider::data() const 00486 { 00487 return d->m_data; 00488 } 00489 00490 QWidget* KFileMetaDataProvider::createValueWidget(const KUrl& metaDataUri, 00491 const Nepomuk::Variant& value, 00492 QWidget* parent) const 00493 { 00494 Q_ASSERT(parent != 0); 00495 QWidget* widget = 0; 00496 00497 if (d->m_nepomukActivated) { 00498 const QString uri = metaDataUri.url(); 00499 if (uri == QLatin1String("kfileitem#rating")) { 00500 widget = d->createRatingWidget(value.toInt(), parent); 00501 } else if (uri == QLatin1String("kfileitem#tags")) { 00502 const QStringList tagNames = value.toStringList(); 00503 QList<Nepomuk::Tag> tags; 00504 foreach (const QString& tagName, tagNames) { 00505 tags.append(Nepomuk::Tag(tagName)); 00506 } 00507 00508 widget = d->createTagWidget(tags, parent); 00509 } else if (uri == QLatin1String("kfileitem#comment")) { 00510 widget = d->createCommentWidget(value.toString(), parent); 00511 } 00512 } 00513 00514 if (widget == 0) { 00515 widget = d->createValueWidget(value.toString(), parent); 00516 } 00517 00518 widget->setForegroundRole(parent->foregroundRole()); 00519 widget->setFont(parent->font()); 00520 00521 return widget; 00522 } 00523 #endif 00524 00525 int KFileMetaDataProvider::Private::subDirectoriesCount(const QString& path) 00526 { 00527 #ifdef Q_WS_WIN 00528 QDir dir(path); 00529 return dir.entryList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::System).count(); 00530 #else 00531 // Taken from kdelibs/kio/kio/kdirmodel.cpp 00532 // Copyright (C) 2006 David Faure <faure@kde.org> 00533 00534 int count = -1; 00535 DIR* dir = ::opendir(QFile::encodeName(path)); 00536 if (dir) { 00537 count = 0; 00538 struct dirent *dirEntry = 0; 00539 while ((dirEntry = ::readdir(dir))) { // krazy:exclude=syscalls 00540 if (dirEntry->d_name[0] == '.') { 00541 if (dirEntry->d_name[1] == '\0') { 00542 // Skip "." 00543 continue; 00544 } 00545 if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') { 00546 // Skip ".." 00547 continue; 00548 } 00549 } 00550 ++count; 00551 } 00552 ::closedir(dir); 00553 } 00554 return count; 00555 #endif 00556 } 00557 00558 #include "kfilemetadataprovider_p.moc"
KDE 4.7 API Reference