Warning, file /libraries/baloo-widgets/src/widgetfactory.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2012-2014 Vishesh Handa <vhanda@kde.org>
0003 
0004     Code largely copied/adapted from KFileMetadataProvider
0005     SPDX-FileCopyrightText: 2010 Peter Penz <peter.penz@gmx.at>
0006 
0007     SPDX-License-Identifier: LGPL-2.1-or-later
0008 */
0009 
0010 #include "widgetfactory.h"
0011 #include "KRatingWidget"
0012 #include "filemetadatautil_p.h"
0013 #include "kcommentwidget_p.h"
0014 #include "tagwidget.h"
0015 
0016 #include <KFileMetaData/PropertyInfo>
0017 #include <KFileMetaData/UserMetaData>
0018 
0019 #include <QCollator>
0020 #include <QLabel>
0021 #include <QLocale>
0022 #include <QTime>
0023 #include <QUrl>
0024 
0025 #include <KApplicationTrader>
0026 #include <KFormat>
0027 #include <KLocalizedString>
0028 #include <KStringHandler>
0029 
0030 using namespace Baloo;
0031 
0032 WidgetFactory::WidgetFactory(QObject *parent)
0033     : QObject(parent)
0034     , m_dateFormat(QLocale::LongFormat)
0035 {
0036 }
0037 
0038 WidgetFactory::~WidgetFactory() = default;
0039 
0040 //
0041 // Widget Creation
0042 //
0043 static QString formatDateTime(const QVariant &value, QLocale::FormatType dateFormat)
0044 {
0045     const QString valueString = value.toString();
0046     QDateTime dt = QDateTime::fromString(valueString, Qt::ISODate);
0047 
0048     if (dt.isValid()) {
0049         KFormat form;
0050         QTime time = dt.time();
0051         // Check if Date/DateTime
0052         if (!time.hour() && !time.minute() && !time.second()) {
0053             return form.formatRelativeDate(dt.date(), dateFormat);
0054         } else {
0055             return form.formatRelativeDateTime(dt, dateFormat);
0056         }
0057     }
0058 
0059     return valueString;
0060 }
0061 
0062 static QString toString(const QVariant &value, QLocale::FormatType dateFormat)
0063 {
0064     switch (value.type()) {
0065     case QVariant::Int:
0066         return QLocale().toString(value.toInt());
0067     case QVariant::Double:
0068         return QLocale().toString(value.toDouble());
0069     case QVariant::StringList:
0070         return value.toStringList().join(i18nc("String list separator", ", "));
0071     case QVariant::Date:
0072     case QVariant::DateTime: {
0073         return formatDateTime(value, dateFormat);
0074     }
0075     case QVariant::List: {
0076         QStringList list;
0077         const auto valueList = value.toList();
0078         for (const QVariant &var : valueList) {
0079             list << toString(var, dateFormat);
0080         }
0081         return list.join(i18nc("String list separator", ", "));
0082     }
0083 
0084     default:
0085         return value.toString();
0086     }
0087 }
0088 
0089 QWidget *WidgetFactory::createWidget(const QString &prop, const QVariant &value, QWidget *parent)
0090 {
0091     QWidget *widget = nullptr;
0092     const int maxUrlLength = 80;
0093 
0094     if (prop == QLatin1String("rating")) {
0095         widget = createRatingWidget(value.toInt(), parent);
0096     } else if (prop == QLatin1String("userComment")) {
0097         widget = createCommentWidget(value.toString(), parent);
0098     } else if (prop == QLatin1String("tags")) {
0099         widget = createTagWidget(Baloo::Private::sortTags(value.toStringList()), parent);
0100     } else if (prop == QLatin1String("gpsLocation")) {
0101         const auto pair = value.value<QPair<float, float>>();
0102         const auto latitude = pair.first;
0103         const auto longitude = pair.second;
0104 
0105         const QString geoUri = QStringLiteral("geo:%1,%2").arg(latitude).arg(longitude);
0106 
0107         const QString latitudeStr = latitude < 0 ? i18nc("Latitude (South)", "%1°S", -latitude) : i18nc("Latitude (North)", "%1°N", latitude);
0108         const QString longitudeStr = longitude < 0 ? i18nc("Longitude (West)", "%1°W", -longitude) : i18nc("Longitude (East)", "%1°E", longitude);
0109         const QString gpsLocationStr = latitudeStr + QLatin1Char(' ') + longitudeStr;
0110 
0111         QLabel *valueWidget = createValueWidget(parent);
0112 
0113         if (const auto geoService = KApplicationTrader::preferredService(QStringLiteral("x-scheme-handler/geo"))) {
0114             valueWidget->setTextFormat(Qt::RichText);
0115             valueWidget->setTextInteractionFlags(Qt::TextBrowserInteraction);
0116             valueWidget->setText(QStringLiteral("<a href='%1'>%2</a>").arg(geoUri, gpsLocationStr));
0117             valueWidget->setToolTip(i18nc("@info:tooltip Show location in map viewer", "Show location in %1", geoService->name()));
0118         } else {
0119             valueWidget->setText(gpsLocationStr);
0120         }
0121 
0122         widget = valueWidget;
0123     } else {
0124         QString valueString;
0125         QLabel *valueWidget = createValueWidget(parent);
0126 
0127         auto pi = KFileMetaData::PropertyInfo::fromName(prop);
0128         if (pi.name() == QLatin1String("originUrl")) {
0129             auto url = value.toUrl();
0130             valueString = url.toString();
0131             // Shrink link label
0132             auto labelString = KStringHandler::csqueeze(valueString, maxUrlLength);
0133             valueString = QStringLiteral("<a href=\"%1\">%2</a>").arg(valueString, labelString);
0134             valueWidget->setTextFormat(Qt::RichText);
0135             valueWidget->setTextInteractionFlags(Qt::TextBrowserInteraction);
0136 
0137         } else if (pi.name() != QLatin1String("empty")) {
0138             if (pi.valueType() == QVariant::DateTime || pi.valueType() == QVariant::Date) {
0139                 valueString = formatDateTime(value, m_dateFormat);
0140             } else {
0141                 valueString = pi.formatAsDisplayString(value);
0142             }
0143         } else {
0144             valueString = toString(value, m_dateFormat);
0145         }
0146 
0147         valueWidget->setText(valueString);
0148         widget = valueWidget;
0149     }
0150 
0151     widget->setForegroundRole(parent->foregroundRole());
0152     widget->setFont(parent->font());
0153     widget->setObjectName(prop);
0154     return widget;
0155 }
0156 
0157 QWidget *WidgetFactory::createTagWidget(const QStringList &tags, QWidget *parent)
0158 {
0159     auto tagWidget = new TagWidget(parent);
0160     tagWidget->setReadyOnly(m_readOnly);
0161     tagWidget->setSelectedTags(tags);
0162 
0163     connect(tagWidget, &TagWidget::selectionChanged, this, &WidgetFactory::slotTagsChanged);
0164     connect(tagWidget, &TagWidget::tagClicked, this, &WidgetFactory::slotTagClicked);
0165 
0166     m_tagWidget = tagWidget;
0167     m_prevTags = tags;
0168 
0169     return tagWidget;
0170 }
0171 
0172 QWidget *WidgetFactory::createCommentWidget(const QString &comment, QWidget *parent)
0173 {
0174     auto commentWidget = new KCommentWidget(parent);
0175     commentWidget->setText(comment);
0176     commentWidget->setReadOnly(m_readOnly);
0177 
0178     connect(commentWidget, &KCommentWidget::commentChanged, this, &WidgetFactory::slotCommentChanged);
0179 
0180     m_commentWidget = commentWidget;
0181 
0182     return commentWidget;
0183 }
0184 
0185 QWidget *WidgetFactory::createRatingWidget(int rating, QWidget *parent)
0186 {
0187     auto ratingWidget = new KRatingWidget(parent);
0188     const Qt::Alignment align = (ratingWidget->layoutDirection() == Qt::LeftToRight) ? Qt::AlignLeft : Qt::AlignRight;
0189     ratingWidget->setAlignment(align);
0190     ratingWidget->setRating(rating);
0191     const QFontMetrics metrics(parent->font());
0192     ratingWidget->setPixmapSize(metrics.height());
0193 
0194     connect(ratingWidget, static_cast<void (KRatingWidget::*)(int)>(&KRatingWidget::ratingChanged), this, &WidgetFactory::slotRatingChanged);
0195 
0196     m_ratingWidget = ratingWidget;
0197 
0198     return ratingWidget;
0199 }
0200 
0201 // The default size hint of QLabel tries to return a square size.
0202 // This does not work well in combination with layouts that use
0203 // heightForWidth(): In this case it is possible that the content
0204 // of a label might get clipped. By specifying a size hint
0205 // with a maximum width that is necessary to contain the whole text,
0206 // using heightForWidth() assures having a non-clipped text.
0207 class ValueWidget : public QLabel
0208 {
0209 public:
0210     explicit ValueWidget(QWidget *parent = nullptr);
0211     QSize sizeHint() const override;
0212 };
0213 
0214 ValueWidget::ValueWidget(QWidget *parent)
0215     : QLabel(parent)
0216 {
0217 }
0218 
0219 QSize ValueWidget::sizeHint() const
0220 {
0221     QFontMetrics metrics(font());
0222     // TODO: QLabel internally provides already a method sizeForWidth(),
0223     // that would be sufficient. However this method is not accessible.
0224     return metrics.size(Qt::TextSingleLine, text());
0225 }
0226 
0227 QLabel *WidgetFactory::createValueWidget(QWidget *parent)
0228 {
0229     auto valueWidget = new ValueWidget(parent);
0230     valueWidget->setTextInteractionFlags(Qt::TextSelectableByMouse);
0231     valueWidget->setTextFormat(Qt::PlainText);
0232     valueWidget->setWordWrap(true);
0233     valueWidget->setAlignment(Qt::AlignTop | Qt::AlignLeft);
0234     connect(valueWidget, &ValueWidget::linkActivated, this, &WidgetFactory::slotLinkActivated);
0235 
0236     return valueWidget;
0237 }
0238 
0239 //
0240 // Data Synchronization
0241 //
0242 
0243 void WidgetFactory::slotCommentChanged(const QString &comment)
0244 {
0245     for (const KFileItem &item : std::as_const(m_items)) {
0246         QUrl url = item.targetUrl();
0247         if (!url.isLocalFile()) {
0248             continue;
0249         }
0250         KFileMetaData::UserMetaData md(url.toLocalFile());
0251         md.setUserComment(comment);
0252     }
0253     Q_EMIT dataChangeStarted();
0254     Q_EMIT dataChangeFinished();
0255 }
0256 
0257 void WidgetFactory::slotRatingChanged(int rating)
0258 {
0259     for (const KFileItem &item : std::as_const(m_items)) {
0260         QUrl url = item.targetUrl();
0261         if (!url.isLocalFile()) {
0262             continue;
0263         }
0264         KFileMetaData::UserMetaData md(url.toLocalFile());
0265         md.setRating(rating);
0266     }
0267     Q_EMIT dataChangeStarted();
0268     Q_EMIT dataChangeFinished();
0269 }
0270 
0271 void WidgetFactory::slotTagsChanged(const QStringList &tags)
0272 {
0273     if (m_tagWidget) {
0274         for (const KFileItem &item : std::as_const(m_items)) {
0275             QUrl url = item.targetUrl();
0276             if (!url.isLocalFile()) {
0277                 continue;
0278             }
0279             KFileMetaData::UserMetaData md(url.toLocalFile());
0280 
0281             // When multiple tags are selected one doesn't want to loose the old tags
0282             // of any of the resources. Unless specifically removed.
0283             QStringList newTags = md.tags() + tags;
0284             newTags.removeDuplicates();
0285 
0286             for (const QString &tag : std::as_const(m_prevTags)) {
0287                 if (!tags.contains(tag)) {
0288                     newTags.removeAll(tag);
0289                 }
0290             }
0291             md.setTags(newTags);
0292         }
0293 
0294         m_prevTags = tags;
0295         Q_EMIT dataChangeStarted();
0296         Q_EMIT dataChangeFinished();
0297     }
0298 }
0299 
0300 //
0301 // Notifications
0302 //
0303 
0304 void WidgetFactory::slotLinkActivated(const QString &url)
0305 {
0306     Q_EMIT urlActivated(QUrl::fromUserInput(url));
0307 }
0308 
0309 void WidgetFactory::slotTagClicked(const QString &tag)
0310 {
0311     QUrl url;
0312     url.setScheme(QStringLiteral("tags"));
0313     url.setPath(tag);
0314 
0315     Q_EMIT urlActivated(url);
0316 }
0317 
0318 //
0319 // Accessor Methods
0320 //
0321 void WidgetFactory::setReadOnly(bool value)
0322 {
0323     m_readOnly = value;
0324 }
0325 
0326 void WidgetFactory::setItems(const KFileItemList &items)
0327 {
0328     m_items = items;
0329 }
0330 
0331 Baloo::DateFormats WidgetFactory::dateFormat() const
0332 {
0333     return static_cast<Baloo::DateFormats>(m_dateFormat);
0334 }
0335 
0336 void Baloo::WidgetFactory::setDateFormat(const Baloo::DateFormats format)
0337 {
0338     m_dateFormat = static_cast<QLocale::FormatType>(format);
0339 }