1 /***************************************************************************
2 * Copyright (C) 2008 by Sebastian Trueg <trueg@kde.org> *
3 * Copyright (C) 2009 by Peter Penz <peter.penz@gmx.at> *
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. *
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. *
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 ***************************************************************************/
21 #include "metadatawidget.h"
23 #include "metadataconfigurationdialog.h"
26 #include <kconfiggroup.h>
27 #include <kfileitem.h>
28 #include <kglobalsettings.h>
31 #include <QFontMetrics>
32 #include <QGridLayout>
37 #include <config-nepomuk.h>
39 #define DISABLE_NEPOMUK_LEGACY
41 #include "commentwidget_p.h"
42 #include "nepomukmassupdatejob_p.h"
43 #include "taggingwidget_p.h"
45 #include <Nepomuk/KRatingWidget>
46 #include <Nepomuk/Resource>
47 #include <Nepomuk/Types/Property>
48 #include <Nepomuk/Variant>
50 #include <Soprano/Vocabulary/Xesam>
52 #include <QSpacerItem>
56 class MetaDataWidget::Private
65 Private(MetaDataWidget
* parent
);
68 void addRow(QLabel
* label
, QWidget
* infoWidget
);
69 void removeMetaInfoRows();
70 void setRowVisible(QWidget
* infoWidget
, bool visible
);
73 * Initializes the configuration file "kmetainformationrc"
74 * with proper default settings for the first start in
75 * an uninitialized environment.
77 void initMetaInfoSettings();
80 * Parses the configuration file "kmetainformationrc" and
81 * updates the visibility of all rows.
83 void updateRowsVisibility();
85 void slotLoadingFinished();
86 void slotRatingChanged(unsigned int rating
);
87 void slotTagsChanged(const QList
<Nepomuk::Tag
>& tags
);
88 void slotCommentChanged(const QString
& comment
);
89 void slotMetaDataUpdateDone();
92 * Disables the metadata widget and starts the job that
93 * changes the meta data asynchronously. After the job
94 * has been finished, the metadata widget gets enabled again.
96 void startChangeDataJob(KJob
* job
);
98 QList
<KFileItem
> m_fileItems
;
101 QGridLayout
* m_gridLayout
;
106 QLabel
* m_modifiedInfo
;
108 QLabel
* m_permissionsInfo
;
111 KRatingWidget
* m_ratingWidget
;
112 TaggingWidget
* m_taggingWidget
;
113 CommentWidget
* m_commentWidget
;
115 // shared data between the GUI-thread and
116 // the loader-thread (see LoadFilesThread):
122 QList
<Nepomuk::Tag
> tags
;
123 QList
<QString
> metaInfoLabels
;
124 QList
<QString
> metaInfoValues
;
125 QMap
<KUrl
, Nepomuk::Resource
> files
;
129 * Loads the meta data of files and writes
130 * the result into a shared data pool that
131 * can be used by the widgets in the GUI thread.
133 class LoadFilesThread
: public QThread
136 LoadFilesThread(SharedData
* m_sharedData
, QMutex
* m_mutex
);
137 virtual ~LoadFilesThread();
138 void loadFiles(const KUrl::List
& urls
);
143 * Assures that the settings for the meta information
144 * are initialized with proper default values.
146 void initMetaInfoSettings(KConfigGroup
& group
);
149 * Temporary helper method for KDE 4.3 as we currently don't get
150 * translated labels for Nepmok literals: Replaces camelcase labels
151 * like "fileLocation" by "File Location:".
153 QString
tunedLabel(const QString
& label
) const;
156 SharedData
* m_sharedData
;
162 LoadFilesThread
* m_loadFilesThread
;
166 MetaDataWidget
* const q
;
169 MetaDataWidget::Private::Private(MetaDataWidget
* parent
) :
178 m_permissionsInfo(0),
183 m_loadFilesThread(0),
187 m_gridLayout
= new QGridLayout(parent
);
188 m_gridLayout
->setMargin(0);
190 m_typeInfo
= new QLabel(parent
);
191 m_sizeLabel
= new QLabel(parent
);
192 m_sizeInfo
= new QLabel(parent
);
193 m_modifiedInfo
= new QLabel(parent
);
194 m_ownerInfo
= new QLabel(parent
);
195 m_permissionsInfo
= new QLabel(parent
);
197 const QFontMetrics
fontMetrics(KGlobalSettings::smallestReadableFont());
198 m_ratingWidget
= new KRatingWidget(parent
);
199 m_ratingWidget
->setFixedHeight(fontMetrics
.height());
200 connect(m_ratingWidget
, SIGNAL(ratingChanged(unsigned int)),
201 q
, SLOT(slotRatingChanged(unsigned int)));
203 m_taggingWidget
= new TaggingWidget(parent
);
204 connect(m_taggingWidget
, SIGNAL(tagsChanged(const QList
<Nepomuk::Tag
>&)),
205 q
, SLOT(slotTagsChanged(const QList
<Nepomuk::Tag
>&)));
207 m_commentWidget
= new CommentWidget(parent
);
208 connect(m_commentWidget
, SIGNAL(commentChanged(const QString
&)),
209 q
, SLOT(slotCommentChanged(const QString
&)));
212 addRow(new QLabel(i18nc("@label", "Type:"), parent
), m_typeInfo
);
213 addRow(m_sizeLabel
, m_sizeInfo
);
214 addRow(new QLabel(i18nc("@label", "Modified:"), parent
), m_modifiedInfo
);
215 addRow(new QLabel(i18nc("@label", "Owner:"), parent
), m_ownerInfo
);
216 addRow(new QLabel(i18nc("@label", "Permissions:"), parent
), m_permissionsInfo
);
218 addRow(new QLabel(i18nc("@label", "Rating:"), parent
), m_ratingWidget
);
219 addRow(new QLabel(i18nc("@label", "Tags:"), parent
), m_taggingWidget
);
220 addRow(new QLabel(i18nc("@label", "Comment:"), parent
), m_commentWidget
);
222 m_sharedData
.rating
= 0;
223 m_loadFilesThread
= new LoadFilesThread(&m_sharedData
, &m_mutex
);
224 connect(m_loadFilesThread
, SIGNAL(finished()), q
, SLOT(slotLoadingFinished()));
227 initMetaInfoSettings();
228 updateRowsVisibility();
231 MetaDataWidget::Private::~Private()
234 delete m_loadFilesThread
;
238 void MetaDataWidget::Private::addRow(QLabel
* label
, QWidget
* infoWidget
)
242 row
.infoWidget
= infoWidget
;
245 const QFont smallFont
= KGlobalSettings::smallestReadableFont();
246 // use a brighter color for the label and a small font size
247 QPalette palette
= label
->palette();
248 QColor textColor
= palette
.color(QPalette::Text
);
249 textColor
.setAlpha(128);
250 palette
.setColor(QPalette::WindowText
, textColor
);
251 label
->setPalette(palette
);
252 label
->setFont(smallFont
);
253 label
->setWordWrap(true);
254 label
->setAlignment(Qt::AlignTop
| Qt::AlignRight
);
256 QLabel
* infoLabel
= qobject_cast
<QLabel
*>(infoWidget
);
257 if (infoLabel
!= 0) {
258 infoLabel
->setFont(smallFont
);
259 infoLabel
->setWordWrap(true);
260 infoLabel
->setAlignment(Qt::AlignTop
| Qt::AlignLeft
);
263 // add the row to grid layout
264 const int rowIndex
= m_rows
.count();
265 m_gridLayout
->addWidget(label
, rowIndex
, 0, Qt::AlignRight
);
266 const int spacerWidth
= QFontMetrics(smallFont
).size(Qt::TextSingleLine
, " ").width();
267 m_gridLayout
->addItem(new QSpacerItem(spacerWidth
, 1), rowIndex
, 1);
268 m_gridLayout
->addWidget(infoWidget
, rowIndex
, 2, Qt::AlignLeft
);
271 void MetaDataWidget::Private::setRowVisible(QWidget
* infoWidget
, bool visible
)
273 foreach (const Row
& row
, m_rows
) {
274 if (row
.infoWidget
== infoWidget
) {
275 row
.label
->setVisible(visible
);
276 row
.infoWidget
->setVisible(visible
);
283 void MetaDataWidget::Private::initMetaInfoSettings()
285 KConfig
config("kmetainformationrc", KConfig::NoGlobals
);
286 KConfigGroup settings
= config
.group("Show");
287 if (!settings
.readEntry("initialized", false)) {
288 // The resource file is read the first time. Assure
289 // that some meta information is disabled per default.
291 static const char* disabledProperties
[] = {
292 "asText", "contentSize", "created", "depth", "description", "fileExtension",
293 "fileName", "fileSize", "hasTag", "isPartOf", "lastModified", "mimeType", "name",
294 "numericRating", "parentUrl", "permissions", "plainTextContent", "owner",
295 "sourceModified", "url",
296 0 // mandatory last entry
300 while (disabledProperties
[i
] != 0) {
301 settings
.writeEntry(disabledProperties
[i
], false);
305 // mark the group as initialized
306 settings
.writeEntry("initialized", true);
310 void MetaDataWidget::Private::updateRowsVisibility()
312 KConfig
config("kmetainformationrc", KConfig::NoGlobals
);
313 KConfigGroup settings
= config
.group("Show");
314 setRowVisible(m_typeInfo
, settings
.readEntry("type", true));
315 setRowVisible(m_sizeInfo
, settings
.readEntry("size", true));
316 setRowVisible(m_modifiedInfo
, settings
.readEntry("modified", true));
317 setRowVisible(m_ownerInfo
, settings
.readEntry("owner", true));
318 setRowVisible(m_permissionsInfo
, settings
.readEntry("permissions", true));
320 setRowVisible(m_ratingWidget
, settings
.readEntry("rating", true));
321 setRowVisible(m_taggingWidget
, settings
.readEntry("tagging", true));
322 setRowVisible(m_commentWidget
, settings
.readEntry("comment", true));
326 void MetaDataWidget::Private::slotLoadingFinished()
329 QMutexLocker
locker(&m_mutex
);
330 m_ratingWidget
->setRating(m_sharedData
.rating
);
331 m_commentWidget
->setText(m_sharedData
.comment
);
332 m_taggingWidget
->setTags(m_sharedData
.tags
);
334 // Show the remaining meta information as text. The number
335 // of required rows may very. Existing rows are reused to
336 // prevent flickering.
337 int index
= 8; // TODO: don't hardcode this value here
338 const int rowCount
= m_rows
.count();
339 Q_ASSERT(rowCount
>= index
);
341 Q_ASSERT(m_sharedData
.metaInfoLabels
.count() == m_sharedData
.metaInfoValues
.count());
342 const int metaInfoCount
= m_sharedData
.metaInfoLabels
.count();
343 for (int i
= 0; i
< metaInfoCount
; ++i
) {
344 if (index
< rowCount
) {
345 // adjust texts of the current row
346 m_rows
[index
].label
->setText(m_sharedData
.metaInfoLabels
[i
]);
347 QLabel
* infoValueLabel
= qobject_cast
<QLabel
*>(m_rows
[index
].infoWidget
);
348 Q_ASSERT(infoValueLabel
!= 0);
349 infoValueLabel
->setText(m_sharedData
.metaInfoValues
[i
]);
352 QLabel
* infoLabel
= new QLabel(m_sharedData
.metaInfoLabels
[i
], q
);
353 QLabel
* infoValue
= new QLabel(m_sharedData
.metaInfoValues
[i
], q
);
354 addRow(infoLabel
, infoValue
);
358 if (metaInfoCount
> 0) {
362 // remove rows that are not needed anymore
363 for (int i
= rowCount
- 1; i
> index
; --i
) {
364 delete m_rows
[i
].label
;
365 delete m_rows
[i
].infoWidget
;
369 emit q
->loadingFinished();
373 void MetaDataWidget::Private::slotRatingChanged(unsigned int rating
)
376 QMutexLocker
locker(&m_mutex
);
377 Nepomuk::MassUpdateJob
* job
=
378 Nepomuk::MassUpdateJob::rateResources(m_sharedData
.files
.values(), rating
);
380 startChangeDataJob(job
);
382 emit q
->ratingChanged(rating
);
385 void MetaDataWidget::Private::slotTagsChanged(const QList
<Nepomuk::Tag
>& tags
)
388 m_taggingWidget
->setTags(tags
);
390 QMutexLocker
locker(&m_mutex
);
391 Nepomuk::MassUpdateJob
* job
=
392 Nepomuk::MassUpdateJob::tagResources(m_sharedData
.files
.values(), tags
);
394 startChangeDataJob(job
);
396 emit q
->tagsChanged(tags
);
399 void MetaDataWidget::Private::slotCommentChanged(const QString
& comment
)
402 QMutexLocker
locker(&m_mutex
);
403 Nepomuk::MassUpdateJob
* job
=
404 Nepomuk::MassUpdateJob::commentResources(m_sharedData
.files
.values(), comment
);
406 startChangeDataJob(job
);
408 emit q
->commentChanged(comment
);
411 void MetaDataWidget::Private::slotMetaDataUpdateDone()
416 void MetaDataWidget::Private::startChangeDataJob(KJob
* job
)
418 connect(job
, SIGNAL(result(KJob
*)),
419 q
, SLOT(slotMetaDataUpdateDone()));
420 q
->setEnabled(false); // no updates during execution
425 MetaDataWidget::Private::LoadFilesThread::LoadFilesThread(
426 MetaDataWidget::Private::SharedData
* m_sharedData
,
428 m_sharedData(m_sharedData
),
435 MetaDataWidget::Private::LoadFilesThread::~LoadFilesThread()
437 // This thread may very well be deleted during execution. We need
438 // to protect it from crashes here.
443 void MetaDataWidget::Private::LoadFilesThread::loadFiles(const KUrl::List
& urls
)
445 QMutexLocker
locker(m_mutex
);
451 void MetaDataWidget::Private::LoadFilesThread::run()
453 QMutexLocker
locker(m_mutex
);
454 const KUrl::List urls
= m_urls
;
457 KConfig
config("kmetainformationrc", KConfig::NoGlobals
);
458 KConfigGroup settings
= config
.group("Show");
461 unsigned int rating
= 0;
463 QList
<Nepomuk::Tag
> tags
;
464 QList
<QString
> metaInfoLabels
;
465 QList
<QString
> metaInfoValues
;
466 QMap
<KUrl
, Nepomuk::Resource
> files
;
467 foreach (const KUrl
& url
, urls
) {
472 Nepomuk::Resource
file(url
, Soprano::Vocabulary::Xesam::File());
473 files
.insert(url
, file
);
475 if (!first
&& (rating
!= file
.rating())) {
476 rating
= 0; // reset rating
478 rating
= file
.rating();
481 if (!first
&& (comment
!= file
.description())) {
482 comment
.clear(); // reset comment
484 comment
= file
.description();
487 if (!first
&& (tags
!= file
.tags())) {
488 tags
.clear(); // reset tags
493 if (first
&& (urls
.count() == 1)) {
494 // TODO: show shared meta informations instead
495 // of not showing anything on multiple selections
496 QHash
<QUrl
, Nepomuk::Variant
> properties
= file
.properties();
497 QHash
<QUrl
, Nepomuk::Variant
>::const_iterator it
= properties
.constBegin();
498 while (it
!= properties
.constEnd()) {
499 Nepomuk::Types::Property
prop(it
.key());
500 if (settings
.readEntry(prop
.name(), true)) {
501 // TODO #1: use Nepomuk::formatValue(res, prop) if available
502 // instead of it.value().toString()
503 // TODO #2: using tunedLabel() is a workaround for KDE 4.3 until
504 // we get translated labels
505 metaInfoLabels
.append(tunedLabel(prop
.label()));
506 metaInfoValues
.append(it
.value().toString());
516 m_sharedData
->rating
= rating
;
517 m_sharedData
->comment
= comment
;
518 m_sharedData
->tags
= tags
;
519 m_sharedData
->metaInfoLabels
= metaInfoLabels
;
520 m_sharedData
->metaInfoValues
= metaInfoValues
;
521 m_sharedData
->files
= files
;
524 QString
MetaDataWidget::Private::LoadFilesThread::tunedLabel(const QString
& label
) const
527 const int labelLength
= label
.length();
528 if (labelLength
> 0) {
529 tunedLabel
.reserve(labelLength
);
530 tunedLabel
= label
[0].toUpper();
531 for (int i
= 1; i
< labelLength
; ++i
) {
532 if (label
[i
].isUpper() && !label
[i
- 1].isSpace() && !label
[i
- 1].isUpper()) {
534 tunedLabel
+= label
[i
].toLower();
536 tunedLabel
+= label
[i
];
540 return tunedLabel
+ ':';
543 #endif // HAVE_NEPOMUK
545 MetaDataWidget::MetaDataWidget(QWidget
* parent
) :
551 MetaDataWidget::~MetaDataWidget()
556 void MetaDataWidget::setItem(const KFileItem
& item
)
558 // update values for "type", "size", "modified",
559 // "owner" and "permissions" synchronously
560 d
->m_sizeLabel
->setText(i18nc("@label", "Size:"));
562 d
->m_typeInfo
->setText(i18nc("@label", "Folder"));
563 d
->setRowVisible(d
->m_sizeInfo
, false);
565 d
->m_typeInfo
->setText(item
.mimeComment());
566 d
->m_sizeInfo
->setText(KIO::convertSize(item
.size()));
567 d
->setRowVisible(d
->m_sizeInfo
, true);
569 d
->m_modifiedInfo
->setText(item
.timeString());
570 d
->m_ownerInfo
->setText(item
.user());
571 d
->m_permissionsInfo
->setText(item
.permissionsString());
573 setItems(KFileItemList() << item
);
576 void MetaDataWidget::setItems(const KFileItemList
& items
)
578 d
->m_fileItems
= items
;
580 if (items
.count() > 1) {
581 // calculate the size of all items and show this
582 // information to the user
583 d
->m_sizeLabel
->setText(i18nc("@label", "Total Size:"));
584 d
->setRowVisible(d
->m_sizeInfo
, true);
586 quint64 totalSize
= 0;
587 foreach (const KFileItem
& item
, items
) {
588 if (!item
.isDir() && !item
.isLink()) {
589 totalSize
+= item
.size();
592 d
->m_sizeInfo
->setText(KIO::convertSize(totalSize
));
597 foreach (const KFileItem
& item
, items
) {
598 const KUrl url
= item
.nepomukUri();
603 d
->m_loadFilesThread
->loadFiles(urls
);
607 void MetaDataWidget::openConfigurationDialog()
609 const KUrl url
= d
->m_fileItems
[0].nepomukUri();
610 if (!url
.isValid()) {
614 MetaDataConfigurationDialog
dialog(url
, this, Qt::Dialog
);
615 if (dialog
.exec() == KDialog::Accepted
) {
616 d
->updateRowsVisibility();
620 unsigned int MetaDataWidget::rating() const
623 QMutexLocker
locker(&d
->m_mutex
);
624 return d
->m_sharedData
.rating
;
630 QList
<Nepomuk::Tag
> MetaDataWidget::tags() const
633 QMutexLocker
locker(&d
->m_mutex
);
634 return d
->m_sharedData
.tags
;
636 return QList
<Nepomuk::Tag
>();
640 QString
MetaDataWidget::comment() const
643 QMutexLocker
locker(&d
->m_mutex
);
644 return d
->m_sharedData
.comment
;
650 #include "metadatawidget.moc"