]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/information/metadatawidget.cpp
SVN_SILENT made messages (.desktop file)
[dolphin.git] / src / panels / information / metadatawidget.cpp
1 /***************************************************************************
2 * Copyright (C) 2008 by Sebastian Trueg <trueg@kde.org> *
3 * Copyright (C) 2009 by Peter Penz <peter.penz@gmx.at> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include "metadatawidget.h"
22
23 #include <kfileitem.h>
24 #include <kglobalsettings.h>
25 #include <klocale.h>
26
27 #include <QFontMetrics>
28 #include <QGridLayout>
29 #include <QLabel>
30 #include <QList>
31 #include <QString>
32
33 #include <config-nepomuk.h>
34 #ifdef HAVE_NEPOMUK
35 #define DISABLE_NEPOMUK_LEGACY
36
37 #include "commentwidget_p.h"
38 #include "nepomukmassupdatejob_p.h"
39 #include "taggingwidget_p.h"
40
41 #include <kconfig.h>
42 #include <kconfiggroup.h>
43
44 #include <Nepomuk/KRatingWidget>
45 #include <Nepomuk/Resource>
46 #include <Nepomuk/Tag>
47 #include <Nepomuk/Types/Property>
48 #include <Nepomuk/Variant>
49
50 #include <Soprano/Vocabulary/Xesam>
51 #include <QMutex>
52 #include <QSpacerItem>
53 #include <QThread>
54 #endif
55
56 class MetaDataWidget::Private
57 {
58 public:
59 struct Row
60 {
61 QLabel* label;
62 QWidget* infoWidget;
63 };
64
65 Private(MetaDataWidget* parent);
66 ~Private();
67
68 void addRow(QLabel* label, QWidget* infoWidget);
69 void removeMetaInfoRows();
70 void setRowVisible(QWidget* infoWidget, bool visible);
71
72 void slotLoadingFinished();
73
74 QList<Row> m_rows;
75
76 QGridLayout* m_gridLayout;
77
78 QLabel* m_typeInfo;
79 QLabel* m_sizeLabel;
80 QLabel* m_sizeInfo;
81 QLabel* m_modifiedInfo;
82 QLabel* m_ownerInfo;
83 QLabel* m_permissionsInfo;
84
85 #ifdef HAVE_NEPOMUK
86 KRatingWidget* m_ratingWidget;
87 TaggingWidget* m_taggingWidget;
88 CommentWidget* m_commentWidget;
89
90 // shared data between the GUI-thread and
91 // the loader-thread (see LoadFilesThread):
92 QMutex m_mutex;
93 struct SharedData
94 {
95 int rating;
96 QString comment;
97 QList<Nepomuk::Tag> tags;
98 QList<QString> metaInfoLabels;
99 QList<QString> metaInfoValues;
100 } m_sharedData;
101
102 /**
103 * Loads the meta data of files and writes
104 * the result into a shared data pool that
105 * can be used by the widgets in the GUI thread.
106 */
107 class LoadFilesThread : public QThread
108 {
109 public:
110 LoadFilesThread(SharedData* m_sharedData, QMutex* m_mutex);
111 virtual ~LoadFilesThread();
112 void loadFiles(const KUrl::List& urls);
113 virtual void run();
114
115 private:
116 /**
117 * Assures that the settings for the meta information
118 * are initialized with proper default values.
119 */
120 void initMetaInfoSettings(KConfigGroup& group);
121
122 /**
123 * Temporary helper method for KDE 4.3 as we currently don't get
124 * translated labels for Nepmok literals: Replaces camelcase labels
125 * like "fileLocation" by "File Location:".
126 */
127 QString tunedLabel(const QString& label) const;
128
129 private:
130 SharedData* m_m_sharedData;
131 QMutex* m_m_mutex;
132 KUrl::List m_urls;
133 bool m_canceled;
134 };
135
136 LoadFilesThread* m_loadFilesThread;
137 #endif
138
139 private:
140 MetaDataWidget* const q;
141 };
142
143 MetaDataWidget::Private::Private(MetaDataWidget* parent) :
144 m_rows(),
145 m_gridLayout(0),
146 m_typeInfo(0),
147 m_sizeLabel(0),
148 m_sizeInfo(0),
149 m_modifiedInfo(0),
150 m_ownerInfo(0),
151 m_permissionsInfo(0),
152 #ifdef HAVE_NEPOMUK
153 m_ratingWidget(0),
154 m_taggingWidget(0),
155 m_commentWidget(0),
156 m_loadFilesThread(0),
157 #endif
158 q(parent)
159 {
160 m_gridLayout = new QGridLayout(parent);
161 m_gridLayout->setMargin(0);
162
163 m_typeInfo = new QLabel(parent);
164 m_sizeLabel = new QLabel(parent);
165 m_sizeInfo = new QLabel(parent);
166 m_modifiedInfo = new QLabel(parent);
167 m_ownerInfo = new QLabel(parent);
168 m_permissionsInfo = new QLabel(parent);
169 #ifdef HAVE_NEPOMUK
170 const QFontMetrics fontMetrics(KGlobalSettings::smallestReadableFont());
171 m_ratingWidget = new KRatingWidget(parent);
172 m_ratingWidget->setFixedHeight(fontMetrics.height());
173
174 m_taggingWidget = new TaggingWidget(parent);
175
176 m_commentWidget = new CommentWidget(parent);
177 #endif
178
179 addRow(new QLabel(i18nc("@label", "Type:"), parent), m_typeInfo);
180 addRow(m_sizeLabel, m_sizeInfo);
181 addRow(new QLabel(i18nc("@label", "Modified:"), parent), m_modifiedInfo);
182 addRow(new QLabel(i18nc("@label", "Owner:"), parent), m_ownerInfo);
183 addRow(new QLabel(i18nc("@label", "Permissions:"), parent), m_permissionsInfo);
184 #ifdef HAVE_NEPOMUK
185 addRow(new QLabel(i18nc("@label", "Rating:"), parent), m_ratingWidget);
186 addRow(new QLabel(i18nc("@label", "Tags:"), parent), m_taggingWidget);
187 addRow(new QLabel(i18nc("@label", "Comment:"), parent), m_commentWidget);
188
189 m_sharedData.rating = 0;
190 m_loadFilesThread = new LoadFilesThread(&m_sharedData, &m_mutex);
191 connect(m_loadFilesThread, SIGNAL(finished()), q, SLOT(slotLoadingFinished()));
192 #endif
193 }
194
195 MetaDataWidget::Private::~Private()
196 {
197 #ifdef HAVE_NEPOMUK
198 delete m_loadFilesThread;
199 #endif
200 }
201
202 void MetaDataWidget::Private::addRow(QLabel* label, QWidget* infoWidget)
203 {
204 Row row;
205 row.label = label;
206 row.infoWidget = infoWidget;
207 m_rows.append(row);
208
209 const QFont smallFont = KGlobalSettings::smallestReadableFont();
210 // use a brighter color for the label and a small font size
211 QPalette palette = label->palette();
212 QColor textColor = palette.color(QPalette::Text);
213 textColor.setAlpha(128);
214 palette.setColor(QPalette::WindowText, textColor);
215 label->setPalette(palette);
216 label->setFont(smallFont);
217 label->setWordWrap(true);
218 label->setAlignment(Qt::AlignTop | Qt::AlignRight);
219
220 QLabel* infoLabel = qobject_cast<QLabel*>(infoWidget);
221 if (infoLabel != 0) {
222 infoLabel->setFont(smallFont);
223 infoLabel->setWordWrap(true);
224 infoLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
225 }
226
227 // add the row to grid layout
228 const int rowIndex = m_rows.count();
229 m_gridLayout->addWidget(label, rowIndex, 0, Qt::AlignRight);
230 const int spacerWidth = QFontMetrics(smallFont).size(Qt::TextSingleLine, " ").width();
231 m_gridLayout->addItem(new QSpacerItem(spacerWidth, 1), rowIndex, 1);
232 m_gridLayout->addWidget(infoWidget, rowIndex, 2, Qt::AlignLeft);
233 }
234
235 void MetaDataWidget::Private::setRowVisible(QWidget* infoWidget, bool visible)
236 {
237 foreach (const Row& row, m_rows) {
238 if (row.infoWidget == infoWidget) {
239 row.label->setVisible(visible);
240 row.infoWidget->setVisible(visible);
241 return;
242 }
243 }
244 }
245
246 void MetaDataWidget::Private::slotLoadingFinished()
247 {
248 #ifdef HAVE_NEPOMUK
249 QMutexLocker locker(&m_mutex);
250 m_ratingWidget->setRating(m_sharedData.rating);
251 m_commentWidget->setText(m_sharedData.comment);
252 m_taggingWidget->setTags(m_sharedData.tags);
253
254 // Show the remaining meta information as text. The number
255 // of required rows may very. Existing rows are reused to
256 // prevent flickering.
257 int index = 8; // TODO: don't hardcode this value here
258 const int rowCount = m_rows.count();
259 Q_ASSERT(rowCount >= index);
260
261 Q_ASSERT(m_sharedData.metaInfoLabels.count() == m_sharedData.metaInfoValues.count());
262 const int metaInfoCount = m_sharedData.metaInfoLabels.count();
263 for (int i = 0; i < metaInfoCount; ++i) {
264 if (index < rowCount) {
265 // adjust texts of the current row
266 m_rows[index].label->setText(m_sharedData.metaInfoLabels[i]);
267 QLabel* infoValueLabel = qobject_cast<QLabel*>(m_rows[index].infoWidget);
268 Q_ASSERT(infoValueLabel != 0);
269 infoValueLabel->setText(m_sharedData.metaInfoValues[i]);
270 } else {
271 // create new row
272 QLabel* infoLabel = new QLabel(m_sharedData.metaInfoLabels[i], q);
273 QLabel* infoValue = new QLabel(m_sharedData.metaInfoValues[i], q);
274 addRow(infoLabel, infoValue);
275 }
276 ++index;
277 }
278 if (metaInfoCount > 0) {
279 --index;
280 }
281
282 // remove rows that are not needed anymore
283 for (int i = rowCount - 1; i > index; --i) {
284 delete m_rows[i].label;
285 delete m_rows[i].infoWidget;
286 m_rows.pop_back();
287 }
288 #endif
289 }
290
291 #ifdef HAVE_NEPOMUK
292 MetaDataWidget::Private::LoadFilesThread::LoadFilesThread(
293 MetaDataWidget::Private::SharedData* m_sharedData,
294 QMutex* m_mutex) :
295 m_m_sharedData(m_sharedData),
296 m_m_mutex(m_mutex),
297 m_urls(),
298 m_canceled(false)
299 {
300 }
301
302 MetaDataWidget::Private::LoadFilesThread::~LoadFilesThread()
303 {
304 // This thread may very well be deleted during execution. We need
305 // to protect it from crashes here.
306 m_canceled = true;
307 wait();
308 }
309
310 void MetaDataWidget::Private::LoadFilesThread::loadFiles(const KUrl::List& urls)
311 {
312 QMutexLocker locker(m_m_mutex);
313 m_urls = urls;
314 m_canceled = false;
315 start();
316 }
317
318 void MetaDataWidget::Private::LoadFilesThread::run()
319 {
320 QMutexLocker locker(m_m_mutex);
321 const KUrl::List urls = m_urls;
322 locker.unlock();
323
324 KConfig config("kmetainformationrc", KConfig::NoGlobals);
325 KConfigGroup settings = config.group("Show");
326 initMetaInfoSettings(settings);
327
328 bool first = true;
329 unsigned int rating = 0;
330 QString comment;
331 QList<Nepomuk::Tag> tags;
332 QList<QString> metaInfoLabels;
333 QList<QString> metaInfoValues;
334 foreach (const KUrl& url, urls) {
335 if (m_canceled) {
336 return;
337 }
338
339 Nepomuk::Resource file(url, Soprano::Vocabulary::Xesam::File());
340
341 if (!first && (rating != file.rating())) {
342 rating = 0; // reset rating
343 } else if (first) {
344 rating = file.rating();
345 }
346
347 if (!first && (comment != file.description())) {
348 comment.clear(); // reset comment
349 } else if (first) {
350 comment = file.description();
351 }
352
353 if (!first && (tags != file.tags())) {
354 tags.clear(); // reset tags
355 } else if (first) {
356 tags = file.tags();
357 }
358
359 if (first && (urls.count() == 1)) {
360 // TODO: show shared meta informations instead
361 // of not showing anything on multiple selections
362 QHash<QUrl, Nepomuk::Variant> properties = file.properties();
363 QHash<QUrl, Nepomuk::Variant>::const_iterator it = properties.constBegin();
364 while (it != properties.constEnd()) {
365 Nepomuk::Types::Property prop(it.key());
366 if (true /*settings.readEntry(prop.name(), true)*/) {
367 // TODO #1: use Nepomuk::formatValue(res, prop) if available
368 // instead of it.value().toString()
369 // TODO #2: using tunedLabel() is a workaround for KDE 4.3 until
370 // we get translated labels
371 metaInfoLabels.append(tunedLabel(prop.label()));
372 metaInfoValues.append(it.value().toString());
373 }
374 ++it;
375 }
376 }
377
378 first = false;
379 }
380
381 locker.relock();
382 m_m_sharedData->rating = rating;
383 m_m_sharedData->comment = comment;
384 m_m_sharedData->tags = tags;
385 m_m_sharedData->metaInfoLabels = metaInfoLabels;
386 m_m_sharedData->metaInfoValues = metaInfoValues;
387 }
388
389 void MetaDataWidget::Private::LoadFilesThread::initMetaInfoSettings(KConfigGroup& group)
390 {
391 if (!group.readEntry("initialized", false)) {
392 // The resource file is read the first time. Assure
393 // that some meta information is disabled per default.
394
395 static const char* disabledProperties[] = {
396 "asText", "contentSize", "depth", "fileExtension",
397 "fileName", "fileSize", "isPartOf", "mimetype", "name",
398 "parentUrl", "plainTextContent", "sourceModified",
399 "size", "url",
400 0 // mandatory last entry
401 };
402
403 int i = 0;
404 while (disabledProperties[i] != 0) {
405 group.writeEntry(disabledProperties[i], false);
406 ++i;
407 }
408
409 // mark the group as initialized
410 group.writeEntry("initialized", true);
411 }
412 }
413
414 QString MetaDataWidget::Private::LoadFilesThread::tunedLabel(const QString& label) const
415 {
416 QString tunedLabel;
417 const int labelLength = label.length();
418 if (labelLength > 0) {
419 tunedLabel.reserve(labelLength);
420 tunedLabel = label[0].toUpper();
421 for (int i = 1; i < labelLength; ++i) {
422 if (label[i].isUpper() && !label[i - 1].isSpace() && !label[i - 1].isUpper()) {
423 tunedLabel += ' ';
424 tunedLabel += label[i].toLower();
425 } else {
426 tunedLabel += label[i];
427 }
428 }
429 }
430 return tunedLabel + ':';
431 }
432
433 #endif
434
435 MetaDataWidget::MetaDataWidget(QWidget* parent) :
436 QWidget(parent),
437 d(new Private(this))
438 {
439 }
440
441 MetaDataWidget::~MetaDataWidget()
442 {
443 delete d;
444 }
445
446 void MetaDataWidget::setItem(const KFileItem& item)
447 {
448 // update values for "type", "size", "modified",
449 // "owner" and "permissions" synchronously
450 d->m_sizeLabel->setText(i18nc("@label", "Size:"));
451 if (item.isDir()) {
452 d->m_typeInfo->setText(i18nc("@label", "Folder"));
453 d->setRowVisible(d->m_sizeInfo, false);
454 } else {
455 d->m_typeInfo->setText(item.mimeComment());
456 d->m_sizeInfo->setText(KIO::convertSize(item.size()));
457 d->setRowVisible(d->m_sizeInfo, true);
458 }
459 d->m_modifiedInfo->setText(item.timeString());
460 d->m_ownerInfo->setText(item.user());
461 d->m_permissionsInfo->setText(item.permissionsString());
462
463 setItems(KFileItemList() << item);
464 }
465
466 void MetaDataWidget::setItems(const KFileItemList& items)
467 {
468 if (items.count() > 1) {
469 // calculate the size of all items and show this
470 // information to the user
471 d->m_sizeLabel->setText(i18nc("@label", "Total Size:"));
472 d->setRowVisible(d->m_sizeInfo, true);
473
474 quint64 totalSize = 0;
475 foreach (const KFileItem& item, items) {
476 if (!item.isDir() && !item.isLink()) {
477 totalSize += item.size();
478 }
479 }
480 d->m_sizeInfo->setText(KIO::convertSize(totalSize));
481 }
482
483 #ifdef HAVE_NEPOMUK
484 QList<KUrl> urls;
485 foreach (const KFileItem& item, items) {
486 const KUrl url = item.nepomukUri();
487 if (url.isValid()) {
488 urls.append(url);
489 }
490 }
491 d->m_loadFilesThread->loadFiles(urls);
492 #endif
493 }
494
495 #include "metadatawidget.moc"