1 /*****************************************************************************
2 * Copyright (C) 2008 by Sebastian Trueg <trueg@kde.org> *
3 * Copyright (C) 2009 by Peter Penz <peter.penz@gmx.at> *
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. *
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. *
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 *****************************************************************************/
20 #include "kmetadatawidget.h"
23 #include <kconfiggroup.h>
24 #include <kfileitem.h>
25 #include <kglobalsettings.h>
30 #include <QFontMetrics>
31 #include <QGridLayout>
36 #include <config-nepomuk.h>
38 #define DISABLE_NEPOMUK_LEGACY
40 #include "kcommentwidget_p.h"
41 #include "kloadmetadatathread_p.h"
42 #include "ktaggingwidget_p.h"
44 #include <nepomuk/kratingwidget.h>
45 #include <nepomuk/resource.h>
46 #include <nepomuk/resourcemanager.h>
47 #include <nepomuk/property.h>
48 #include <nepomuk/tag.h>
49 #include <nepomuk/variant.h>
50 #include "nepomukmassupdatejob_p.h"
53 #include <QSpacerItem>
62 class KMetaDataWidget::Private
71 Private(KMetaDataWidget
* parent
);
74 void addRow(QLabel
* label
, QWidget
* infoWidget
);
75 void removeMetaInfoRows();
76 void setRowVisible(QWidget
* infoWidget
, bool visible
);
79 * Initializes the configuration file "kmetainformationrc"
80 * with proper default settings for the first start in
81 * an uninitialized environment.
83 void initMetaInfoSettings();
86 * Parses the configuration file "kmetainformationrc" and
87 * updates the visibility of all rows.
89 void updateRowsVisibility();
91 void slotLoadingFinished();
93 void slotRatingChanged(unsigned int rating
);
94 void slotTagsChanged(const QList
<Nepomuk::Tag
>& tags
);
95 void slotCommentChanged(const QString
& comment
);
97 void slotMetaDataUpdateDone();
98 void slotLinkActivated(const QString
& link
);
102 * Disables the metadata widget and starts the job that
103 * changes the meta data asynchronously. After the job
104 * has been finished, the metadata widget gets enabled again.
106 void startChangeDataJob(KJob
* job
);
109 * Merges items like 'width' and 'height' as one item.
111 QList
<KLoadMetaDataThread::Item
> mergedItems(const QList
<KLoadMetaDataThread::Item
>& items
);
116 bool m_nepomukActivated
;
117 MetaDataTypes m_visibleDataTypes
;
118 QList
<KFileItem
> m_fileItems
;
121 QGridLayout
* m_gridLayout
;
126 QLabel
* m_modifiedInfo
;
128 QLabel
* m_permissionsInfo
;
131 KRatingWidget
* m_ratingWidget
;
132 KTaggingWidget
* m_taggingWidget
;
133 KCommentWidget
* m_commentWidget
;
135 QMap
<KUrl
, Nepomuk::Resource
> m_files
;
137 KLoadMetaDataThread
* m_loadMetaDataThread
;
141 KMetaDataWidget
* const q
;
144 KMetaDataWidget::Private::Private(KMetaDataWidget
* parent
) :
147 m_nepomukActivated(false),
148 m_visibleDataTypes(TypeData
| SizeData
| ModifiedData
| OwnerData
|
149 PermissionsData
| RatingData
| TagsData
| CommentData
),
158 m_permissionsInfo(0),
164 m_loadMetaDataThread(0),
168 const QFontMetrics
fontMetrics(KGlobalSettings::smallestReadableFont());
170 m_gridLayout
= new QGridLayout(parent
);
171 m_gridLayout
->setMargin(0);
172 m_gridLayout
->setSpacing(fontMetrics
.height() / 4);
174 m_typeInfo
= new QLabel(parent
);
175 m_sizeLabel
= new QLabel(parent
);
176 m_sizeInfo
= new QLabel(parent
);
177 m_modifiedInfo
= new QLabel(parent
);
178 m_ownerInfo
= new QLabel(parent
);
179 m_permissionsInfo
= new QLabel(parent
);
182 m_nepomukActivated
= (Nepomuk::ResourceManager::instance()->init() == 0);
183 if (m_nepomukActivated
) {
184 m_ratingWidget
= new KRatingWidget(parent
);
185 m_ratingWidget
->setFixedHeight(fontMetrics
.height());
186 connect(m_ratingWidget
, SIGNAL(ratingChanged(unsigned int)),
187 q
, SLOT(slotRatingChanged(unsigned int)));
189 m_taggingWidget
= new KTaggingWidget(parent
);
190 connect(m_taggingWidget
, SIGNAL(tagsChanged(const QList
<Nepomuk::Tag
>&)),
191 q
, SLOT(slotTagsChanged(const QList
<Nepomuk::Tag
>&)));
193 m_commentWidget
= new KCommentWidget(parent
);
194 connect(m_commentWidget
, SIGNAL(commentChanged(const QString
&)),
195 q
, SLOT(slotCommentChanged(const QString
&)));
199 initMetaInfoSettings();
202 KMetaDataWidget::Private::~Private()
205 if (m_loadMetaDataThread
!= 0) {
206 disconnect(m_loadMetaDataThread
, SIGNAL(finished()), q
, SLOT(slotLoadingFinished()));
207 m_loadMetaDataThread
->cancelAndDelete();
208 m_loadMetaDataThread
= 0;
213 void KMetaDataWidget::Private::addRow(QLabel
* label
, QWidget
* infoWidget
)
217 row
.infoWidget
= infoWidget
;
220 const QFont smallFont
= KGlobalSettings::smallestReadableFont();
221 // use a brighter color for the label and a small font size
222 QPalette palette
= label
->palette();
223 const QPalette::ColorRole role
= q
->foregroundRole();
224 QColor textColor
= palette
.color(role
);
225 textColor
.setAlpha(128);
226 palette
.setColor(role
, textColor
);
227 label
->setPalette(palette
);
228 label
->setForegroundRole(role
);
229 label
->setFont(smallFont
);
230 label
->setWordWrap(true);
231 label
->setAlignment(Qt::AlignTop
| Qt::AlignRight
);
233 infoWidget
->setForegroundRole(role
);
234 QLabel
* infoLabel
= qobject_cast
<QLabel
*>(infoWidget
);
235 if (infoLabel
!= 0) {
236 infoLabel
->setFont(smallFont
);
237 infoLabel
->setWordWrap(true);
238 infoLabel
->setAlignment(Qt::AlignTop
| Qt::AlignLeft
);
241 // add the row to grid layout
242 const int rowIndex
= m_rows
.count();
243 m_gridLayout
->addWidget(label
, rowIndex
, 0, Qt::AlignRight
);
244 const int spacerWidth
= QFontMetrics(smallFont
).size(Qt::TextSingleLine
, " ").width();
245 m_gridLayout
->addItem(new QSpacerItem(spacerWidth
, 1), rowIndex
, 1);
246 m_gridLayout
->addWidget(infoWidget
, rowIndex
, 2, Qt::AlignLeft
);
249 void KMetaDataWidget::Private::setRowVisible(QWidget
* infoWidget
, bool visible
)
251 foreach (const Row
& row
, m_rows
) {
252 if (row
.infoWidget
== infoWidget
) {
253 row
.label
->setVisible(visible
);
254 row
.infoWidget
->setVisible(visible
);
261 void KMetaDataWidget::Private::initMetaInfoSettings()
263 KConfig
config("kmetainformationrc", KConfig::NoGlobals
);
264 KConfigGroup settings
= config
.group("Show");
265 if (!settings
.readEntry("initialized", false)) {
266 // The resource file is read the first time. Assure
267 // that some meta information is disabled per default.
269 static const char* disabledProperties
[] = {
270 "asText", "contentSize", "created", "depth", "description", "fileExtension",
271 "fileName", "fileSize", "hasTag", "isPartOf", "lastModified", "mimeType", "name",
272 "numericRating", "parentUrl", "permissions", "plainTextContent", "owner",
273 "sourceModified", "url",
274 0 // mandatory last entry
278 while (disabledProperties
[i
] != 0) {
279 settings
.writeEntry(disabledProperties
[i
], false);
283 // mark the group as initialized
284 settings
.writeEntry("initialized", true);
288 void KMetaDataWidget::Private::updateRowsVisibility()
290 KConfig
config("kmetainformationrc", KConfig::NoGlobals
);
291 KConfigGroup settings
= config
.group("Show");
293 setRowVisible(m_typeInfo
,
294 (m_visibleDataTypes
& KMetaDataWidget::TypeData
) &&
295 settings
.readEntry("type", true));
297 // Cache in m_sizeVisible whether the size should be shown. This
298 // is necessary as the size is temporary hidden when the target
299 // file item is a directory.
300 m_sizeVisible
= (m_visibleDataTypes
& KMetaDataWidget::SizeData
) &&
301 settings
.readEntry("size", true);
302 setRowVisible(m_sizeInfo
, m_sizeVisible
);
304 setRowVisible(m_modifiedInfo
,
305 (m_visibleDataTypes
& KMetaDataWidget::ModifiedData
) &&
306 settings
.readEntry("modified", true));
308 setRowVisible(m_ownerInfo
,
309 (m_visibleDataTypes
& KMetaDataWidget::OwnerData
) &&
310 settings
.readEntry("owner", true));
312 setRowVisible(m_permissionsInfo
,
313 (m_visibleDataTypes
& KMetaDataWidget::PermissionsData
) &&
314 settings
.readEntry("permissions", true));
317 if (m_nepomukActivated
) {
318 setRowVisible(m_ratingWidget
,
319 (m_visibleDataTypes
& KMetaDataWidget::RatingData
) &&
320 settings
.readEntry("rating", true));
322 setRowVisible(m_taggingWidget
,
323 (m_visibleDataTypes
& KMetaDataWidget::TagsData
) &&
324 settings
.readEntry("tags", true));
326 setRowVisible(m_commentWidget
,
327 (m_visibleDataTypes
& KMetaDataWidget::CommentData
) &&
328 settings
.readEntry("comment", true));
333 void KMetaDataWidget::Private::slotLoadingFinished()
336 Q_ASSERT(m_loadMetaDataThread
!= 0);
337 Q_ASSERT(m_ratingWidget
!= 0);
338 Q_ASSERT(m_commentWidget
!= 0);
339 Q_ASSERT(m_taggingWidget
!= 0);
340 m_ratingWidget
->setRating(m_loadMetaDataThread
->rating());
341 m_commentWidget
->setText(m_loadMetaDataThread
->comment());
342 m_taggingWidget
->setTags(m_loadMetaDataThread
->tags());
344 // Show the remaining meta information as text. The number
345 // of required rows may very. Existing rows are reused to
346 // prevent flickering.
347 int index
= 8; // TODO: don't hardcode this value here
348 const int rowCount
= m_rows
.count();
349 Q_ASSERT(rowCount
>= index
);
351 const QList
<KLoadMetaDataThread::Item
> items
= mergedItems(m_loadMetaDataThread
->items());
352 foreach (const KLoadMetaDataThread::Item
& item
, items
) {
353 if (index
< rowCount
) {
354 // adjust texts of the current row
355 m_rows
[index
].label
->setText(item
.label
);
356 QLabel
* infoValueLabel
= qobject_cast
<QLabel
*>(m_rows
[index
].infoWidget
);
357 Q_ASSERT(infoValueLabel
!= 0);
358 infoValueLabel
->setText(item
.value
);
361 QLabel
* infoLabel
= new QLabel(item
.label
, q
);
362 QLabel
* infoValue
= new QLabel(item
.value
, q
);
363 connect(infoValue
, SIGNAL(linkActivated(QString
)),
364 q
, SLOT(slotLinkActivated(QString
)));
365 addRow(infoLabel
, infoValue
);
369 if (items
.count() > 0) {
373 // remove rows that are not needed anymore
374 for (int i
= m_rows
.count() - 1; i
>= index
; --i
) {
375 delete m_rows
[i
].label
;
376 delete m_rows
[i
].infoWidget
;
380 m_files
= m_loadMetaDataThread
->files();
382 delete m_loadMetaDataThread
;
383 m_loadMetaDataThread
= 0;
389 void KMetaDataWidget::Private::slotRatingChanged(unsigned int rating
)
392 Nepomuk::MassUpdateJob
* job
=
393 Nepomuk::MassUpdateJob::rateResources(m_files
.values(), rating
);
394 startChangeDataJob(job
);
400 void KMetaDataWidget::Private::slotTagsChanged(const QList
<Nepomuk::Tag
>& tags
)
403 m_taggingWidget
->setTags(tags
);
405 Nepomuk::MassUpdateJob
* job
=
406 Nepomuk::MassUpdateJob::tagResources(m_files
.values(), tags
);
407 startChangeDataJob(job
);
413 void KMetaDataWidget::Private::slotCommentChanged(const QString
& comment
)
416 Nepomuk::MassUpdateJob
* job
=
417 Nepomuk::MassUpdateJob::commentResources(m_files
.values(), comment
);
418 startChangeDataJob(job
);
424 void KMetaDataWidget::Private::slotMetaDataUpdateDone()
431 void KMetaDataWidget::Private::slotLinkActivated(const QString
& link
)
433 emit q
->urlActivated(KUrl(link
));
437 void KMetaDataWidget::Private::startChangeDataJob(KJob
* job
)
439 connect(job
, SIGNAL(result(KJob
*)),
440 q
, SLOT(slotMetaDataUpdateDone()));
441 q
->setEnabled(false); // no updates during execution
445 QList
<KLoadMetaDataThread::Item
>
446 KMetaDataWidget::Private::mergedItems(const QList
<KLoadMetaDataThread::Item
>& items
)
448 // TODO: Currently only "width" and "height" are merged as "width x height". If
449 // other kind of merges should be done too, a more general approach is required.
450 QList
<KLoadMetaDataThread::Item
> mergedItems
;
452 KLoadMetaDataThread::Item width
;
453 KLoadMetaDataThread::Item height
;
455 foreach (const KLoadMetaDataThread::Item
& item
, items
) {
456 if (item
.name
== "width") {
458 } else if (item
.name
== "height") {
461 // insert the item sorted by the label
462 bool inserted
= false;
464 const int count
= mergedItems
.count();
465 while (!inserted
&& (i
< count
)) {
466 if (item
.label
< mergedItems
[i
].label
) {
467 mergedItems
.insert(i
, item
);
473 mergedItems
.append(item
);
478 const bool foundWidth
= !width
.name
.isEmpty();
479 const bool foundHeight
= !height
.name
.isEmpty();
480 if (foundWidth
&& !foundHeight
) {
481 mergedItems
.insert(0, width
);
482 } else if (foundHeight
&& !foundWidth
) {
483 mergedItems
.insert(0, height
);
484 } else if (foundWidth
&& foundHeight
) {
485 KLoadMetaDataThread::Item size
;
486 size
.label
= i18nc("@label", "Width x Height:");
487 size
.value
= width
.value
+ " x " + height
.value
;
488 mergedItems
.insert(0, size
);
495 KMetaDataWidget::KMetaDataWidget(QWidget
* parent
) :
501 KMetaDataWidget::~KMetaDataWidget()
506 void KMetaDataWidget::setItem(const KFileItem
& item
)
508 // update values for "type", "size", "modified",
509 // "owner" and "permissions" synchronously
510 d
->m_sizeLabel
->setText(i18nc("@label", "Size:"));
512 d
->m_typeInfo
->setText(i18nc("@label", "Folder"));
513 d
->setRowVisible(d
->m_sizeInfo
, false);
515 d
->m_typeInfo
->setText(item
.mimeComment());
516 d
->m_sizeInfo
->setText(KIO::convertSize(item
.size()));
517 d
->setRowVisible(d
->m_sizeInfo
, d
->m_sizeVisible
);
519 d
->m_modifiedInfo
->setText(KGlobal::locale()->formatDateTime(item
.time(KFileItem::ModificationTime
), KLocale::FancyLongDate
));
520 d
->m_ownerInfo
->setText(item
.user());
521 d
->m_permissionsInfo
->setText(item
.permissionsString());
523 setItems(KFileItemList() << item
);
526 void KMetaDataWidget::setItems(const KFileItemList
& items
)
528 d
->m_fileItems
= items
;
530 if (items
.count() > 1) {
531 // calculate the size of all items and show this
532 // information to the user
533 d
->m_sizeLabel
->setText(i18nc("@label", "Total Size:"));
534 d
->setRowVisible(d
->m_sizeInfo
, d
->m_sizeVisible
);
536 quint64 totalSize
= 0;
537 foreach (const KFileItem
& item
, items
) {
538 if (!item
.isDir() && !item
.isLink()) {
539 totalSize
+= item
.size();
542 d
->m_sizeInfo
->setText(KIO::convertSize(totalSize
));
546 if (d
->m_nepomukActivated
) {
548 foreach (const KFileItem
& item
, items
) {
549 const KUrl url
= item
.nepomukUri();
555 if (d
->m_loadMetaDataThread
!= 0) {
556 disconnect(d
->m_loadMetaDataThread
, SIGNAL(finished()), this, SLOT(slotLoadingFinished()));
557 d
->m_loadMetaDataThread
->cancelAndDelete();
560 d
->m_loadMetaDataThread
= new KLoadMetaDataThread();
561 connect(d
->m_loadMetaDataThread
, SIGNAL(finished()), this, SLOT(slotLoadingFinished()));
562 d
->m_loadMetaDataThread
->load(urls
);
567 void KMetaDataWidget::setItem(const KUrl
& url
)
569 KFileItem
item(KFileItem::Unknown
, KFileItem::Unknown
, url
);
574 void KMetaDataWidget::setItems(const QList
<KUrl
>& urls
)
577 foreach (const KUrl
& url
, urls
) {
578 KFileItem
item(KFileItem::Unknown
, KFileItem::Unknown
, url
);
585 KFileItemList
KMetaDataWidget::items() const
587 return d
->m_fileItems
;
590 void KMetaDataWidget::setReadOnly(bool readOnly
)
592 d
->m_readOnly
= readOnly
;
594 // TODO: encapsulate this code as part of a metadata-model for KDE 4.5
595 if (d
->m_taggingWidget
)
596 d
->m_taggingWidget
->setReadOnly(readOnly
);
597 if (d
->m_commentWidget
)
598 d
->m_commentWidget
->setReadOnly(readOnly
);
602 bool KMetaDataWidget::isReadOnly() const
604 return d
->m_readOnly
;
607 void KMetaDataWidget::setVisibleDataTypes(MetaDataTypes data
)
609 d
->m_visibleDataTypes
= data
;
610 d
->updateRowsVisibility();
613 KMetaDataWidget::MetaDataTypes
KMetaDataWidget::visibleDataTypes() const
615 return d
->m_visibleDataTypes
;
618 QSize
KMetaDataWidget::sizeHint() const
620 const int fixedWidth
= 200;
622 int height
= d
->m_gridLayout
->margin() * 2 +
623 d
->m_gridLayout
->spacing() * (d
->m_rows
.count() - 1);
625 foreach (const Private::Row
& row
, d
->m_rows
) {
626 if (row
.infoWidget
!= 0) {
627 int rowHeight
= row
.infoWidget
->heightForWidth(fixedWidth
/ 2);
628 if (rowHeight
<= 0) {
629 rowHeight
= row
.infoWidget
->sizeHint().height();
635 return QSize(fixedWidth
, height
);
638 bool KMetaDataWidget::event(QEvent
* event
)
640 if (event
->type() == QEvent::Polish
) {
641 // The adding of rows is not done in the constructor. This allows the
642 // client of KMetaDataWidget to set a proper foreground role which
643 // will be respected by the rows.
645 d
->addRow(new QLabel(i18nc("@label", "Type:"), this), d
->m_typeInfo
);
646 d
->addRow(d
->m_sizeLabel
, d
->m_sizeInfo
);
647 d
->addRow(new QLabel(i18nc("@label", "Modified:"), this), d
->m_modifiedInfo
);
648 d
->addRow(new QLabel(i18nc("@label", "Owner:"), this), d
->m_ownerInfo
);
649 d
->addRow(new QLabel(i18nc("@label", "Permissions:"), this), d
->m_permissionsInfo
);
652 if (d
->m_nepomukActivated
) {
653 d
->addRow(new QLabel(i18nc("@label", "Rating:"), this), d
->m_ratingWidget
);
654 d
->addRow(new QLabel(i18nc("@label", "Tags:"), this), d
->m_taggingWidget
);
655 d
->addRow(new QLabel(i18nc("@label", "Comment:"), this), d
->m_commentWidget
);
659 d
->updateRowsVisibility();
662 return QWidget::event(event
);
665 #include "kmetadatawidget.moc"