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"
24 #include <kconfiggroup.h>
25 #include <kfileitem.h>
26 #include <kglobalsettings.h>
29 #include <QFontMetrics>
30 #include <QGridLayout>
35 #include <config-nepomuk.h>
37 #define DISABLE_NEPOMUK_LEGACY
39 #include "commentwidget_p.h"
40 #include "nepomukmassupdatejob_p.h"
41 #include "taggingwidget_p.h"
43 #include <Nepomuk/KRatingWidget>
44 #include <Nepomuk/Resource>
45 #include <Nepomuk/ResourceManager>
46 #include <Nepomuk/Types/Property>
47 #include <Nepomuk/Variant>
50 #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
);
99 MetaDataTypes m_hiddenData
;
100 QList
<KFileItem
> m_fileItems
;
103 QGridLayout
* m_gridLayout
;
108 QLabel
* m_modifiedInfo
;
110 QLabel
* m_permissionsInfo
;
113 KRatingWidget
* m_ratingWidget
;
114 TaggingWidget
* m_taggingWidget
;
115 CommentWidget
* m_commentWidget
;
117 // shared data between the GUI-thread and
118 // the loader-thread (see LoadFilesThread):
124 QList
<Nepomuk::Tag
> tags
;
125 QList
<QString
> metaInfoLabels
;
126 QList
<QString
> metaInfoValues
;
127 QMap
<KUrl
, Nepomuk::Resource
> files
;
131 * Loads the meta data of files and writes
132 * the result into a shared data pool that
133 * can be used by the widgets in the GUI thread.
135 class LoadFilesThread
: public QThread
138 LoadFilesThread(SharedData
* m_sharedData
, QMutex
* m_mutex
);
139 virtual ~LoadFilesThread();
140 void loadFiles(const KUrl::List
& urls
);
145 * Assures that the settings for the meta information
146 * are initialized with proper default values.
148 void initMetaInfoSettings(KConfigGroup
& group
);
151 * Temporary helper method for KDE 4.3 as we currently don't get
152 * translated labels for Nepmok literals: Replaces camelcase labels
153 * like "fileLocation" by "File Location:".
155 QString
tunedLabel(const QString
& label
) const;
158 SharedData
* m_sharedData
;
164 LoadFilesThread
* m_loadFilesThread
;
168 MetaDataWidget
* const q
;
171 MetaDataWidget::Private::Private(MetaDataWidget
* parent
) :
172 m_isSizeVisible(true),
182 m_permissionsInfo(0),
187 m_loadFilesThread(0),
191 m_gridLayout
= new QGridLayout(parent
);
192 m_gridLayout
->setMargin(0);
194 m_typeInfo
= new QLabel(parent
);
195 m_sizeLabel
= new QLabel(parent
);
196 m_sizeInfo
= new QLabel(parent
);
197 m_modifiedInfo
= new QLabel(parent
);
198 m_ownerInfo
= new QLabel(parent
);
199 m_permissionsInfo
= new QLabel(parent
);
201 addRow(new QLabel(i18nc("@label", "Type:"), parent
), m_typeInfo
);
202 addRow(m_sizeLabel
, m_sizeInfo
);
203 addRow(new QLabel(i18nc("@label", "Modified:"), parent
), m_modifiedInfo
);
204 addRow(new QLabel(i18nc("@label", "Owner:"), parent
), m_ownerInfo
);
205 addRow(new QLabel(i18nc("@label", "Permissions:"), parent
), m_permissionsInfo
);
208 if (Nepomuk::ResourceManager::instance()->init() == 0) {
209 const QFontMetrics
fontMetrics(KGlobalSettings::smallestReadableFont());
210 m_ratingWidget
= new KRatingWidget(parent
);
211 m_ratingWidget
->setFixedHeight(fontMetrics
.height());
212 connect(m_ratingWidget
, SIGNAL(ratingChanged(unsigned int)),
213 q
, SLOT(slotRatingChanged(unsigned int)));
215 m_taggingWidget
= new TaggingWidget(parent
);
216 connect(m_taggingWidget
, SIGNAL(tagsChanged(const QList
<Nepomuk::Tag
>&)),
217 q
, SLOT(slotTagsChanged(const QList
<Nepomuk::Tag
>&)));
219 m_commentWidget
= new CommentWidget(parent
);
220 connect(m_commentWidget
, SIGNAL(commentChanged(const QString
&)),
221 q
, SLOT(slotCommentChanged(const QString
&)));
223 addRow(new QLabel(i18nc("@label", "Rating:"), parent
), m_ratingWidget
);
224 addRow(new QLabel(i18nc("@label", "Tags:"), parent
), m_taggingWidget
);
225 addRow(new QLabel(i18nc("@label", "Comment:"), parent
), m_commentWidget
);
227 m_loadFilesThread
= new LoadFilesThread(&m_sharedData
, &m_mutex
);
228 connect(m_loadFilesThread
, SIGNAL(finished()), q
, SLOT(slotLoadingFinished()));
231 m_sharedData
.rating
= 0;
234 initMetaInfoSettings();
235 updateRowsVisibility();
238 MetaDataWidget::Private::~Private()
241 delete m_loadFilesThread
;
245 void MetaDataWidget::Private::addRow(QLabel
* label
, QWidget
* infoWidget
)
249 row
.infoWidget
= infoWidget
;
252 const QFont smallFont
= KGlobalSettings::smallestReadableFont();
253 // use a brighter color for the label and a small font size
254 QPalette palette
= label
->palette();
255 QColor textColor
= palette
.color(QPalette::Text
);
256 textColor
.setAlpha(128);
257 palette
.setColor(QPalette::WindowText
, textColor
);
258 label
->setPalette(palette
);
259 label
->setFont(smallFont
);
260 label
->setWordWrap(true);
261 label
->setAlignment(Qt::AlignTop
| Qt::AlignRight
);
263 QLabel
* infoLabel
= qobject_cast
<QLabel
*>(infoWidget
);
264 if (infoLabel
!= 0) {
265 infoLabel
->setFont(smallFont
);
266 infoLabel
->setWordWrap(true);
267 infoLabel
->setAlignment(Qt::AlignTop
| Qt::AlignLeft
);
270 // add the row to grid layout
271 const int rowIndex
= m_rows
.count();
272 m_gridLayout
->addWidget(label
, rowIndex
, 0, Qt::AlignRight
);
273 const int spacerWidth
= QFontMetrics(smallFont
).size(Qt::TextSingleLine
, " ").width();
274 m_gridLayout
->addItem(new QSpacerItem(spacerWidth
, 1), rowIndex
, 1);
275 m_gridLayout
->addWidget(infoWidget
, rowIndex
, 2, Qt::AlignLeft
);
278 void MetaDataWidget::Private::setRowVisible(QWidget
* infoWidget
, bool visible
)
280 foreach (const Row
& row
, m_rows
) {
281 if (row
.infoWidget
== infoWidget
) {
282 row
.label
->setVisible(visible
);
283 row
.infoWidget
->setVisible(visible
);
290 void MetaDataWidget::Private::initMetaInfoSettings()
292 KConfig
config("kmetainformationrc", KConfig::NoGlobals
);
293 KConfigGroup settings
= config
.group("Show");
294 if (!settings
.readEntry("initialized", false)) {
295 // The resource file is read the first time. Assure
296 // that some meta information is disabled per default.
298 static const char* disabledProperties
[] = {
299 "asText", "contentSize", "created", "depth", "description", "fileExtension",
300 "fileName", "fileSize", "hasTag", "isPartOf", "lastModified", "mimeType", "name",
301 "numericRating", "parentUrl", "permissions", "plainTextContent", "owner",
302 "sourceModified", "url",
303 0 // mandatory last entry
307 while (disabledProperties
[i
] != 0) {
308 settings
.writeEntry(disabledProperties
[i
], false);
312 // mark the group as initialized
313 settings
.writeEntry("initialized", true);
317 void MetaDataWidget::Private::updateRowsVisibility()
319 KConfig
config("kmetainformationrc", KConfig::NoGlobals
);
320 KConfigGroup settings
= config
.group("Show");
322 setRowVisible(m_typeInfo
,
323 !(m_hiddenData
& MetaDataWidget::TypeData
) &&
324 settings
.readEntry("type", true));
326 // Cache in m_isSizeVisible whether the size should be shown. This
327 // is necessary as the size is temporary hidden when the target
328 // file item is a directory.
329 m_isSizeVisible
= !(m_hiddenData
& MetaDataWidget::SizeData
) &&
330 settings
.readEntry("size", true);
331 setRowVisible(m_sizeInfo
, m_isSizeVisible
);
333 setRowVisible(m_modifiedInfo
,
334 !(m_hiddenData
& MetaDataWidget::ModifiedData
) &&
335 settings
.readEntry("modified", true));
337 setRowVisible(m_ownerInfo
,
338 !(m_hiddenData
& MetaDataWidget::OwnerData
) &&
339 settings
.readEntry("owner", true));
341 setRowVisible(m_permissionsInfo
,
342 !(m_hiddenData
& MetaDataWidget::PermissionsData
) &&
343 settings
.readEntry("permissions", true));
346 if (Nepomuk::ResourceManager::instance()->init() == 0) {
347 setRowVisible(m_ratingWidget
,
348 !(m_hiddenData
& MetaDataWidget::RatingData
) &&
349 settings
.readEntry("rating", true));
351 setRowVisible(m_taggingWidget
,
352 !(m_hiddenData
& MetaDataWidget::TagsData
) &&
353 settings
.readEntry("tags", true));
355 setRowVisible(m_commentWidget
,
356 !(m_hiddenData
& MetaDataWidget::CommentData
) &&
357 settings
.readEntry("comment", true));
362 void MetaDataWidget::Private::slotLoadingFinished()
365 QMutexLocker
locker(&m_mutex
);
366 m_ratingWidget
->setRating(m_sharedData
.rating
);
367 m_commentWidget
->setText(m_sharedData
.comment
);
368 m_taggingWidget
->setTags(m_sharedData
.tags
);
370 // Show the remaining meta information as text. The number
371 // of required rows may very. Existing rows are reused to
372 // prevent flickering.
373 int index
= 8; // TODO: don't hardcode this value here
374 const int rowCount
= m_rows
.count();
375 Q_ASSERT(rowCount
>= index
);
377 Q_ASSERT(m_sharedData
.metaInfoLabels
.count() == m_sharedData
.metaInfoValues
.count());
378 const int metaInfoCount
= m_sharedData
.metaInfoLabels
.count();
379 for (int i
= 0; i
< metaInfoCount
; ++i
) {
380 if (index
< rowCount
) {
381 // adjust texts of the current row
382 m_rows
[index
].label
->setText(m_sharedData
.metaInfoLabels
[i
]);
383 QLabel
* infoValueLabel
= qobject_cast
<QLabel
*>(m_rows
[index
].infoWidget
);
384 Q_ASSERT(infoValueLabel
!= 0);
385 infoValueLabel
->setText(m_sharedData
.metaInfoValues
[i
]);
388 QLabel
* infoLabel
= new QLabel(m_sharedData
.metaInfoLabels
[i
], q
);
389 QLabel
* infoValue
= new QLabel(m_sharedData
.metaInfoValues
[i
], q
);
390 addRow(infoLabel
, infoValue
);
394 if (metaInfoCount
> 0) {
398 // remove rows that are not needed anymore
399 for (int i
= rowCount
- 1; i
> index
; --i
) {
400 delete m_rows
[i
].label
;
401 delete m_rows
[i
].infoWidget
;
405 emit q
->loadingFinished();
409 void MetaDataWidget::Private::slotRatingChanged(unsigned int rating
)
412 QMutexLocker
locker(&m_mutex
);
413 Nepomuk::MassUpdateJob
* job
=
414 Nepomuk::MassUpdateJob::rateResources(m_sharedData
.files
.values(), rating
);
416 startChangeDataJob(job
);
418 emit q
->ratingChanged(rating
);
421 void MetaDataWidget::Private::slotTagsChanged(const QList
<Nepomuk::Tag
>& tags
)
424 m_taggingWidget
->setTags(tags
);
426 QMutexLocker
locker(&m_mutex
);
427 Nepomuk::MassUpdateJob
* job
=
428 Nepomuk::MassUpdateJob::tagResources(m_sharedData
.files
.values(), tags
);
430 startChangeDataJob(job
);
432 emit q
->tagsChanged(tags
);
435 void MetaDataWidget::Private::slotCommentChanged(const QString
& comment
)
438 QMutexLocker
locker(&m_mutex
);
439 Nepomuk::MassUpdateJob
* job
=
440 Nepomuk::MassUpdateJob::commentResources(m_sharedData
.files
.values(), comment
);
442 startChangeDataJob(job
);
444 emit q
->commentChanged(comment
);
447 void MetaDataWidget::Private::slotMetaDataUpdateDone()
452 void MetaDataWidget::Private::startChangeDataJob(KJob
* job
)
454 connect(job
, SIGNAL(result(KJob
*)),
455 q
, SLOT(slotMetaDataUpdateDone()));
456 q
->setEnabled(false); // no updates during execution
461 MetaDataWidget::Private::LoadFilesThread::LoadFilesThread(
462 MetaDataWidget::Private::SharedData
* m_sharedData
,
464 m_sharedData(m_sharedData
),
471 MetaDataWidget::Private::LoadFilesThread::~LoadFilesThread()
473 // This thread may very well be deleted during execution. We need
474 // to protect it from crashes here.
479 void MetaDataWidget::Private::LoadFilesThread::loadFiles(const KUrl::List
& urls
)
481 QMutexLocker
locker(m_mutex
);
487 void MetaDataWidget::Private::LoadFilesThread::run()
489 QMutexLocker
locker(m_mutex
);
490 const KUrl::List urls
= m_urls
;
493 KConfig
config("kmetainformationrc", KConfig::NoGlobals
);
494 KConfigGroup settings
= config
.group("Show");
497 unsigned int rating
= 0;
499 QList
<Nepomuk::Tag
> tags
;
500 QList
<QString
> metaInfoLabels
;
501 QList
<QString
> metaInfoValues
;
502 QMap
<KUrl
, Nepomuk::Resource
> files
;
503 foreach (const KUrl
& url
, urls
) {
508 Nepomuk::Resource
file(url
);
509 files
.insert(url
, file
);
511 if (!first
&& (rating
!= file
.rating())) {
512 rating
= 0; // reset rating
514 rating
= file
.rating();
517 if (!first
&& (comment
!= file
.description())) {
518 comment
.clear(); // reset comment
520 comment
= file
.description();
523 if (!first
&& (tags
!= file
.tags())) {
524 tags
.clear(); // reset tags
529 if (first
&& (urls
.count() == 1)) {
530 // TODO: show shared meta information instead
531 // of not showing anything on multiple selections
532 QHash
<QUrl
, Nepomuk::Variant
> properties
= file
.properties();
533 QHash
<QUrl
, Nepomuk::Variant
>::const_iterator it
= properties
.constBegin();
534 while (it
!= properties
.constEnd()) {
535 Nepomuk::Types::Property
prop(it
.key());
536 if (settings
.readEntry(prop
.name(), true)) {
537 // TODO #1: use Nepomuk::formatValue(res, prop) if available
538 // instead of it.value().toString()
539 // TODO #2: using tunedLabel() is a workaround for KDE 4.3 (4.4?) until
540 // we get translated labels
541 metaInfoLabels
.append(tunedLabel(prop
.label()));
542 metaInfoValues
.append(it
.value().toString());
552 m_sharedData
->rating
= rating
;
553 m_sharedData
->comment
= comment
;
554 m_sharedData
->tags
= tags
;
555 m_sharedData
->metaInfoLabels
= metaInfoLabels
;
556 m_sharedData
->metaInfoValues
= metaInfoValues
;
557 m_sharedData
->files
= files
;
560 QString
MetaDataWidget::Private::LoadFilesThread::tunedLabel(const QString
& label
) const
563 const int labelLength
= label
.length();
564 if (labelLength
> 0) {
565 tunedLabel
.reserve(labelLength
);
566 tunedLabel
= label
[0].toUpper();
567 for (int i
= 1; i
< labelLength
; ++i
) {
568 if (label
[i
].isUpper() && !label
[i
- 1].isSpace() && !label
[i
- 1].isUpper()) {
570 tunedLabel
+= label
[i
].toLower();
572 tunedLabel
+= label
[i
];
576 return tunedLabel
+ ':';
579 #endif // HAVE_NEPOMUK
581 MetaDataWidget::MetaDataWidget(QWidget
* parent
) :
587 MetaDataWidget::~MetaDataWidget()
592 void MetaDataWidget::setItem(const KFileItem
& item
)
594 // update values for "type", "size", "modified",
595 // "owner" and "permissions" synchronously
596 d
->m_sizeLabel
->setText(i18nc("@label", "Size:"));
598 d
->m_typeInfo
->setText(i18nc("@label", "Folder"));
599 d
->setRowVisible(d
->m_sizeInfo
, false);
601 d
->m_typeInfo
->setText(item
.mimeComment());
602 d
->m_sizeInfo
->setText(KIO::convertSize(item
.size()));
603 d
->setRowVisible(d
->m_sizeInfo
, d
->m_isSizeVisible
);
605 d
->m_modifiedInfo
->setText(item
.timeString());
606 d
->m_ownerInfo
->setText(item
.user());
607 d
->m_permissionsInfo
->setText(item
.permissionsString());
609 setItems(KFileItemList() << item
);
612 void MetaDataWidget::setItems(const KFileItemList
& items
)
614 d
->m_fileItems
= items
;
616 if (items
.count() > 1) {
617 // calculate the size of all items and show this
618 // information to the user
619 d
->m_sizeLabel
->setText(i18nc("@label", "Total Size:"));
620 d
->setRowVisible(d
->m_sizeInfo
, d
->m_isSizeVisible
);
622 quint64 totalSize
= 0;
623 foreach (const KFileItem
& item
, items
) {
624 if (!item
.isDir() && !item
.isLink()) {
625 totalSize
+= item
.size();
628 d
->m_sizeInfo
->setText(KIO::convertSize(totalSize
));
632 if (Nepomuk::ResourceManager::instance()->init() == 0) {
634 foreach (const KFileItem
& item
, items
) {
635 const KUrl url
= item
.nepomukUri();
640 d
->m_loadFilesThread
->loadFiles(urls
);
645 void MetaDataWidget::setItem(const KUrl
& url
)
647 KFileItem
item(KFileItem::Unknown
, KFileItem::Unknown
, url
);
652 void MetaDataWidget::setItems(const QList
<KUrl
>& urls
)
655 foreach (const KUrl
& url
, urls
) {
656 KFileItem
item(KFileItem::Unknown
, KFileItem::Unknown
, url
);
663 KFileItemList
MetaDataWidget::items() const
665 return d
->m_fileItems
;
668 void MetaDataWidget::setHiddenData(MetaDataTypes data
)
670 d
->m_hiddenData
= data
;
671 d
->updateRowsVisibility();
674 MetaDataWidget::MetaDataTypes
MetaDataWidget::hiddenData() const
676 return d
->m_hiddenData
;
679 unsigned int MetaDataWidget::rating() const
682 QMutexLocker
locker(&d
->m_mutex
);
683 return d
->m_sharedData
.rating
;
689 QList
<Nepomuk::Tag
> MetaDataWidget::tags() const
692 QMutexLocker
locker(&d
->m_mutex
);
693 return d
->m_sharedData
.tags
;
695 return QList
<Nepomuk::Tag
>();
699 QString
MetaDataWidget::comment() const
702 QMutexLocker
locker(&d
->m_mutex
);
703 return d
->m_sharedData
.comment
;
709 #include "metadatawidget.moc"