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 }