Baloo Widgets

widgetfactory.cpp
1/*
2 SPDX-FileCopyrightText: 2012-2014 Vishesh Handa <vhanda@kde.org>
3
4 Code largely copied/adapted from KFileMetadataProvider
5 SPDX-FileCopyrightText: 2010 Peter Penz <peter.penz@gmx.at>
6
7 SPDX-License-Identifier: LGPL-2.1-or-later
8*/
9
10#include "widgetfactory.h"
11#include "KRatingWidget"
12#include "filemetadatautil_p.h"
13#include "kcommentwidget_p.h"
14#include "tagwidget.h"
15
16#include <KFileMetaData/PropertyInfo>
17#include <KFileMetaData/UserMetaData>
18
19#include <QCollator>
20#include <QLabel>
21#include <QLocale>
22#include <QMetaType>
23#include <QTime>
24#include <QUrl>
25
26#include <KApplicationTrader>
27#include <KFormat>
28#include <KLocalizedString>
29#include <KStringHandler>
30
31using namespace Baloo;
32
33WidgetFactory::WidgetFactory(QObject *parent)
34 : QObject(parent)
35 , m_dateFormat(QLocale::LongFormat)
36{
37}
38
39WidgetFactory::~WidgetFactory() = default;
40
41//
42// Widget Creation
43//
44static QString formatDateTime(const QVariant &value, QLocale::FormatType dateFormat)
45{
46 const QString valueString = value.toString();
48
49 if (dt.isValid()) {
50 KFormat form;
51 QTime time = dt.time();
52 // Check if Date/DateTime
53 if (!time.hour() && !time.minute() && !time.second()) {
54 return form.formatRelativeDate(dt.date(), dateFormat);
55 } else {
56 return form.formatRelativeDateTime(dt, dateFormat);
57 }
58 }
59
60 return valueString;
61}
62
63static QString toString(const QVariant &value, QLocale::FormatType dateFormat)
64{
65 switch (value.type()) {
66 case QVariant::Int:
67 return QLocale().toString(value.toInt());
69 return QLocale().toString(value.toDouble());
71 return value.toStringList().join(i18nc("String list separator", ", "));
72 case QVariant::Date:
73 case QVariant::DateTime: {
74 return formatDateTime(value, dateFormat);
75 }
76 case QVariant::List: {
78 const auto valueList = value.toList();
79 for (const QVariant &var : valueList) {
80 list << toString(var, dateFormat);
81 }
82 return list.join(i18nc("String list separator", ", "));
83 }
84
85 default:
86 return value.toString();
87 }
88}
89
90QWidget *WidgetFactory::createWidget(const QString &prop, const QVariant &value, QWidget *parent)
91{
92 QWidget *widget = nullptr;
93 const int maxUrlLength = 80;
94
95 if (prop == QLatin1String("rating")) {
96 widget = createRatingWidget(value.toInt(), parent);
97 } else if (prop == QLatin1String("userComment")) {
98 widget = createCommentWidget(value.toString(), parent);
99 } else if (prop == QLatin1String("tags")) {
100 widget = createTagWidget(Baloo::Private::sortTags(value.toStringList()), parent);
101 } else if (prop == QLatin1String("gpsLocation")) {
102 const auto pair = value.value<QPair<float, float>>();
103 const auto latitude = pair.first;
104 const auto longitude = pair.second;
105
106 const QString latitudeStr = latitude < 0 ? i18nc("Latitude (South)", "%1°S", -latitude) : i18nc("Latitude (North)", "%1°N", latitude);
107 const QString longitudeStr = longitude < 0 ? i18nc("Longitude (West)", "%1°W", -longitude) : i18nc("Longitude (East)", "%1°E", longitude);
108 const QString gpsLocationStr = latitudeStr + QLatin1Char(' ') + longitudeStr;
109
110 if (const auto geoService = KApplicationTrader::preferredService(QStringLiteral("x-scheme-handler/geo"))) {
111 const QString geoUri = QStringLiteral("geo:%1,%2").arg(latitude).arg(longitude);
112 QLabel *valueWidget = createLinkWidget(parent);
113
114 valueWidget->setText(QStringLiteral("<a href='%1'>%2</a>").arg(geoUri, gpsLocationStr));
115 valueWidget->setToolTip(i18nc("@info:tooltip Show location in map viewer", "Show location in %1", geoService->name()));
116 widget = valueWidget;
117 } else {
118 QLabel *valueWidget = createValueWidget(parent);
119 valueWidget->setText(gpsLocationStr);
120 widget = valueWidget;
121 }
122
123 } else if (prop == QLatin1String("originUrl")) {
124 QLabel *valueWidget = createLinkWidget(parent);
125 QString valueString = value.toUrl().toString();
126 // Shrink link label
127 auto labelString = KStringHandler::csqueeze(valueString, maxUrlLength);
128 valueString = QStringLiteral("<a href=\"%1\">%2</a>").arg(valueString, labelString);
129 valueWidget->setText(valueString);
130 widget = valueWidget;
131
132 } else {
133 QString valueString;
134 QLabel *valueWidget = createValueWidget(parent);
135
136 if (prop == QLatin1String("dimensions") && value.type() == QMetaType::QSize) {
137 valueString = i18nc("width × height", "%1 × %2", value.toSize().width(), value.toSize().height());
138 } else {
140 if (pi.property() != KFileMetaData::Property::Empty) {
141 if (pi.valueType() == QVariant::DateTime || pi.valueType() == QVariant::Date) {
142 valueString = formatDateTime(value, m_dateFormat);
143 } else {
144 valueString = pi.formatAsDisplayString(value);
145 }
146 } else {
147 valueString = toString(value, m_dateFormat);
148 }
149 }
150
151 valueWidget->setText(valueString);
152 widget = valueWidget;
153 }
154
155 widget->setForegroundRole(parent->foregroundRole());
156 widget->setFont(parent->font());
157 widget->setObjectName(prop);
158 return widget;
159}
160
161QWidget *WidgetFactory::createTagWidget(const QStringList &tags, QWidget *parent)
162{
163 auto tagWidget = new TagWidget(parent);
164 tagWidget->setReadyOnly(m_readOnly);
165 tagWidget->setSelectedTags(tags);
166
167 connect(tagWidget, &TagWidget::selectionChanged, this, &WidgetFactory::slotTagsChanged);
168 connect(tagWidget, &TagWidget::tagClicked, this, [this](const QString &tag) {
169 QUrl url;
170 url.setScheme(QStringLiteral("tags"));
171 url.setPath(tag);
172 Q_EMIT urlActivated(url);
173 });
174
175 m_tagWidget = tagWidget;
176 m_prevTags = tags;
177
178 return tagWidget;
179}
180
181QWidget *WidgetFactory::createCommentWidget(const QString &comment, QWidget *parent)
182{
183 auto commentWidget = new KCommentWidget(parent);
184 commentWidget->setText(comment);
185 commentWidget->setReadOnly(m_readOnly);
186
187 connect(commentWidget, &KCommentWidget::commentChanged, this, &WidgetFactory::slotCommentChanged);
188
189 m_commentWidget = commentWidget;
190
191 return commentWidget;
192}
193
194QWidget *WidgetFactory::createRatingWidget(int rating, QWidget *parent)
195{
196 auto ratingWidget = new KRatingWidget(parent);
197 const Qt::Alignment align = (ratingWidget->layoutDirection() == Qt::LeftToRight) ? Qt::AlignLeft : Qt::AlignRight;
198 ratingWidget->setAlignment(align);
199 ratingWidget->setRating(rating);
200 const QFontMetrics metrics(parent->font());
201 ratingWidget->setPixmapSize(metrics.height());
202
203 connect(ratingWidget, static_cast<void (KRatingWidget::*)(int)>(&KRatingWidget::ratingChanged), this, &WidgetFactory::slotRatingChanged);
204
205 m_ratingWidget = ratingWidget;
206
207 return ratingWidget;
208}
209
210// The default size hint of QLabel tries to return a square size.
211// This does not work well in combination with layouts that use
212// heightForWidth(): In this case it is possible that the content
213// of a label might get clipped. By specifying a size hint
214// with a maximum width that is necessary to contain the whole text,
215// using heightForWidth() assures having a non-clipped text.
216class ValueWidget : public QLabel
217{
218public:
219 explicit ValueWidget(QWidget *parent = nullptr);
220 QSize sizeHint() const override;
221};
222
223ValueWidget::ValueWidget(QWidget *parent)
224 : QLabel(parent)
225{
226}
227
228QSize ValueWidget::sizeHint() const
229{
230 QFontMetrics metrics(font());
231 // TODO: QLabel internally provides already a method sizeForWidth(),
232 // that would be sufficient. However this method is not accessible.
233 return metrics.size(Qt::TextSingleLine, text());
234}
235
236QLabel *WidgetFactory::createValueWidget(QWidget *parent)
237{
238 auto valueWidget = new ValueWidget(parent);
239 valueWidget->setWordWrap(true);
241
243 valueWidget->setTextFormat(Qt::PlainText);
244
245 return valueWidget;
246}
247
248QLabel *WidgetFactory::createLinkWidget(QWidget *parent)
249{
250 auto valueWidget = new ValueWidget(parent);
251 valueWidget->setWordWrap(true);
253
255 valueWidget->setTextFormat(Qt::RichText);
256 connect(valueWidget, &ValueWidget::linkActivated, this, [this](const QString &url) {
257 Q_EMIT this->urlActivated(QUrl::fromUserInput(url));
258 });
259
260 return valueWidget;
261}
262
263//
264// Data Synchronization
265//
266
267void WidgetFactory::slotCommentChanged(const QString &comment)
268{
269 for (const KFileItem &item : std::as_const(m_items)) {
270 QUrl url = item.targetUrl();
271 if (!url.isLocalFile()) {
272 continue;
273 }
274 KFileMetaData::UserMetaData md(url.toLocalFile());
275 md.setUserComment(comment);
276 }
277}
278
279void WidgetFactory::slotRatingChanged(int rating)
280{
281 for (const KFileItem &item : std::as_const(m_items)) {
282 QUrl url = item.targetUrl();
283 if (!url.isLocalFile()) {
284 continue;
285 }
286 KFileMetaData::UserMetaData md(url.toLocalFile());
287 md.setRating(rating);
288 }
289}
290
291void WidgetFactory::slotTagsChanged(const QStringList &tags)
292{
293 if (m_tagWidget) {
294 for (const KFileItem &item : std::as_const(m_items)) {
295 QUrl url = item.targetUrl();
296 if (!url.isLocalFile()) {
297 continue;
298 }
299 KFileMetaData::UserMetaData md(url.toLocalFile());
300
301 // When multiple tags are selected one doesn't want to loose the old tags
302 // of any of the resources. Unless specifically removed.
303 QStringList newTags = md.tags() + tags;
304 newTags.removeDuplicates();
305
306 for (const QString &tag : std::as_const(m_prevTags)) {
307 if (!tags.contains(tag)) {
308 newTags.removeAll(tag);
309 }
310 }
311 md.setTags(newTags);
312 }
313
314 m_prevTags = tags;
315 }
316}
317
318//
319// Accessor Methods
320//
321void WidgetFactory::setReadOnly(bool value)
322{
323 m_readOnly = value;
324}
325
326void WidgetFactory::setItems(const KFileItemList &items)
327{
328 m_items = items;
329}
330
331Baloo::DateFormats WidgetFactory::dateFormat() const
332{
333 return static_cast<Baloo::DateFormats>(m_dateFormat);
334}
335
336void Baloo::WidgetFactory::setDateFormat(const Baloo::DateFormats format)
337{
338 m_dateFormat = static_cast<QLocale::FormatType>(format);
339}
340
341#include "moc_widgetfactory.cpp"
Allows to change a selection of tags.
Definition tagwidget.h:31
void selectionChanged(const QStringList &tags)
Emitted whenever the selection of tags changes.
void tagClicked(const QString &)
This signal is emitted whenever a tag is clicked.
static PropertyInfo fromName(const QString &name)
QString formatRelativeDateTime(const QDateTime &dateTime, QLocale::FormatType format) const
QString formatRelativeDate(const QDate &date, QLocale::FormatType format) const
void ratingChanged(int rating)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KSERVICE_EXPORT KService::Ptr preferredService(const QString &mimeType)
QUrl geoUri(const QVariant &location)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KCOREADDONS_EXPORT QString csqueeze(const QString &str, int maxlen=40)
QDate date() const const
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
QTime time() const const
void setAlignment(Qt::Alignment)
void linkActivated(const QString &link)
void setText(const QString &)
void setTextFormat(Qt::TextFormat)
void setTextInteractionFlags(Qt::TextInteractionFlags flags)
void setWordWrap(bool on)
qsizetype removeAll(const AT &t)
QString toString(QDate date, FormatType format) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
void setObjectName(QAnyStringView name)
int height() const const
int width() const const
QString arg(Args &&... args) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
qsizetype removeDuplicates()
typedef Alignment
LeftToRight
TextSingleLine
PlainText
TextSelectableByMouse
int hour() const const
int minute() const const
int second() const const
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
bool isLocalFile() const const
void setPath(const QString &path, ParsingMode mode)
void setScheme(const QString &scheme)
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
Type type() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QList< QVariant > toList() const const
QSize toSize() const const
QString toString() const const
QStringList toStringList() const const
QUrl toUrl() const const
T value() const const
void setFont(const QFont &)
void setForegroundRole(QPalette::ColorRole role)
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:22 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.