]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/information/kmetadatawidget.cpp
When the Back, Forward and Up buttons in the toolbar are clicked with
[dolphin.git] / src / panels / information / kmetadatawidget.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 library is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU Library General Public *
7 * License version 2 as published by the Free Software Foundation. *
8 * *
9 * This library is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Library General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Library General Public License *
15 * along with this library; see the file COPYING.LIB. If not, write to *
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301, USA. *
18 *****************************************************************************/
19
20 #include "kmetadatawidget.h"
21
22 #include <kconfig.h>
23 #include <kconfiggroup.h>
24 #include <kfileitem.h>
25 #include <kglobalsettings.h>
26 #include <kglobal.h>
27 #include <klocale.h>
28
29 #include <QFontMetrics>
30 #include <QGridLayout>
31 #include <QLabel>
32 #include <QList>
33 #include <QString>
34
35 #include <config-nepomuk.h>
36 #ifdef HAVE_NEPOMUK
37 #define DISABLE_NEPOMUK_LEGACY
38
39 #include "kcommentwidget_p.h"
40 #include "kloadmetadatathread_p.h"
41 #include "ktaggingwidget_p.h"
42
43 #include <nepomuk/kratingwidget.h>
44 #include <nepomuk/resource.h>
45 #include <nepomuk/resourcemanager.h>
46 #include <nepomuk/property.h>
47 #include <nepomuk/tag.h>
48 #include <nepomuk/variant.h>
49 #include "nepomukmassupdatejob_p.h"
50
51 #include <QMutex>
52 #include <QSpacerItem>
53 #include <QThread>
54 #else
55 namespace Nepomuk
56 {
57 typedef int Tag;
58 }
59 #endif
60
61 class KMetaDataWidget::Private
62 {
63 public:
64 struct Row
65 {
66 QLabel* label;
67 QWidget* infoWidget;
68 };
69
70 Private(KMetaDataWidget* parent);
71 ~Private();
72
73 void addRow(QLabel* label, QWidget* infoWidget);
74 void removeMetaInfoRows();
75 void setRowVisible(QWidget* infoWidget, bool visible);
76
77 /**
78 * Initializes the configuration file "kmetainformationrc"
79 * with proper default settings for the first start in
80 * an uninitialized environment.
81 */
82 void initMetaInfoSettings();
83
84 /**
85 * Parses the configuration file "kmetainformationrc" and
86 * updates the visibility of all rows.
87 */
88 void updateRowsVisibility();
89
90 void slotLoadingFinished();
91
92 void slotRatingChanged(unsigned int rating);
93 void slotTagsChanged(const QList<Nepomuk::Tag>& tags);
94 void slotCommentChanged(const QString& comment);
95
96 void slotMetaDataUpdateDone();
97
98 /**
99 * Disables the metadata widget and starts the job that
100 * changes the meta data asynchronously. After the job
101 * has been finished, the metadata widget gets enabled again.
102 */
103 void startChangeDataJob(KJob* job);
104
105 bool m_isSizeVisible;
106 MetaDataTypes m_visibleDataTypes;
107 QList<KFileItem> m_fileItems;
108 QList<Row> m_rows;
109
110 QGridLayout* m_gridLayout;
111
112 QLabel* m_typeInfo;
113 QLabel* m_sizeLabel;
114 QLabel* m_sizeInfo;
115 QLabel* m_modifiedInfo;
116 QLabel* m_ownerInfo;
117 QLabel* m_permissionsInfo;
118
119 #ifdef HAVE_NEPOMUK
120 KRatingWidget* m_ratingWidget;
121 KTaggingWidget* m_taggingWidget;
122 KCommentWidget* m_commentWidget;
123
124 QMap<KUrl, Nepomuk::Resource> m_files;
125
126 KLoadMetaDataThread* m_loadMetaDataThread;
127 #endif
128
129 private:
130 KMetaDataWidget* const q;
131 };
132
133 KMetaDataWidget::Private::Private(KMetaDataWidget* parent) :
134 m_isSizeVisible(true),
135 m_visibleDataTypes(TypeData | SizeData | ModifiedData | OwnerData |
136 PermissionsData | RatingData | TagsData | CommentData),
137 m_fileItems(),
138 m_rows(),
139 m_gridLayout(0),
140 m_typeInfo(0),
141 m_sizeLabel(0),
142 m_sizeInfo(0),
143 m_modifiedInfo(0),
144 m_ownerInfo(0),
145 m_permissionsInfo(0),
146 #ifdef HAVE_NEPOMUK
147 m_ratingWidget(0),
148 m_taggingWidget(0),
149 m_commentWidget(0),
150 m_files(),
151 m_loadMetaDataThread(0),
152 #endif
153 q(parent)
154 {
155 const QFontMetrics fontMetrics(KGlobalSettings::smallestReadableFont());
156
157 m_gridLayout = new QGridLayout(parent);
158 m_gridLayout->setMargin(0);
159 m_gridLayout->setSpacing(fontMetrics.height() / 4);
160
161 m_typeInfo = new QLabel(parent);
162 m_sizeLabel = new QLabel(parent);
163 m_sizeInfo = new QLabel(parent);
164 m_modifiedInfo = new QLabel(parent);
165 m_ownerInfo = new QLabel(parent);
166 m_permissionsInfo = new QLabel(parent);
167
168 addRow(new QLabel(i18nc("@label", "Type:"), parent), m_typeInfo);
169 addRow(m_sizeLabel, m_sizeInfo);
170 addRow(new QLabel(i18nc("@label", "Modified:"), parent), m_modifiedInfo);
171 addRow(new QLabel(i18nc("@label", "Owner:"), parent), m_ownerInfo);
172 addRow(new QLabel(i18nc("@label", "Permissions:"), parent), m_permissionsInfo);
173
174 #ifdef HAVE_NEPOMUK
175 if (Nepomuk::ResourceManager::instance()->init() == 0) {
176 m_ratingWidget = new KRatingWidget(parent);
177 m_ratingWidget->setFixedHeight(fontMetrics.height());
178 connect(m_ratingWidget, SIGNAL(ratingChanged(unsigned int)),
179 q, SLOT(slotRatingChanged(unsigned int)));
180
181 m_taggingWidget = new KTaggingWidget(parent);
182 connect(m_taggingWidget, SIGNAL(tagsChanged(const QList<Nepomuk::Tag>&)),
183 q, SLOT(slotTagsChanged(const QList<Nepomuk::Tag>&)));
184
185 m_commentWidget = new KCommentWidget(parent);
186 connect(m_commentWidget, SIGNAL(commentChanged(const QString&)),
187 q, SLOT(slotCommentChanged(const QString&)));
188
189 addRow(new QLabel(i18nc("@label", "Rating:"), parent), m_ratingWidget);
190 addRow(new QLabel(i18nc("@label", "Tags:"), parent), m_taggingWidget);
191 addRow(new QLabel(i18nc("@label", "Comment:"), parent), m_commentWidget);
192 }
193 #endif
194
195 initMetaInfoSettings();
196 updateRowsVisibility();
197 }
198
199 KMetaDataWidget::Private::~Private()
200 {
201 #ifdef HAVE_NEPOMUK
202 if (m_loadMetaDataThread != 0) {
203 disconnect(m_loadMetaDataThread, SIGNAL(finished()), q, SLOT(slotLoadingFinished()));
204 m_loadMetaDataThread->cancelAndDelete();
205 m_loadMetaDataThread = 0;
206 }
207 #endif
208 }
209
210 void KMetaDataWidget::Private::addRow(QLabel* label, QWidget* infoWidget)
211 {
212 Row row;
213 row.label = label;
214 row.infoWidget = infoWidget;
215 m_rows.append(row);
216
217 const QFont smallFont = KGlobalSettings::smallestReadableFont();
218 // use a brighter color for the label and a small font size
219 QPalette palette = label->palette();
220 QColor textColor = palette.color(QPalette::Text);
221 textColor.setAlpha(128);
222 palette.setColor(QPalette::WindowText, textColor);
223 label->setPalette(palette);
224 label->setFont(smallFont);
225 label->setWordWrap(true);
226 label->setAlignment(Qt::AlignTop | Qt::AlignRight);
227
228 QLabel* infoLabel = qobject_cast<QLabel*>(infoWidget);
229 if (infoLabel != 0) {
230 infoLabel->setFont(smallFont);
231 infoLabel->setWordWrap(true);
232 infoLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
233 }
234
235 // add the row to grid layout
236 const int rowIndex = m_rows.count();
237 m_gridLayout->addWidget(label, rowIndex, 0, Qt::AlignRight);
238 const int spacerWidth = QFontMetrics(smallFont).size(Qt::TextSingleLine, " ").width();
239 m_gridLayout->addItem(new QSpacerItem(spacerWidth, 1), rowIndex, 1);
240 m_gridLayout->addWidget(infoWidget, rowIndex, 2, Qt::AlignLeft);
241 }
242
243 void KMetaDataWidget::Private::setRowVisible(QWidget* infoWidget, bool visible)
244 {
245 foreach (const Row& row, m_rows) {
246 if (row.infoWidget == infoWidget) {
247 row.label->setVisible(visible);
248 row.infoWidget->setVisible(visible);
249 return;
250 }
251 }
252 }
253
254
255 void KMetaDataWidget::Private::initMetaInfoSettings()
256 {
257 KConfig config("kmetainformationrc", KConfig::NoGlobals);
258 KConfigGroup settings = config.group("Show");
259 if (!settings.readEntry("initialized", false)) {
260 // The resource file is read the first time. Assure
261 // that some meta information is disabled per default.
262
263 static const char* disabledProperties[] = {
264 "asText", "contentSize", "created", "depth", "description", "fileExtension",
265 "fileName", "fileSize", "hasTag", "isPartOf", "lastModified", "mimeType", "name",
266 "numericRating", "parentUrl", "permissions", "plainTextContent", "owner",
267 "sourceModified", "url",
268 0 // mandatory last entry
269 };
270
271 int i = 0;
272 while (disabledProperties[i] != 0) {
273 settings.writeEntry(disabledProperties[i], false);
274 ++i;
275 }
276
277 // mark the group as initialized
278 settings.writeEntry("initialized", true);
279 }
280 }
281
282 void KMetaDataWidget::Private::updateRowsVisibility()
283 {
284 KConfig config("kmetainformationrc", KConfig::NoGlobals);
285 KConfigGroup settings = config.group("Show");
286
287 setRowVisible(m_typeInfo,
288 (m_visibleDataTypes & KMetaDataWidget::TypeData) &&
289 settings.readEntry("type", true));
290
291 // Cache in m_isSizeVisible whether the size should be shown. This
292 // is necessary as the size is temporary hidden when the target
293 // file item is a directory.
294 m_isSizeVisible = (m_visibleDataTypes & KMetaDataWidget::SizeData) &&
295 settings.readEntry("size", true);
296 setRowVisible(m_sizeInfo, m_isSizeVisible);
297
298 setRowVisible(m_modifiedInfo,
299 (m_visibleDataTypes & KMetaDataWidget::ModifiedData) &&
300 settings.readEntry("modified", true));
301
302 setRowVisible(m_ownerInfo,
303 (m_visibleDataTypes & KMetaDataWidget::OwnerData) &&
304 settings.readEntry("owner", true));
305
306 setRowVisible(m_permissionsInfo,
307 (m_visibleDataTypes & KMetaDataWidget::PermissionsData) &&
308 settings.readEntry("permissions", true));
309
310 #ifdef HAVE_NEPOMUK
311 if (Nepomuk::ResourceManager::instance()->init() == 0) {
312 setRowVisible(m_ratingWidget,
313 (m_visibleDataTypes & KMetaDataWidget::RatingData) &&
314 settings.readEntry("rating", true));
315
316 setRowVisible(m_taggingWidget,
317 (m_visibleDataTypes & KMetaDataWidget::TagsData) &&
318 settings.readEntry("tags", true));
319
320 setRowVisible(m_commentWidget,
321 (m_visibleDataTypes & KMetaDataWidget::CommentData) &&
322 settings.readEntry("comment", true));
323 }
324 #endif
325 }
326
327 void KMetaDataWidget::Private::slotLoadingFinished()
328 {
329 #ifdef HAVE_NEPOMUK
330 Q_ASSERT(m_loadMetaDataThread != 0);
331 m_ratingWidget->setRating(m_loadMetaDataThread->rating());
332 m_commentWidget->setText(m_loadMetaDataThread->comment());
333 m_taggingWidget->setTags(m_loadMetaDataThread->tags());
334
335 // Show the remaining meta information as text. The number
336 // of required rows may very. Existing rows are reused to
337 // prevent flickering.
338 int index = 8; // TODO: don't hardcode this value here
339 const int rowCount = m_rows.count();
340 Q_ASSERT(rowCount >= index);
341
342 Q_ASSERT(m_loadMetaDataThread->metaInfoLabels().count() == m_loadMetaDataThread->metaInfoValues().count());
343 const int metaInfoCount = m_loadMetaDataThread->metaInfoLabels().count();
344 for (int i = 0; i < metaInfoCount; ++i) {
345 const QList<QString> metaInfoLabels = m_loadMetaDataThread->metaInfoLabels();
346 const QList<QString> metaInfoValues = m_loadMetaDataThread->metaInfoValues();
347 if (index < rowCount) {
348 // adjust texts of the current row
349 m_rows[index].label->setText(metaInfoLabels[i]);
350 QLabel* infoValueLabel = qobject_cast<QLabel*>(m_rows[index].infoWidget);
351 Q_ASSERT(infoValueLabel != 0);
352 infoValueLabel->setText(metaInfoValues[i]);
353 } else {
354 // create new row
355 QLabel* infoLabel = new QLabel(metaInfoLabels[i], q);
356 QLabel* infoValue = new QLabel(metaInfoValues[i], q);
357 addRow(infoLabel, infoValue);
358 }
359 ++index;
360 }
361 if (metaInfoCount > 0) {
362 --index;
363 }
364
365 // remove rows that are not needed anymore
366 for (int i = rowCount - 1; i > index; --i) {
367 delete m_rows[i].label;
368 delete m_rows[i].infoWidget;
369 m_rows.pop_back();
370 }
371
372 m_files = m_loadMetaDataThread->files();
373
374 delete m_loadMetaDataThread;
375 m_loadMetaDataThread = 0;
376 #endif
377
378 q->updateGeometry();
379 }
380
381 void KMetaDataWidget::Private::slotRatingChanged(unsigned int rating)
382 {
383 #ifdef HAVE_NEPOMUK
384 Nepomuk::MassUpdateJob* job =
385 Nepomuk::MassUpdateJob::rateResources(m_files.values(), rating);
386 startChangeDataJob(job);
387 #else
388 Q_UNUSED(rating);
389 #endif
390 }
391
392 void KMetaDataWidget::Private::slotTagsChanged(const QList<Nepomuk::Tag>& tags)
393 {
394 #ifdef HAVE_NEPOMUK
395 m_taggingWidget->setTags(tags);
396
397 Nepomuk::MassUpdateJob* job =
398 Nepomuk::MassUpdateJob::tagResources(m_files.values(), tags);
399 startChangeDataJob(job);
400 #else
401 Q_UNUSED(tags);
402 #endif
403 }
404
405 void KMetaDataWidget::Private::slotCommentChanged(const QString& comment)
406 {
407 #ifdef HAVE_NEPOMUK
408 Nepomuk::MassUpdateJob* job =
409 Nepomuk::MassUpdateJob::commentResources(m_files.values(), comment);
410 startChangeDataJob(job);
411 #else
412 Q_UNUSED(comment);
413 #endif
414 }
415
416 void KMetaDataWidget::Private::slotMetaDataUpdateDone()
417 {
418 #ifdef HAVE_NEPOMUK
419 q->setEnabled(true);
420 #endif
421 }
422
423 #ifdef HAVE_NEPOMUK
424 void KMetaDataWidget::Private::startChangeDataJob(KJob* job)
425 {
426 connect(job, SIGNAL(result(KJob*)),
427 q, SLOT(slotMetaDataUpdateDone()));
428 q->setEnabled(false); // no updates during execution
429 job->start();
430 }
431 #endif
432
433 KMetaDataWidget::KMetaDataWidget(QWidget* parent) :
434 QWidget(parent),
435 d(new Private(this))
436 {
437 }
438
439 KMetaDataWidget::~KMetaDataWidget()
440 {
441 delete d;
442 }
443
444 void KMetaDataWidget::setItem(const KFileItem& item)
445 {
446 // update values for "type", "size", "modified",
447 // "owner" and "permissions" synchronously
448 d->m_sizeLabel->setText(i18nc("@label", "Size:"));
449 if (item.isDir()) {
450 d->m_typeInfo->setText(i18nc("@label", "Folder"));
451 d->setRowVisible(d->m_sizeInfo, false);
452 } else {
453 d->m_typeInfo->setText(item.mimeComment());
454 d->m_sizeInfo->setText(KIO::convertSize(item.size()));
455 d->setRowVisible(d->m_sizeInfo, d->m_isSizeVisible);
456 }
457 d->m_modifiedInfo->setText(KGlobal::locale()->formatDateTime(item.time(KFileItem::ModificationTime), KLocale::FancyLongDate));
458 d->m_ownerInfo->setText(item.user());
459 d->m_permissionsInfo->setText(item.permissionsString());
460
461 setItems(KFileItemList() << item);
462 }
463
464 void KMetaDataWidget::setItems(const KFileItemList& items)
465 {
466 d->m_fileItems = 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, d->m_isSizeVisible);
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 if (Nepomuk::ResourceManager::instance()->init() == 0) {
485 QList<KUrl> urls;
486 foreach (const KFileItem& item, items) {
487 const KUrl url = item.nepomukUri();
488 if (url.isValid()) {
489 urls.append(url);
490 }
491 }
492
493 if (d->m_loadMetaDataThread != 0) {
494 disconnect(d->m_loadMetaDataThread, SIGNAL(finished()), this, SLOT(slotLoadingFinished()));
495 d->m_loadMetaDataThread->cancelAndDelete();
496 }
497
498 d->m_loadMetaDataThread = new KLoadMetaDataThread();
499 connect(d->m_loadMetaDataThread, SIGNAL(finished()), this, SLOT(slotLoadingFinished()));
500 d->m_loadMetaDataThread->load(urls);
501 }
502 #endif
503 }
504
505 void KMetaDataWidget::setItem(const KUrl& url)
506 {
507 KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
508 item.refresh();
509 setItem(item);
510 }
511
512 void KMetaDataWidget::setItems(const QList<KUrl>& urls)
513 {
514 KFileItemList items;
515 foreach (const KUrl& url, urls) {
516 KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
517 item.refresh();
518 items.append(item);
519 }
520 setItems(items);
521 }
522
523 KFileItemList KMetaDataWidget::items() const
524 {
525 return d->m_fileItems;
526 }
527
528 void KMetaDataWidget::setVisibleDataTypes(MetaDataTypes data)
529 {
530 d->m_visibleDataTypes = data;
531 d->updateRowsVisibility();
532 }
533
534 KMetaDataWidget::MetaDataTypes KMetaDataWidget::visibleDataTypes() const
535 {
536 return d->m_visibleDataTypes;
537 }
538
539 QSize KMetaDataWidget::sizeHint() const
540 {
541 const int fixedWidth = 200;
542
543 int height = d->m_gridLayout->margin() * 2 +
544 d->m_gridLayout->spacing() * (d->m_rows.count() - 1);
545
546 foreach (const Private::Row& row, d->m_rows) {
547 if (row.infoWidget != 0) {
548 int rowHeight = row.infoWidget->heightForWidth(fixedWidth / 2);
549 if (rowHeight <= 0) {
550 rowHeight = row.infoWidget->sizeHint().height();
551 }
552 height += rowHeight;
553 }
554 }
555
556 return QSize(fixedWidth, height);
557 }
558
559 #include "kmetadatawidget.moc"