File indexing completed on 2024-04-21 04:39:57
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 latitudeStr = latitude < 0 ? i18nc("Latitude (South)", "%1°S", -latitude) : i18nc("Latitude (North)", "%1°N", latitude); 0106 const QString longitudeStr = longitude < 0 ? i18nc("Longitude (West)", "%1°W", -longitude) : i18nc("Longitude (East)", "%1°E", longitude); 0107 const QString gpsLocationStr = latitudeStr + QLatin1Char(' ') + longitudeStr; 0108 0109 if (const auto geoService = KApplicationTrader::preferredService(QStringLiteral("x-scheme-handler/geo"))) { 0110 const QString geoUri = QStringLiteral("geo:%1,%2").arg(latitude).arg(longitude); 0111 QLabel *valueWidget = createLinkWidget(parent); 0112 0113 valueWidget->setText(QStringLiteral("<a href='%1'>%2</a>").arg(geoUri, gpsLocationStr)); 0114 valueWidget->setToolTip(i18nc("@info:tooltip Show location in map viewer", "Show location in %1", geoService->name())); 0115 widget = valueWidget; 0116 } else { 0117 QLabel *valueWidget = createValueWidget(parent); 0118 valueWidget->setText(gpsLocationStr); 0119 widget = valueWidget; 0120 } 0121 0122 } else if (prop == QLatin1String("originUrl")) { 0123 QLabel *valueWidget = createLinkWidget(parent); 0124 QString valueString = value.toUrl().toString(); 0125 // Shrink link label 0126 auto labelString = KStringHandler::csqueeze(valueString, maxUrlLength); 0127 valueString = QStringLiteral("<a href=\"%1\">%2</a>").arg(valueString, labelString); 0128 valueWidget->setText(valueString); 0129 widget = valueWidget; 0130 0131 } else { 0132 QString valueString; 0133 QLabel *valueWidget = createValueWidget(parent); 0134 0135 auto pi = KFileMetaData::PropertyInfo::fromName(prop); 0136 if (pi.property() != KFileMetaData::Property::Empty) { 0137 if (pi.valueType() == QVariant::DateTime || pi.valueType() == QVariant::Date) { 0138 valueString = formatDateTime(value, m_dateFormat); 0139 } else { 0140 valueString = pi.formatAsDisplayString(value); 0141 } 0142 } else { 0143 valueString = toString(value, m_dateFormat); 0144 } 0145 0146 valueWidget->setText(valueString); 0147 widget = valueWidget; 0148 } 0149 0150 widget->setForegroundRole(parent->foregroundRole()); 0151 widget->setFont(parent->font()); 0152 widget->setObjectName(prop); 0153 return widget; 0154 } 0155 0156 QWidget *WidgetFactory::createTagWidget(const QStringList &tags, QWidget *parent) 0157 { 0158 auto tagWidget = new TagWidget(parent); 0159 tagWidget->setReadyOnly(m_readOnly); 0160 tagWidget->setSelectedTags(tags); 0161 0162 connect(tagWidget, &TagWidget::selectionChanged, this, &WidgetFactory::slotTagsChanged); 0163 connect(tagWidget, &TagWidget::tagClicked, this, [this](const QString &tag) { 0164 QUrl url; 0165 url.setScheme(QStringLiteral("tags")); 0166 url.setPath(tag); 0167 Q_EMIT urlActivated(url); 0168 }); 0169 0170 m_tagWidget = tagWidget; 0171 m_prevTags = tags; 0172 0173 return tagWidget; 0174 } 0175 0176 QWidget *WidgetFactory::createCommentWidget(const QString &comment, QWidget *parent) 0177 { 0178 auto commentWidget = new KCommentWidget(parent); 0179 commentWidget->setText(comment); 0180 commentWidget->setReadOnly(m_readOnly); 0181 0182 connect(commentWidget, &KCommentWidget::commentChanged, this, &WidgetFactory::slotCommentChanged); 0183 0184 m_commentWidget = commentWidget; 0185 0186 return commentWidget; 0187 } 0188 0189 QWidget *WidgetFactory::createRatingWidget(int rating, QWidget *parent) 0190 { 0191 auto ratingWidget = new KRatingWidget(parent); 0192 const Qt::Alignment align = (ratingWidget->layoutDirection() == Qt::LeftToRight) ? Qt::AlignLeft : Qt::AlignRight; 0193 ratingWidget->setAlignment(align); 0194 ratingWidget->setRating(rating); 0195 const QFontMetrics metrics(parent->font()); 0196 ratingWidget->setPixmapSize(metrics.height()); 0197 0198 connect(ratingWidget, static_cast<void (KRatingWidget::*)(int)>(&KRatingWidget::ratingChanged), this, &WidgetFactory::slotRatingChanged); 0199 0200 m_ratingWidget = ratingWidget; 0201 0202 return ratingWidget; 0203 } 0204 0205 // The default size hint of QLabel tries to return a square size. 0206 // This does not work well in combination with layouts that use 0207 // heightForWidth(): In this case it is possible that the content 0208 // of a label might get clipped. By specifying a size hint 0209 // with a maximum width that is necessary to contain the whole text, 0210 // using heightForWidth() assures having a non-clipped text. 0211 class ValueWidget : public QLabel 0212 { 0213 public: 0214 explicit ValueWidget(QWidget *parent = nullptr); 0215 QSize sizeHint() const override; 0216 }; 0217 0218 ValueWidget::ValueWidget(QWidget *parent) 0219 : QLabel(parent) 0220 { 0221 } 0222 0223 QSize ValueWidget::sizeHint() const 0224 { 0225 QFontMetrics metrics(font()); 0226 // TODO: QLabel internally provides already a method sizeForWidth(), 0227 // that would be sufficient. However this method is not accessible. 0228 return metrics.size(Qt::TextSingleLine, text()); 0229 } 0230 0231 QLabel *WidgetFactory::createValueWidget(QWidget *parent) 0232 { 0233 auto valueWidget = new ValueWidget(parent); 0234 valueWidget->setWordWrap(true); 0235 valueWidget->setAlignment(Qt::AlignTop | Qt::AlignLeft); 0236 0237 valueWidget->setTextInteractionFlags(Qt::TextSelectableByMouse); 0238 valueWidget->setTextFormat(Qt::PlainText); 0239 0240 return valueWidget; 0241 } 0242 0243 QLabel *WidgetFactory::createLinkWidget(QWidget *parent) 0244 { 0245 auto valueWidget = new ValueWidget(parent); 0246 valueWidget->setWordWrap(true); 0247 valueWidget->setAlignment(Qt::AlignTop | Qt::AlignLeft); 0248 0249 valueWidget->setTextInteractionFlags(Qt::TextBrowserInteraction); 0250 valueWidget->setTextFormat(Qt::RichText); 0251 connect(valueWidget, &ValueWidget::linkActivated, this, [this](const QString &url) { 0252 Q_EMIT this->urlActivated(QUrl::fromUserInput(url)); 0253 }); 0254 0255 return valueWidget; 0256 } 0257 0258 // 0259 // Data Synchronization 0260 // 0261 0262 void WidgetFactory::slotCommentChanged(const QString &comment) 0263 { 0264 for (const KFileItem &item : std::as_const(m_items)) { 0265 QUrl url = item.targetUrl(); 0266 if (!url.isLocalFile()) { 0267 continue; 0268 } 0269 KFileMetaData::UserMetaData md(url.toLocalFile()); 0270 md.setUserComment(comment); 0271 } 0272 } 0273 0274 void WidgetFactory::slotRatingChanged(int rating) 0275 { 0276 for (const KFileItem &item : std::as_const(m_items)) { 0277 QUrl url = item.targetUrl(); 0278 if (!url.isLocalFile()) { 0279 continue; 0280 } 0281 KFileMetaData::UserMetaData md(url.toLocalFile()); 0282 md.setRating(rating); 0283 } 0284 } 0285 0286 void WidgetFactory::slotTagsChanged(const QStringList &tags) 0287 { 0288 if (m_tagWidget) { 0289 for (const KFileItem &item : std::as_const(m_items)) { 0290 QUrl url = item.targetUrl(); 0291 if (!url.isLocalFile()) { 0292 continue; 0293 } 0294 KFileMetaData::UserMetaData md(url.toLocalFile()); 0295 0296 // When multiple tags are selected one doesn't want to loose the old tags 0297 // of any of the resources. Unless specifically removed. 0298 QStringList newTags = md.tags() + tags; 0299 newTags.removeDuplicates(); 0300 0301 for (const QString &tag : std::as_const(m_prevTags)) { 0302 if (!tags.contains(tag)) { 0303 newTags.removeAll(tag); 0304 } 0305 } 0306 md.setTags(newTags); 0307 } 0308 0309 m_prevTags = tags; 0310 } 0311 } 0312 0313 // 0314 // Accessor Methods 0315 // 0316 void WidgetFactory::setReadOnly(bool value) 0317 { 0318 m_readOnly = value; 0319 } 0320 0321 void WidgetFactory::setItems(const KFileItemList &items) 0322 { 0323 m_items = items; 0324 } 0325 0326 Baloo::DateFormats WidgetFactory::dateFormat() const 0327 { 0328 return static_cast<Baloo::DateFormats>(m_dateFormat); 0329 } 0330 0331 void Baloo::WidgetFactory::setDateFormat(const Baloo::DateFormats format) 0332 { 0333 m_dateFormat = static_cast<QLocale::FormatType>(format); 0334 } 0335 0336 #include "moc_widgetfactory.cpp"