diff --git a/src/tagcheckbox.h b/src/tagcheckbox.h --- a/src/tagcheckbox.h +++ b/src/tagcheckbox.h @@ -1,7 +1,8 @@ /* This file is part of the Nepomuk KDE project. Copyright (C) 2010 Sebastian Trueg - + Copyright (C) 2018 Michael Heidelbach + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -28,21 +29,23 @@ class QMouseEvent; class QLabel; +class QUrl; namespace Baloo { class TagCheckBox : public QWidget { Q_OBJECT - + public: - TagCheckBox(const QString& tag, QWidget* parent = 0); + TagCheckBox(const QString& content, + const QString& baseUrl = QString(), + const TagWidget::DisplayMode mode = TagWidget::DisplayMode::Unrestricted, + QWidget* parent = nullptr); ~TagCheckBox(); - QString tag() const { return m_tag; } - Q_SIGNALS: - void tagClicked(const QString& tag); + void tagClicked(const QUrl& url); protected: void leaveEvent(QEvent* event ) Q_DECL_OVERRIDE; @@ -52,11 +55,11 @@ QRect tagRect() const; void enableUrlHover( bool enabled ); - // two modes: checkbox and simple label +private: + QString m_content; + QString m_baseUrl; + TagWidget::DisplayMode m_mode; QLabel* m_label; - QWidget* m_child; - - QString m_tag; bool m_urlHover; }; } diff --git a/src/tagcheckbox.cpp b/src/tagcheckbox.cpp --- a/src/tagcheckbox.cpp +++ b/src/tagcheckbox.cpp @@ -1,5 +1,6 @@ /* - This file is part of the Nepomuk KDE project. + This file is part of the Baloo project. + Copyright (C) 2018 Michael Heidelbach Copyright (C) 2013 Vishesh Handa Copyright (C) 2010 Sebastian Trueg @@ -27,28 +28,38 @@ #include #include #include +#include using namespace Baloo; -TagCheckBox::TagCheckBox(const QString& tag, QWidget* parent) - : QWidget( parent ), - m_label(0), - m_tag(tag), - m_urlHover(false) +TagCheckBox::TagCheckBox(const QString& content, + const QString& baseUrl, + const TagWidget::DisplayMode mode, + QWidget* parent) + : QWidget(parent) + , m_content(content) + , m_baseUrl(baseUrl) + , m_mode(mode) + , m_label(0) + , m_urlHover(false) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); - - m_label = new QLabel(tag.split("/", QString::SkipEmptyParts).last(), this); - m_label->setToolTip(tag); - m_label->setMouseTracking(true); + const QString displayString = (m_mode & TagWidget::DisplayMode::Tags) == TagWidget::DisplayMode::Tags + ? m_content.split("/", QString::SkipEmptyParts).last() + : m_content; + m_label = new QLabel(displayString, this); + if (displayString != m_content) { + m_label->setToolTip(m_content); + } m_label->setTextFormat(Qt::PlainText); m_label->setForegroundRole(parent->foregroundRole()); - m_child = m_label; - m_child->installEventFilter( this ); - m_child->setMouseTracking(true); - layout->addWidget( m_child ); + if ((m_mode & TagWidget::DisplayMode::NoLinks) != TagWidget::DisplayMode::NoLinks) { + m_label->setMouseTracking(true); + m_label->installEventFilter(this); + } + layout->addWidget(m_label); } TagCheckBox::~TagCheckBox() @@ -64,7 +75,7 @@ bool TagCheckBox::eventFilter( QObject* watched, QEvent* event ) { - if( watched == m_child ) { + if (watched == m_label) { switch( event->type() ) { case QEvent::MouseMove: { QMouseEvent* me = static_cast(event); @@ -75,7 +86,18 @@ case QEvent::MouseButtonRelease: { QMouseEvent* me = static_cast(event); if (me->button() == Qt::LeftButton && tagRect().contains(me->pos())) { - emit tagClicked( m_tag ); + // TODO: Improve url composition + auto url = QUrl::fromUserInput(m_baseUrl + m_content); + auto scheme = url.scheme(); + if (url.scheme() == QLatin1String("tags")) { + // Assert correct case + url.setHost(QString()); + url.setPath(m_content); + } else { + QString query = url.query().replace(" ", "+"); + url.setQuery(query); + } + emit tagClicked(url); return true; } break; @@ -99,13 +121,16 @@ void TagCheckBox::enableUrlHover( bool enable ) { - if( m_urlHover != enable ) { - m_urlHover = enable; - QFont f = font(); - if(enable) - f.setUnderline(true); - m_child->setFont(f); - m_child->setCursor( enable ? Qt::PointingHandCursor : Qt::ArrowCursor ); + if (m_mode & TagWidget::DisplayMode::NoLinks || m_urlHover == enable ) { + return; + } + + m_urlHover = enable; + QFont f = font(); + if(enable) { + f.setUnderline(true); } + m_label->setFont(f); + m_label->setCursor(enable ? Qt::PointingHandCursor : Qt::ArrowCursor); } diff --git a/src/tagwidget.h b/src/tagwidget.h --- a/src/tagwidget.h +++ b/src/tagwidget.h @@ -41,6 +41,17 @@ class BALOO_WIDGETS_EXPORT TagWidget : public QWidget { Q_OBJECT + public: + /** + * Modify how items are displayed + */ + enum DisplayMode : uint { + Unrestricted = 0, /// Display items as links and add a 'Change' link. + ReadOnly = 1, /// Display items as links. + NoLinks = 2, /// Add a 'Change' link. + ReadOnlyNoLinks = ReadOnly | NoLinks, /// Display as plain text. + Tags = 0x100 /// Use 'tags://' as base URL. + }; public: /** @@ -80,7 +91,7 @@ /** * This signal is emitted whenever a tag is clicked. */ - void tagClicked(const QString&); + void tagClicked(const QUrl&); /** * Emitted whenever the selection of tags changes. @@ -98,6 +109,15 @@ * \since 4.5 */ void setSelectedTags(const QStringList& tags); + /** + * Set the list of tags. + * + * \p propertyName - the property the tags belong to. + * \p tags - list of tags or token-like strings. + * \since 4.5 + */ + + void setSelectedTags(const QString& propertyName, const QStringList& tags); /** * Set the alignment to use. Only horizontal alignment flags make a @@ -111,6 +131,13 @@ * Set the TagWidget as read only */ void setReadyOnly(bool readOnly = true); + /** + * Set the name of the property. + * Default is 'tags' + */ + void setPropertyName(const QString& name); + bool displayAsLinks() const; + void setDisplayAsLinks(const bool asLinks); private Q_SLOTS: void slotShowAll(); diff --git a/src/tagwidget.cpp b/src/tagwidget.cpp --- a/src/tagwidget.cpp +++ b/src/tagwidget.cpp @@ -1,7 +1,8 @@ /* - * This file is part of the Nepomuk KDE project. + * This file is part of the Baloo KDE project. * Copyright (C) 2006-2010 Sebastian Trueg * Copyright (C) 2011-2013 Vishesh Handa + * Copyright (C) 2018 Michael Heidelbach * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -40,7 +41,7 @@ m_readOnly = false; m_showAllLinkLabel = 0; m_editTagsDialog = 0; - + m_propertyName = QStringLiteral("tags"); QGridLayout* mainLayout = new QGridLayout( q ); mainLayout->setMargin(0); //TODO spacingHint should be declared. Old code m_flowLayout = new KBlockLayout( 0, KDialog::spacingHint()*3 ); @@ -94,8 +95,15 @@ QMap::iterator it = m_checkBoxHash.find(tag); if( it == m_checkBoxHash.end() ) { //kDebug() << "Creating checkbox for" << tag.genericLabel(); - TagCheckBox* checkBox = new TagCheckBox(tag, q); - q->connect( checkBox, SIGNAL(tagClicked(QString)), SIGNAL(tagClicked(QString)) ); + QString url = m_propertyName == QLatin1String("tags") + ? QStringLiteral("tags://") + : QStringLiteral("baloosearch:/?query=%1:").arg(m_propertyName); + TagWidget::DisplayMode mode = m_asLinks + ? TagWidget::DisplayMode::Unrestricted + : TagWidget::DisplayMode::NoLinks; + + auto checkBox = new TagCheckBox(tag, url, mode, q); + q->connect( checkBox, SIGNAL(tagClicked(QUrl)), SIGNAL(tagClicked(QUrl)) ); m_checkBoxHash.insert( tag, checkBox ); m_flowLayout->addWidget( checkBox ); return checkBox; @@ -152,17 +160,45 @@ d->selectTags(tags); } +void TagWidget::setSelectedTags(const QString& name, const QStringList& tags) +{ + d->m_propertyName = name; + d->selectTags(tags); +} + void TagWidget::setAlignment( Qt::Alignment alignment ) { d->m_flowLayout->setAlignment( alignment ); } void TagWidget::setReadyOnly(bool readOnly) { + if (d->m_readOnly == readOnly) { + return; + } d->m_readOnly = readOnly; d->rebuild(); } +void TagWidget::setPropertyName(const QString& name) +{ + d->m_propertyName = name; +} + +bool TagWidget::displayAsLinks() const +{ + return d->m_asLinks; +} + +void TagWidget::setDisplayAsLinks(const bool asLinks) +{ + if (asLinks == d->m_asLinks) { + return; + } + d->m_asLinks = asLinks; + d->rebuild(); +} + void TagWidget::slotTagUpdateDone() { diff --git a/src/tagwidget_p.h b/src/tagwidget_p.h --- a/src/tagwidget_p.h +++ b/src/tagwidget_p.h @@ -22,6 +22,7 @@ #define _BALOO_TAG_WIDGET_P_H_ #include "tagwidget.h" +#include "tagcheckbox.h" #include #include @@ -47,6 +48,7 @@ /// check the corresponding checkboxes and even /// add missing checkboxes void selectTags(const QStringList& tags); + void setPropertyName(const QString& name); bool m_readOnly; @@ -56,6 +58,8 @@ TagWidget* q; KEditTagsDialog* m_editTagsDialog; + QString m_propertyName; + bool m_asLinks; }; } diff --git a/src/widgetfactory.h b/src/widgetfactory.h --- a/src/widgetfactory.h +++ b/src/widgetfactory.h @@ -58,12 +58,12 @@ void slotCommentChanged(const QString& comment); void slotRatingChanged(uint rating); - void slotTagClicked(const QString& tag); + void slotTagClicked(const QUrl& url); void slotLinkActivated(const QString& url); private: QWidget* createRatingWidget(int rating, QWidget* parent); - QWidget* createTagWidget(const QStringList& tags, QWidget* parent); + QWidget* createTagWidget(const QString& name, const QStringList& tags, bool readOnly, QWidget* parent); QWidget* createCommentWidget(const QString& comment, QWidget* parent); QWidget* createValueWidget(const QString& value, QWidget* parent); diff --git a/src/widgetfactory.cpp b/src/widgetfactory.cpp --- a/src/widgetfactory.cpp +++ b/src/widgetfactory.cpp @@ -1,6 +1,7 @@ /* Copyright (C) 2012-2014 Vishesh Handa - + Copyright (C) 2018 Michael Heidelbach + Code largely copied/adapted from KFileMetadataProvider Copyright (C) 2010 by Peter Penz @@ -32,6 +33,7 @@ #include #include #include +#include #include #include @@ -100,6 +102,18 @@ QWidget* WidgetFactory::createWidget(const QString& prop, const QVariant& value, QWidget* parent) { QWidget* widget = 0; + auto persons = QStringList{ + QStringLiteral("author"), + QStringLiteral("composer"), + QStringLiteral("lyricist"), + QStringLiteral("artist"), + QStringLiteral("albumArtist") + }; + auto tokens = QStringList{ + QStringLiteral("genre"), + QStringLiteral("keywords"), + QStringLiteral("subject") + }; if (prop == QLatin1String("rating")) { widget = createRatingWidget( value.toInt(), parent ); @@ -112,7 +126,28 @@ QCollator coll; coll.setNumericMode(true); std::sort(tags.begin(), tags.end(), [&](const QString& s1, const QString& s2){ return coll.compare(s1, s2) < 0; }); - widget = createTagWidget( tags, parent ); + widget = createTagWidget(prop, tags, m_readOnly, parent ); + + } else if (tokens.contains(prop)) { + auto values = value.type() == QVariant::Type::StringList + ? value.toStringList() + : value.toString().split(QRegularExpression{",\\s*|;\\s*"}, QString::SplitBehavior::SkipEmptyParts); + QCollator coll; + coll.setNumericMode(true); + std::sort(values.begin(), values.end(), [&](const QString& s1, const QString& s2){ + return coll.compare(s1, s2) < 0; + }); + widget = createTagWidget(prop, values, true, parent); + + } else if (persons.contains(prop)) { + auto values = value.type() == QVariant::Type::StringList + ? value.toStringList() + : value.toString().split(QRegularExpression{"\\s*&\\s*"}); + widget = createTagWidget(prop, values, true, parent); + } else if (prop == QLatin1String("releaseYear")) { + // FIXME: This is misuse + widget = createTagWidget(prop, {value.toString()}, true, parent); + } else { KFormat form; @@ -158,10 +193,12 @@ return widget; } -QWidget* WidgetFactory::createTagWidget(const QStringList& tags, QWidget* parent) +QWidget* WidgetFactory::createTagWidget(const QString& name, const QStringList& tags, bool readOnly, QWidget* parent) { TagWidget* tagWidget = new TagWidget(parent); - tagWidget->setReadyOnly(m_readOnly); + tagWidget->setReadyOnly(readOnly); + tagWidget->setPropertyName(name); + tagWidget->setDisplayAsLinks(!m_noLinks || name == QLatin1String("tags")); tagWidget->setSelectedTags(tags); connect(tagWidget, &TagWidget::selectionChanged, this, &WidgetFactory::slotTagsChanged); @@ -301,12 +338,8 @@ emit urlActivated(QUrl::fromUserInput(url)); } -void WidgetFactory::slotTagClicked(const QString& tag) +void WidgetFactory::slotTagClicked(const QUrl& url) { - QUrl url; - url.setScheme("tags"); - url.setPath(tag); - emit urlActivated(url); }