]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/panels/information/informationpanel.cpp
Simplify how Nepomuk comments can be edited by the user:
[dolphin.git] / src / panels / information / informationpanel.cpp
index 000e7fdfbd5435f0c7f8cf517a1c6ba5073f3e17..2150c64659862522d28342488551de169be23024 100644 (file)
 
 #include <kdialog.h>
 #include <kdirnotify.h>
-#include <kfileplacesmodel.h>
-#include <klocale.h>
-#include <kstandarddirs.h>
-#include <kio/previewjob.h>
 #include <kfileitem.h>
-#include <kglobalsettings.h>
 #include <kfilemetainfo.h>
+#include <kfileplacesmodel.h>
+#include <kglobalsettings.h>
+#include <kio/previewjob.h>
 #include <kiconeffect.h>
-#include <kseparator.h>
 #include <kiconloader.h>
+#include <klocale.h>
+#include <kmenu.h>
+#include <kseparator.h>
+
+#ifdef HAVE_NEPOMUK
+#include <Nepomuk/Resource>
+#include <Nepomuk/Types/Property>
+#include <Nepomuk/Variant>
+#endif
 
 #include <Phonon/BackendCapabilities>
 #include <Phonon/MediaObject>
 #include <QPainter>
 #include <QPixmap>
 #include <QResizeEvent>
+#include <QScrollArea>
 #include <QTextLayout>
 #include <QTextLine>
 #include <QTimer>
+#include <QScrollBar>
 #include <QVBoxLayout>
 
+#include "dolphin_informationpanelsettings.h"
 #include "settings/dolphinsettings.h"
 #include "metadatawidget.h"
 #include "metatextlabel.h"
 #include "phononwidget.h"
 #include "pixmapviewer.h"
 
+/**
+ * Helper function for sorting items with qSort() in
+ * InformationPanel::contextMenu().
+ */
+bool lessThan(const QAction* action1, const QAction* action2)
+{
+    return action1->text() < action2->text();
+}
+
+
 InformationPanel::InformationPanel(QWidget* parent) :
     Panel(parent),
     m_initialized(false),
@@ -67,14 +86,18 @@ InformationPanel::InformationPanel(QWidget* parent) :
     m_selection(),
     m_nameLabel(0),
     m_preview(0),
+    m_previewSeparator(0),
     m_phononWidget(0),
     m_metaDataWidget(0),
+    m_metaDataSeparator(0),
+    m_metaTextArea(0),
     m_metaTextLabel(0)
 {
 }
 
 InformationPanel::~InformationPanel()
 {
+    InformationPanelSettings::self()->writeConfig();
 }
 
 QSize InformationPanel::sizeHint() const
@@ -179,15 +202,164 @@ void InformationPanel::resizeEvent(QResizeEvent* event)
         const int maxWidth = event->size().width() - KDialog::spacingHint() * 4;
         m_nameLabel->setMaximumWidth(maxWidth);
 
+        // The metadata widget also contains a text widget which may return
+        // a large preferred width.
+        if (m_metaDataWidget != 0) {
+            m_metaDataWidget->setMaximumWidth(maxWidth);
+        }
+
         // try to increase the preview as large as possible
         m_preview->setSizeHint(QSize(maxWidth, maxWidth));
         m_urlCandidate = m_shownUrl; // reset the URL candidate if a resizing is done
         m_infoTimer->start();
-    }
 
+        if (m_phononWidget->isVisible() && (m_phononWidget->mode() == PhononWidget::Video)) {
+            // assure that the size of the video player is the same as the preview size
+            m_phononWidget->setVideoSize(QSize(maxWidth, maxWidth));
+        }
+    }
     Panel::resizeEvent(event);
 }
 
+bool InformationPanel::eventFilter(QObject* obj, QEvent* event)
+{
+    // Check whether the size of the meta text area has changed and adjust
+    // the fixed width in a way that no horizontal scrollbar needs to be shown.
+    if ((obj == m_metaTextArea->viewport()) && (event->type() == QEvent::Resize)) {
+        QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
+        m_metaTextLabel->setFixedWidth(resizeEvent->size().width());
+    }
+    return Panel::eventFilter(obj, event);
+}
+
+void InformationPanel::contextMenuEvent(QContextMenuEvent* event)
+{
+    Panel::contextMenuEvent(event);
+
+#ifdef HAVE_NEPOMUK
+    if (showMultipleSelectionInfo()) {
+        return;
+    }
+
+    KMenu popup(this);
+
+    QAction* previewAction = popup.addAction(i18nc("@action:inmenu", "Preview"));
+    previewAction->setIcon(KIcon("view-preview"));
+    previewAction->setCheckable(true);
+    previewAction->setChecked(InformationPanelSettings::showPreview());
+
+    const bool metaDataAvailable = MetaDataWidget::metaDataAvailable();
+
+    QAction* ratingAction = popup.addAction(i18nc("@action:inmenu", "Rating"));
+    ratingAction->setIcon(KIcon("rating"));
+    ratingAction->setCheckable(true);
+    ratingAction->setChecked(InformationPanelSettings::showRating());
+    ratingAction->setEnabled(metaDataAvailable);
+
+    QAction* commentAction = popup.addAction(i18nc("@action:inmenu", "Comment"));
+    commentAction->setIcon(KIcon("text-plain"));
+    commentAction->setCheckable(true);
+    commentAction->setChecked(InformationPanelSettings::showComment());
+    commentAction->setEnabled(metaDataAvailable);
+
+    QAction* tagsAction = popup.addAction(i18nc("@action:inmenu", "Tags"));
+    tagsAction->setCheckable(true);
+    tagsAction->setChecked(InformationPanelSettings::showTags());
+    tagsAction->setEnabled(metaDataAvailable);
+
+    KConfig config("kmetainformationrc", KConfig::NoGlobals);
+    KConfigGroup settings = config.group("Show");
+    initMetaInfoSettings(settings);
+
+    QList<QAction*> actions;
+
+    // Get all meta information labels that are available for
+    // the currently shown file item and add them to the popup.
+    Nepomuk::Resource res(fileItem().url());
+    QHash<QUrl, Nepomuk::Variant> properties = res.properties();
+    QHash<QUrl, Nepomuk::Variant>::const_iterator it = properties.constBegin();
+    while (it != properties.constEnd()) {
+        Nepomuk::Types::Property prop(it.key());
+        const QString key = prop.label();
+
+        // Meta information provided by Nepomuk that is already
+        // available from KFileItem should not be configurable.
+        bool skip = (key == "fileExtension") ||
+                    (key == "name") ||
+                    (key == "sourceModified") ||
+                    (key == "size") ||
+                    (key == "mime type");
+        if (!skip) {
+            // Check whether there is already a meta information
+            // having the same label. In this case don't show it
+            // twice in the menu.
+            foreach (const QAction* action, actions) {
+                if (action->data().toString() == key) {
+                    skip = true;
+                    break;
+                }
+            }
+        }
+
+        if (!skip) {
+            const QString label = tunedLabel(key); // TODO
+            QAction* action = new QAction(label, &popup);
+            action->setCheckable(true);
+            action->setChecked(settings.readEntry(key, true));
+            action->setData(key);
+            actions.append(action);
+        }
+
+        ++it;
+    }
+
+    if (actions.count() > 0) {
+        popup.addSeparator();
+
+        // add all items alphabetically sorted to the popup
+        qSort(actions.begin(), actions.end(), lessThan);
+        foreach (QAction* action, actions) {
+            popup.addAction(action);
+        }
+    }
+
+    // Open the popup and adjust the settings for the
+    // selected action.
+    QAction* action = popup.exec(QCursor::pos());
+    if (action == 0) {
+        return;
+    }
+
+    const bool isChecked = action->isChecked();
+    if (action == previewAction) {
+        m_preview->setVisible(isChecked);
+        m_previewSeparator->setVisible(isChecked);
+        InformationPanelSettings::setShowPreview(isChecked);
+        updatePhononWidget();
+    } else if (action == ratingAction) {
+        m_metaDataWidget->setRatingVisible(isChecked);
+        InformationPanelSettings::setShowRating(isChecked);
+    } else if (action == commentAction) {
+        m_metaDataWidget->setCommentVisible(isChecked);
+        InformationPanelSettings::setShowComment(isChecked);
+    } else if (action == tagsAction) {
+        m_metaDataWidget->setTagsVisible(isChecked);
+        InformationPanelSettings::setShowTags(isChecked);
+    } else {
+        settings.writeEntry(action->data().toString(), action->isChecked());
+        settings.sync();
+        showMetaInfo();
+    }
+
+    if (m_metaDataWidget != 0) {
+        const bool visible = m_metaDataWidget->isRatingVisible() ||
+                             m_metaDataWidget->isCommentVisible() ||
+                             m_metaDataWidget->areTagsVisible();
+        m_metaDataSeparator->setVisible(visible);
+    }
+#endif
+}
+
 void InformationPanel::showItemInfo()
 {
     if (!isVisible()) {
@@ -275,10 +447,9 @@ void InformationPanel::showPreview(const KFileItem& item,
 void InformationPanel::slotFileRenamed(const QString& source, const QString& dest)
 {
     if (m_shownUrl == KUrl(source)) {
-        // the currently shown file has been renamed, hence update the item information
-        // for the renamed file
-        KFileItem item(KFileItem::Unknown, KFileItem::Unknown, KUrl(dest));
-        requestDelayedItemInfo(item);
+        m_shownUrl = KUrl(dest);
+        m_fileItem = KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_shownUrl);
+        showItemInfo();
     }
 }
 
@@ -308,8 +479,7 @@ void InformationPanel::slotFilesRemoved(const QStringList& files)
         if (m_shownUrl == KUrl(fileName)) {
             // the currently shown item has been removed, show
             // the parent directory as fallback
-            m_shownUrl = url();
-            showItemInfo();
+            reset();
             break;
         }
     }
@@ -330,11 +500,20 @@ void InformationPanel::slotLeftDirectory(const QString& directory)
         // has been unmounted. In this case no directory change will be
         // done in Dolphin, but the Information Panel must be updated to
         // indicate an invalid directory.
-        m_shownUrl = url();
-        showItemInfo();
+        reset();
     }
 }
 
+void InformationPanel::slotPlayingStarted()
+{
+    m_preview->setVisible(m_phononWidget->mode() != PhononWidget::Video);
+}
+
+void InformationPanel::slotPlayingStopped()
+{
+    m_preview->setVisible(true);
+}
+
 bool InformationPanel::applyPlace(const KUrl& url)
 {
     KFilePlacesModel* placesModel = DolphinSettings::instance().placesModel();
@@ -391,88 +570,36 @@ void InformationPanel::showMetaInfo()
             m_metaTextLabel->add(i18nc("@label", "Size:"), KIO::convertSize(item.size()));
             m_metaTextLabel->add(i18nc("@label", "Modified:"), item.timeString());
 
-            if (item.isLocalFile()) {
-                // TODO: See convertMetaInfo below, find a way to display only interesting information
-                // in a readable way
-                const KFileMetaInfo::WhatFlags flags = KFileMetaInfo::Fastest |
-                                                       KFileMetaInfo::TechnicalInfo |
-                                                       KFileMetaInfo::ContentInfo;
-                const QString path = item.url().path();
-                const KFileMetaInfo fileMetaInfo(path, QString(), flags);
-                if (fileMetaInfo.isValid()) {
-                    const QHash<QString, KFileMetaInfoItem>& items = fileMetaInfo.items();
-                    QHash<QString, KFileMetaInfoItem>::const_iterator it = items.constBegin();
-                    const QHash<QString, KFileMetaInfoItem>::const_iterator end = items.constEnd();
-                    QString labelText;
-                    while (it != end) {
-                        const KFileMetaInfoItem& metaInfoItem = it.value();
-                        const QVariant& value = metaInfoItem.value();
-                        if (value.isValid() && convertMetaInfo(metaInfoItem.name(), labelText)) {
-                            m_metaTextLabel->add(labelText, value.toString());
-                        }
-                        ++it;
-                    }
+#ifdef HAVE_NEPOMUK
+            KConfig config("kmetainformationrc", KConfig::NoGlobals);
+            KConfigGroup settings = config.group("Show");
+            initMetaInfoSettings(settings);
+
+            Nepomuk::Resource res(item.url());
+
+            QHash<QUrl, Nepomuk::Variant> properties = res.properties();
+            QHash<QUrl, Nepomuk::Variant>::const_iterator it = properties.constBegin();
+            while (it != properties.constEnd()) {
+                Nepomuk::Types::Property prop(it.key());
+                const QString label = prop.label();
+                if (settings.readEntry(label, true)) {
+                    // TODO #1: use Nepomuk::formatValue(res, prop) if available
+                    // instead of it.value().toString()
+                    // TODO #2: using tunedLabel() is a workaround for KDE 4.3 until
+                    // we get translated labels
+                    m_metaTextLabel->add(tunedLabel(label) + ':', it.value().toString());
                 }
+                ++it;
             }
+#endif
         }
 
         if (m_metaDataWidget != 0) {
             m_metaDataWidget->setFile(item.targetUrl());
         }
-
-        if (Phonon::BackendCapabilities::isMimeTypeAvailable(item.mimetype())) {
-            if (m_phononWidget == 0) {
-                m_phononWidget = new PhononWidget(this);
-
-                QVBoxLayout* vBoxLayout = qobject_cast<QVBoxLayout*>(layout());
-                Q_ASSERT(vBoxLayout != 0);
-                vBoxLayout->insertWidget(3, m_phononWidget);
-            }
-            m_phononWidget->setUrl(item.url());
-        } else {
-            delete m_phononWidget;
-            m_phononWidget = 0;
-        }
     }
-}
 
-bool InformationPanel::convertMetaInfo(const QString& key, QString& text) const
-{
-    struct MetaKey {
-        const char* key;
-        QString text;
-    };
-
-    // sorted list of keys, where its data should be shown
-    static const MetaKey keys[] = {
-        { "http://freedesktop.org/standards/xesam/1.0/core#album",       i18nc("@label", "Album:") },
-        { "http://freedesktop.org/standards/xesam/1.0/core#artist",      i18nc("@label", "Artist:") },
-        { "http://freedesktop.org/standards/xesam/1.0/core#genre",       i18nc("@label", "Genre:") },
-        { "http://freedesktop.org/standards/xesam/1.0/core#height",      i18nc("@label", "Height:") },
-        { "http://freedesktop.org/standards/xesam/1.0/core#lineCount",   i18nc("@label", "Lines:") },
-        { "http://freedesktop.org/standards/xesam/1.0/core#title",       i18nc("@label", "Title:") },
-        { "http://freedesktop.org/standards/xesam/1.0/core#type",        i18nc("@label", "Type:") },
-        { "http://freedesktop.org/standards/xesam/1.0/core#trackNumber", i18nc("@label", "Track:") },
-        { "http://freedesktop.org/standards/xesam/1.0/core#width",       i18nc("@label", "Width:") }
-    };
-
-    // do a binary search for the key...
-    int top = 0;
-    int bottom = sizeof(keys) / sizeof(MetaKey) - 1;
-    while (top <= bottom) {
-        const int middle = (top + bottom) / 2;
-        const int result = key.compare(keys[middle].key);
-        if (result < 0) {
-            bottom = middle - 1;
-        } else if (result > 0) {
-            top = middle + 1;
-        } else {
-            text = keys[middle].text;
-            return true;
-        }
-    }
-
-    return false;
+    updatePhononWidget();
 }
 
 KFileItem InformationPanel::fileItem() const
@@ -532,10 +659,90 @@ void InformationPanel::setNameLabelText(const QString& text)
     m_nameLabel->setText(wrappedText);
 }
 
-void InformationPanel::init()
+void InformationPanel::reset()
+{
+    m_selection.clear();
+    m_shownUrl = url();
+    m_fileItem = KFileItem();
+    showItemInfo();
+}
+
+void InformationPanel::initMetaInfoSettings(KConfigGroup& group)
+{
+    if (!group.readEntry("initialized", false)) {
+        // The resource file is read the first time. Assure
+        // that some meta information is disabled per default.
+
+        static const char* disabledProperties[] = {
+            "asText", "contentSize", "depth", "fileExtension",
+            "fileName", "fileSize", "isPartOf", "mimetype", "name",
+            "parentUrl", "plainTextContent", "sourceModified",
+            "size", "url",
+            0 // mandatory last entry
+        };
+
+        int i = 0;
+        while (disabledProperties[i] != 0) {
+            group.writeEntry(disabledProperties[i], false);
+            ++i;
+        }
+
+        // mark the group as initialized
+        group.writeEntry("initialized", true);
+    }
+}
+
+void InformationPanel::updatePhononWidget()
 {
-    const int spacing = KDialog::spacingHint();
+    const bool multipleSelections = showMultipleSelectionInfo();
+    const bool showPreview = InformationPanelSettings::showPreview();
+
+    if (multipleSelections || !showPreview) {
+        m_phononWidget->hide();
+    } else if (!multipleSelections && showPreview) {
+        const KFileItem item = fileItem();
+        const QString mimeType = item.mimetype();
+        const bool usePhonon = Phonon::BackendCapabilities::isMimeTypeAvailable(mimeType) &&
+                               (mimeType != "image/png");  // TODO: workaround, as Phonon
+                                                           // thinks it supports PNG images
+        if (usePhonon) {
+            m_phononWidget->show();
+            PhononWidget::Mode mode = mimeType.startsWith(QLatin1String("video"))
+                                      ? PhononWidget::Video
+                                      : PhononWidget::Audio;
+            m_phononWidget->setMode(mode);
+            m_phononWidget->setUrl(item.url());
+            if ((mode == PhononWidget::Video) && m_preview->isVisible()) {
+                m_phononWidget->setVideoSize(m_preview->size());
+            }
+        } else {
+            m_phononWidget->hide();
+            m_preview->setVisible(true);
+        }
+    }
+}
+
+QString InformationPanel::tunedLabel(const QString& label) const
+{
+    QString tunedLabel;
+    const int labelLength = label.length();
+    if (labelLength > 0) {
+        tunedLabel.reserve(labelLength);
+        tunedLabel = label[0].toUpper();
+        for (int i = 1; i < labelLength; ++i) {
+            if (label[i].isUpper() && !label[i - 1].isSpace() && !label[i - 1].isUpper()) {
+                tunedLabel += ' ';
+                tunedLabel += label[i].toLower();
+            } else {
+                tunedLabel += label[i];
+            }
+        }
+    }
+    return tunedLabel;
+}
 
+void InformationPanel::init()
+{
     m_infoTimer = new QTimer(this);
     m_infoTimer->setInterval(300);
     m_infoTimer->setSingleShot(true);
@@ -552,7 +759,7 @@ void InformationPanel::init()
             this, SLOT(markOutdatedPreview()));
 
     QVBoxLayout* layout = new QVBoxLayout;
-    layout->setSpacing(spacing);
+    layout->setSpacing(KDialog::spacingHint());
 
     // name
     m_nameLabel = new QLabel(this);
@@ -561,35 +768,72 @@ void InformationPanel::init()
     m_nameLabel->setFont(font);
     m_nameLabel->setAlignment(Qt::AlignHCenter);
     m_nameLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+    m_nameLabel->setMaximumWidth(KIconLoader::SizeEnormous);
 
     // preview
+    const int minPreviewWidth = KIconLoader::SizeEnormous + KIconLoader::SizeMedium;
+
     m_preview = new PixmapViewer(this);
-    m_preview->setMinimumWidth(KIconLoader::SizeEnormous + KIconLoader::SizeMedium);
+    m_preview->setMinimumWidth(minPreviewWidth);
     m_preview->setMinimumHeight(KIconLoader::SizeEnormous);
 
+    m_phononWidget = new PhononWidget(this);
+    m_phononWidget->setMinimumWidth(minPreviewWidth);
+    connect(m_phononWidget, SIGNAL(playingStarted()),
+            this, SLOT(slotPlayingStarted()));
+    connect(m_phononWidget, SIGNAL(playingStopped()),
+            this, SLOT(slotPlayingStopped()));
+
+    m_previewSeparator = new KSeparator(this);
+
+    const bool showPreview = InformationPanelSettings::showPreview();
+    m_preview->setVisible(showPreview);
+    m_previewSeparator->setVisible(showPreview);
+
     if (MetaDataWidget::metaDataAvailable()) {
         // rating, comment and tags
         m_metaDataWidget = new MetaDataWidget(this);
         m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+        m_metaDataWidget->setMaximumWidth(KIconLoader::SizeEnormous);
+
+        const bool showRating  = InformationPanelSettings::showRating();
+        const bool showComment = InformationPanelSettings::showComment();
+        const bool showTags    = InformationPanelSettings::showTags();
+
+        m_metaDataWidget->setRatingVisible(showRating);
+        m_metaDataWidget->setCommentVisible(showComment);
+        m_metaDataWidget->setTagsVisible(showTags);
+
+        m_metaDataSeparator = new KSeparator(this);
+        m_metaDataSeparator->setVisible(showRating || showComment || showTags);
     }
 
     // general meta text information
     m_metaTextLabel = new MetaTextLabel(this);
-    m_metaTextLabel->setMinimumWidth(spacing);
     m_metaTextLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
 
+    m_metaTextArea = new QScrollArea(this);
+    m_metaTextArea->setWidget(m_metaTextLabel);
+    m_metaTextArea->setWidgetResizable(true);
+    m_metaTextArea->setFrameShape(QFrame::NoFrame);
+
+    QWidget* viewport = m_metaTextArea->viewport();
+    viewport->installEventFilter(this);
+
+    QPalette palette = viewport->palette();
+    palette.setColor(viewport->backgroundRole(), QColor(Qt::transparent));
+    viewport->setPalette(palette);
+
     layout->addWidget(m_nameLabel);
     layout->addWidget(new KSeparator(this));
     layout->addWidget(m_preview);
-    layout->addWidget(new KSeparator(this));
+    layout->addWidget(m_phononWidget);
+    layout->addWidget(m_previewSeparator);
     if (m_metaDataWidget != 0) {
         layout->addWidget(m_metaDataWidget);
-        layout->addWidget(new KSeparator(this));
+        layout->addWidget(m_metaDataSeparator);
     }
-    layout->addWidget(m_metaTextLabel);
-
-    // ensure that widgets in the information side bar are aligned towards the top
-    layout->addStretch(1);
+    layout->addWidget(m_metaTextArea);
     setLayout(layout);
 
     org::kde::KDirNotify* dirNotify = new org::kde::KDirNotify(QString(), QString(),