18
18
***************************************************************************/
20
20
#include "informationpanel.h"
22
#include <config-nepomuk.h>
25
21
#include <kdirnotify.h>
26
#include <kfileitem.h>
27
#include <kfilemetainfo.h>
28
#include <kfileplacesmodel.h>
29
#include <kglobalsettings.h>
30
#include <kio/previewjob.h>
31
#include <kiconeffect.h>
32
#include <kiconloader.h>
35
#include <kseparator.h>
39
#define DISABLE_NEPOMUK_LEGACY
41
#include <Nepomuk/Resource>
42
#include <Nepomuk/Types/Property>
43
#include <Nepomuk/Variant>
46
#include <Phonon/BackendCapabilities>
47
#include <Phonon/MediaObject>
48
#include <Phonon/SeekSlider>
51
#include <QInputDialog>
55
#include <QResizeEvent>
56
#include <QScrollArea>
57
#include <QTextLayout>
61
#include <QVBoxLayout>
63
#include "dolphin_informationpanelsettings.h"
64
#include "settings/dolphinsettings.h"
65
#include "metadatawidget.h"
66
#include "metatextlabel.h"
67
#include "phononwidget.h"
68
#include "pixmapviewer.h"
71
* Helper function for sorting items with qSort() in
72
* InformationPanel::contextMenu().
74
bool lessThan(const QAction* action1, const QAction* action2)
76
return action1->text() < action2->text();
23
#include "informationpanelcontent.h"
80
25
InformationPanel::InformationPanel(QWidget* parent) :
82
27
m_initialized(false),
83
m_pendingPreview(false),
85
m_outdatedPreviewTimer(0),
86
29
m_urlChangedTimer(0),
87
30
m_resetUrlTimer(0),
204
139
void InformationPanel::resizeEvent(QResizeEvent* event)
206
141
if (isVisible()) {
207
// If the text inside the name label or the info label cannot
208
// get wrapped, then the maximum width of the label is increased
209
// so that the width of the information panel gets increased.
210
// To prevent this, the maximum width is adjusted to
211
// the current width of the panel.
212
const int maxWidth = event->size().width() - KDialog::spacingHint() * 4;
213
m_nameLabel->setMaximumWidth(maxWidth);
215
// The metadata widget also contains a text widget which may return
216
// a large preferred width.
217
if (m_metaDataWidget != 0) {
218
m_metaDataWidget->setMaximumWidth(maxWidth);
221
// try to increase the preview as large as possible
222
m_preview->setSizeHint(QSize(maxWidth, maxWidth));
223
m_urlCandidate = m_shownUrl; // reset the URL candidate if a resizing is done
142
m_urlCandidate = m_shownUrl;
224
143
m_infoTimer->start();
226
if (m_phononWidget->isVisible() && (m_phononWidget->mode() == PhononWidget::Video)) {
227
// assure that the size of the video player is the same as the preview size
228
m_phononWidget->setVideoSize(QSize(maxWidth, maxWidth));
231
145
Panel::resizeEvent(event);
234
bool InformationPanel::eventFilter(QObject* obj, QEvent* event)
236
// Check whether the size of the meta text area has changed and adjust
237
// the fixed width in a way that no horizontal scrollbar needs to be shown.
238
if ((obj == m_metaTextArea->viewport()) && (event->type() == QEvent::Resize)) {
239
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
240
m_metaTextLabel->setFixedWidth(resizeEvent->size().width());
242
return Panel::eventFilter(obj, event);
245
148
void InformationPanel::contextMenuEvent(QContextMenuEvent* event)
150
m_content->configureSettings();
247
151
Panel::contextMenuEvent(event);
250
if (showMultipleSelectionInfo()) {
256
QAction* previewAction = popup.addAction(i18nc("@action:inmenu", "Preview"));
257
previewAction->setIcon(KIcon("view-preview"));
258
previewAction->setCheckable(true);
259
previewAction->setChecked(InformationPanelSettings::showPreview());
261
const bool metaDataAvailable = MetaDataWidget::metaDataAvailable();
263
QAction* ratingAction = popup.addAction(i18nc("@action:inmenu", "Rating"));
264
ratingAction->setIcon(KIcon("rating"));
265
ratingAction->setCheckable(true);
266
ratingAction->setChecked(InformationPanelSettings::showRating());
267
ratingAction->setEnabled(metaDataAvailable);
269
QAction* commentAction = popup.addAction(i18nc("@action:inmenu", "Comment"));
270
commentAction->setIcon(KIcon("text-plain"));
271
commentAction->setCheckable(true);
272
commentAction->setChecked(InformationPanelSettings::showComment());
273
commentAction->setEnabled(metaDataAvailable);
275
QAction* tagsAction = popup.addAction(i18nc("@action:inmenu", "Tags"));
276
tagsAction->setCheckable(true);
277
tagsAction->setChecked(InformationPanelSettings::showTags());
278
tagsAction->setEnabled(metaDataAvailable);
280
KConfig config("kmetainformationrc", KConfig::NoGlobals);
281
KConfigGroup settings = config.group("Show");
282
initMetaInfoSettings(settings);
284
QList<QAction*> actions;
286
// Get all meta information labels that are available for
287
// the currently shown file item and add them to the popup.
288
Nepomuk::Resource res(updateFileItem().url());
289
QHash<QUrl, Nepomuk::Variant> properties = res.properties();
290
QHash<QUrl, Nepomuk::Variant>::const_iterator it = properties.constBegin();
291
while (it != properties.constEnd()) {
292
Nepomuk::Types::Property prop(it.key());
293
const QString key = prop.name();
295
// Meta information provided by Nepomuk that is already
296
// available from KFileItem should not be configurable.
297
bool skip = (key == "fileExtension") ||
299
(key == "sourceModified") ||
301
(key == "mime type");
303
// Check whether there is already a meta information
304
// having the same label. In this case don't show it
305
// twice in the menu.
306
foreach (const QAction* action, actions) {
307
if (action->data().toString() == key) {
315
const QString label = tunedLabel(prop.label());
316
QAction* action = new QAction(label, &popup);
317
action->setCheckable(true);
318
action->setChecked(settings.readEntry(key, true));
319
action->setData(key);
320
actions.append(action);
326
if (actions.count() > 0) {
327
popup.addSeparator();
329
// add all items alphabetically sorted to the popup
330
qSort(actions.begin(), actions.end(), lessThan);
331
foreach (QAction* action, actions) {
332
popup.addAction(action);
336
// Open the popup and adjust the settings for the
338
QAction* action = popup.exec(QCursor::pos());
343
const bool isChecked = action->isChecked();
344
if (action == previewAction) {
345
m_preview->setVisible(isChecked);
346
m_previewSeparator->setVisible(isChecked);
347
InformationPanelSettings::setShowPreview(isChecked);
348
updatePhononWidget();
349
} else if (action == ratingAction) {
350
m_metaDataWidget->setRatingVisible(isChecked);
351
InformationPanelSettings::setShowRating(isChecked);
352
} else if (action == commentAction) {
353
m_metaDataWidget->setCommentVisible(isChecked);
354
InformationPanelSettings::setShowComment(isChecked);
355
} else if (action == tagsAction) {
356
m_metaDataWidget->setTagsVisible(isChecked);
357
InformationPanelSettings::setShowTags(isChecked);
359
settings.writeEntry(action->data().toString(), action->isChecked());
364
if (m_metaDataWidget != 0) {
365
const bool visible = m_metaDataWidget->isRatingVisible() ||
366
m_metaDataWidget->isCommentVisible() ||
367
m_metaDataWidget->areTagsVisible();
368
m_metaDataSeparator->setVisible(visible);
373
154
void InformationPanel::showItemInfo()
381
162
if (showMultipleSelectionInfo()) {
382
KIconLoader iconLoader;
383
QPixmap icon = iconLoader.loadIcon("dialog-information",
384
KIconLoader::NoGroup,
385
KIconLoader::SizeEnormous);
386
m_preview->setPixmap(icon);
387
setNameLabelText(i18ncp("@info", "%1 item selected", "%1 items selected", m_selection.count()));
163
m_content->showItems(m_selection);
388
164
m_shownUrl = KUrl();
390
const KFileItem item = updateFileItem();
391
const KUrl itemUrl = item.url();
392
if (!applyPlace(itemUrl)) {
393
// try to get a preview pixmap from the item...
394
m_pendingPreview = true;
396
// Mark the currently shown preview as outdated. This is done
397
// with a small delay to prevent a flickering when the next preview
398
// can be shown within a short timeframe. This timer is not started
399
// for directories, as directory previews might fail and return the
402
m_outdatedPreviewTimer->start();
405
KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << item,
413
connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
414
this, SLOT(showPreview(const KFileItem&, const QPixmap&)));
415
connect(job, SIGNAL(failed(const KFileItem&)),
416
this, SLOT(showIcon(const KFileItem&)));
418
setNameLabelText(itemUrl.fileName());
167
if (!m_fileItem.isNull()) {
169
} else if (!m_selection.isEmpty()) {
170
Q_ASSERT(m_selection.count() == 1);
171
item = m_selection.first();
173
// no item is hovered and no selection has been done: provide
174
// an item for the directory represented by m_shownUrl
175
item = KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_shownUrl);
179
m_content->showItem(item);
425
183
void InformationPanel::slotInfoTimeout()
527
264
// The signal 'leftDirectory' is also emitted when a media
528
265
// has been unmounted. In this case no directory change will be
529
266
// done in Dolphin, but the Information Panel must be updated to
530
// indicate an invalid directory.
267
// indicate an invalid directory.
531
268
markUrlAsInvalid();
535
void InformationPanel::slotPlayingStarted()
537
m_preview->setVisible(m_phononWidget->mode() != PhononWidget::Video);
540
void InformationPanel::slotPlayingStopped()
542
m_preview->setVisible(true);
545
bool InformationPanel::applyPlace(const KUrl& url)
547
KFilePlacesModel* placesModel = DolphinSettings::instance().placesModel();
548
int count = placesModel->rowCount();
550
for (int i = 0; i < count; ++i) {
551
QModelIndex index = placesModel->index(i, 0);
553
if (url.equals(placesModel->url(index), KUrl::CompareWithoutTrailingSlash)) {
554
setNameLabelText(placesModel->text(index));
555
m_preview->setPixmap(placesModel->icon(index).pixmap(128, 128));
563
272
void InformationPanel::cancelRequest()
565
274
m_infoTimer->stop();
568
void InformationPanel::showMetaInfo()
570
m_metaTextLabel->clear();
572
if (showMultipleSelectionInfo()) {
573
if (m_metaDataWidget != 0) {
575
foreach (const KFileItem& item, m_selection) {
576
urls.append(item.targetUrl());
578
m_metaDataWidget->setFiles(urls);
581
quint64 totalSize = 0;
582
foreach (const KFileItem& item, m_selection) {
583
// Only count the size of files, not dirs to match what
584
// DolphinViewContainer::selectionStatusBarText() does.
585
if (!item.isDir() && !item.isLink()) {
586
totalSize += item.size();
589
m_metaTextLabel->add(i18nc("@label", "Total size:"), KIO::convertSize(totalSize));
591
const KFileItem item = updateFileItem();
593
m_metaTextLabel->add(i18nc("@label", "Type:"), i18nc("@label", "Folder"));
594
m_metaTextLabel->add(i18nc("@label", "Modified:"), item.timeString());
596
m_metaTextLabel->add(i18nc("@label", "Type:"), item.mimeComment());
598
m_metaTextLabel->add(i18nc("@label", "Size:"), KIO::convertSize(item.size()));
599
m_metaTextLabel->add(i18nc("@label", "Modified:"), item.timeString());
602
KConfig config("kmetainformationrc", KConfig::NoGlobals);
603
KConfigGroup settings = config.group("Show");
604
initMetaInfoSettings(settings);
606
Nepomuk::Resource res(item.url());
608
QHash<QUrl, Nepomuk::Variant> properties = res.properties();
609
QHash<QUrl, Nepomuk::Variant>::const_iterator it = properties.constBegin();
610
while (it != properties.constEnd()) {
611
Nepomuk::Types::Property prop(it.key());
612
if (settings.readEntry(prop.name(), true)) {
613
// TODO #1: use Nepomuk::formatValue(res, prop) if available
614
// instead of it.value().toString()
615
// TODO #2: using tunedLabel() is a workaround for KDE 4.3 until
616
// we get translated labels
617
m_metaTextLabel->add(tunedLabel(prop.label()) + ':', it.value().toString());
624
if (m_metaDataWidget != 0) {
625
m_metaDataWidget->setFile(item.targetUrl());
629
updatePhononWidget();
632
KFileItem InformationPanel::updateFileItem()
634
if (!m_fileItem.isNull()) {
638
if (!m_selection.isEmpty()) {
639
Q_ASSERT(m_selection.count() == 1);
640
return m_selection.first();
643
// no item is hovered and no selection has been done: provide
644
// an item for the directory represented by m_shownUrl
645
m_fileItem = KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_shownUrl);
646
m_fileItem.refresh();
650
277
bool InformationPanel::showMultipleSelectionInfo() const
652
279
return m_fileItem.isNull() && (m_selection.count() > 1);
657
284
return m_shownUrl.equals(url, KUrl::CompareWithoutTrailingSlash);
660
void InformationPanel::setNameLabelText(const QString& text)
662
QTextOption textOption;
663
textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
665
QTextLayout textLayout(text);
666
textLayout.setFont(m_nameLabel->font());
667
textLayout.setTextOption(textOption);
670
wrappedText.reserve(text.length());
672
// wrap the text to fit into the width of m_nameLabel
673
textLayout.beginLayout();
674
QTextLine line = textLayout.createLine();
675
while (line.isValid()) {
676
line.setLineWidth(m_nameLabel->width());
677
wrappedText += text.mid(line.textStart(), line.textLength());
679
line = textLayout.createLine();
680
if (line.isValid()) {
681
wrappedText += QChar::LineSeparator;
684
textLayout.endLayout();
686
m_nameLabel->setText(wrappedText);
689
287
void InformationPanel::markUrlAsInvalid()
691
289
m_invalidUrlCandidate = m_shownUrl;
692
290
m_resetUrlTimer->start();
695
void InformationPanel::initMetaInfoSettings(KConfigGroup& group)
697
if (!group.readEntry("initialized", false)) {
698
// The resource file is read the first time. Assure
699
// that some meta information is disabled per default.
701
static const char* disabledProperties[] = {
702
"asText", "contentSize", "depth", "fileExtension",
703
"fileName", "fileSize", "isPartOf", "mimetype", "name",
704
"parentUrl", "plainTextContent", "sourceModified",
706
0 // mandatory last entry
710
while (disabledProperties[i] != 0) {
711
group.writeEntry(disabledProperties[i], false);
715
// mark the group as initialized
716
group.writeEntry("initialized", true);
720
void InformationPanel::updatePhononWidget()
722
const bool multipleSelections = showMultipleSelectionInfo();
723
const bool showPreview = InformationPanelSettings::showPreview();
725
if (multipleSelections || !showPreview) {
726
m_phononWidget->hide();
727
} else if (!multipleSelections && showPreview) {
728
const KFileItem item = updateFileItem();
729
const QString mimeType = item.mimetype();
730
const bool usePhonon = Phonon::BackendCapabilities::isMimeTypeAvailable(mimeType) &&
731
(mimeType != "image/png"); // TODO: workaround, as Phonon
732
// thinks it supports PNG images
734
m_phononWidget->show();
735
PhononWidget::Mode mode = mimeType.startsWith(QLatin1String("video"))
736
? PhononWidget::Video
737
: PhononWidget::Audio;
738
m_phononWidget->setMode(mode);
739
m_phononWidget->setUrl(item.url());
740
if ((mode == PhononWidget::Video) && m_preview->isVisible()) {
741
m_phononWidget->setVideoSize(m_preview->size());
744
m_phononWidget->hide();
745
m_preview->setVisible(true);
750
QString InformationPanel::tunedLabel(const QString& label) const
753
const int labelLength = label.length();
754
if (labelLength > 0) {
755
tunedLabel.reserve(labelLength);
756
tunedLabel = label[0].toUpper();
757
for (int i = 1; i < labelLength; ++i) {
758
if (label[i].isUpper() && !label[i - 1].isSpace() && !label[i - 1].isUpper()) {
760
tunedLabel += label[i].toLower();
762
tunedLabel += label[i];
769
293
void InformationPanel::init()
771
295
const int defaultDelay = 300;
797
312
connect(m_resetUrlTimer, SIGNAL(timeout()),
798
313
this, SLOT(reset()));
800
QVBoxLayout* layout = new QVBoxLayout;
801
layout->setSpacing(KDialog::spacingHint());
804
m_nameLabel = new QLabel(this);
805
QFont font = m_nameLabel->font();
807
m_nameLabel->setFont(font);
808
m_nameLabel->setAlignment(Qt::AlignHCenter);
809
m_nameLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
810
m_nameLabel->setMaximumWidth(KIconLoader::SizeEnormous);
813
const int minPreviewWidth = KIconLoader::SizeEnormous + KIconLoader::SizeMedium;
815
m_preview = new PixmapViewer(this);
816
m_preview->setMinimumWidth(minPreviewWidth);
817
m_preview->setMinimumHeight(KIconLoader::SizeEnormous);
819
m_phononWidget = new PhononWidget(this);
820
m_phononWidget->setMinimumWidth(minPreviewWidth);
821
connect(m_phononWidget, SIGNAL(playingStarted()),
822
this, SLOT(slotPlayingStarted()));
823
connect(m_phononWidget, SIGNAL(playingStopped()),
824
this, SLOT(slotPlayingStopped()));
826
m_previewSeparator = new KSeparator(this);
828
const bool showPreview = InformationPanelSettings::showPreview();
829
m_preview->setVisible(showPreview);
830
m_previewSeparator->setVisible(showPreview);
832
if (MetaDataWidget::metaDataAvailable()) {
833
// rating, comment and tags
834
m_metaDataWidget = new MetaDataWidget(this);
835
m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
836
m_metaDataWidget->setMaximumWidth(KIconLoader::SizeEnormous);
838
const bool showRating = InformationPanelSettings::showRating();
839
const bool showComment = InformationPanelSettings::showComment();
840
const bool showTags = InformationPanelSettings::showTags();
842
m_metaDataWidget->setRatingVisible(showRating);
843
m_metaDataWidget->setCommentVisible(showComment);
844
m_metaDataWidget->setTagsVisible(showTags);
846
m_metaDataSeparator = new KSeparator(this);
847
m_metaDataSeparator->setVisible(showRating || showComment || showTags);
850
// general meta text information
851
m_metaTextLabel = new MetaTextLabel(this);
852
m_metaTextLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
854
m_metaTextArea = new QScrollArea(this);
855
m_metaTextArea->setWidget(m_metaTextLabel);
856
m_metaTextArea->setWidgetResizable(true);
857
m_metaTextArea->setFrameShape(QFrame::NoFrame);
859
QWidget* viewport = m_metaTextArea->viewport();
860
viewport->installEventFilter(this);
862
QPalette palette = viewport->palette();
863
palette.setColor(viewport->backgroundRole(), QColor(Qt::transparent));
864
viewport->setPalette(palette);
866
layout->addWidget(m_nameLabel);
867
layout->addWidget(new KSeparator(this));
868
layout->addWidget(m_preview);
869
layout->addWidget(m_phononWidget);
870
layout->addWidget(m_previewSeparator);
871
if (m_metaDataWidget != 0) {
872
layout->addWidget(m_metaDataWidget);
873
layout->addWidget(m_metaDataSeparator);
875
layout->addWidget(m_metaTextArea);
878
315
org::kde::KDirNotify* dirNotify = new org::kde::KDirNotify(QString(), QString(),
879
316
QDBusConnection::sessionBus(), this);
880
317
connect(dirNotify, SIGNAL(FileRenamed(QString, QString)), SLOT(slotFileRenamed(QString, QString)));