Baloo Widgets

filemetadatawidget.cpp
1/*
2 SPDX-FileCopyrightText: 2012-2013 Vishesh Handa <me@vhanda.in>
3
4 Adapted from KFileMetadataWidget
5 SPDX-FileCopyrightText: 2008 Sebastian Trueg <trueg@kde.org>
6 SPDX-FileCopyrightText: 2009-2010 Peter Penz <peter.penz@gmx.at>
7
8 SPDX-License-Identifier: LGPL-2.1-or-later
9*/
10
11#include "filemetadatawidget.h"
12#include "filemetadataprovider.h"
13#include "metadatafilter.h"
14#include "widgetfactory.h"
15
16#include <KConfig>
17#include <KConfigGroup>
18
19#include <QCheckBox>
20#include <QGridLayout>
21#include <QLabel>
22#include <QList>
23#include <QSpacerItem>
24#include <QString>
25
26using namespace Baloo;
27
28class Baloo::FileMetaDataWidgetPrivate
29{
30public:
31 struct Row {
32 QCheckBox *checkBox;
33 QLabel *label;
34 QWidget *value;
35 };
36
37 explicit FileMetaDataWidgetPrivate(FileMetaDataWidget *parent);
38 ~FileMetaDataWidgetPrivate();
39
40 FileMetaDataWidgetPrivate(const FileMetaDataWidgetPrivate&) = delete;
41 FileMetaDataWidget& operator=(const FileMetaDataWidgetPrivate&) = delete;
42
43 void deleteRows();
44
45 void slotLoadingFinished();
46
47 QStringList sortedKeys(const QVariantMap &data) const;
48 QLabel *createLabel(const QString &key, const QString &itemLabel, FileMetaDataWidget *parent);
49
50 void saveConfig();
51
52 QList<Row> m_rows;
53 FileMetaDataProvider *m_provider = nullptr;
54 QGridLayout *m_gridLayout = nullptr;
55
56 MetadataFilter *m_filter = nullptr;
57 WidgetFactory *m_widgetFactory = nullptr;
58
59 QMap<QString, bool> m_visibilityChanged;
60 bool m_configureVisibleProperties = false;
61
62private:
63 FileMetaDataWidget *const q;
64};
65
66FileMetaDataWidgetPrivate::FileMetaDataWidgetPrivate(FileMetaDataWidget *parent)
67 : m_rows()
68 , q(parent)
69{
70 m_filter = new MetadataFilter(q);
71
72 m_widgetFactory = new WidgetFactory(q);
73 QObject::connect(m_widgetFactory, &WidgetFactory::urlActivated, q, &FileMetaDataWidget::urlActivated);
74
75 // TODO: If KFileMetaDataProvider might get a public class in future KDE releases,
76 // the following code should be moved into KFileMetaDataWidget::setModel():
77 m_provider = new FileMetaDataProvider(q);
79 slotLoadingFinished();
80 });
81}
82
83FileMetaDataWidgetPrivate::~FileMetaDataWidgetPrivate() = default;
84
85void FileMetaDataWidgetPrivate::deleteRows()
86{
87 for (const Row &row : std::as_const(m_rows)) {
88 delete row.label;
89 row.value->deleteLater();
90 if (row.checkBox) {
91 row.checkBox->deleteLater();
92 }
93 }
94
95 m_rows.clear();
96}
97
98QLabel *FileMetaDataWidgetPrivate::createLabel(const QString &key, const QString &itemLabel, FileMetaDataWidget *parent)
99{
100 auto label = new QLabel(itemLabel + QLatin1Char(':'), parent);
103 label->setForegroundRole(parent->foregroundRole());
104 label->setFont(parent->font());
105 label->setWordWrap(true);
106 label->setAlignment(Qt::AlignTop | Qt::AlignRight);
107 label->setObjectName(QStringLiteral("L_%1").arg(key));
108 return label;
109}
110
111void FileMetaDataWidgetPrivate::slotLoadingFinished()
112{
113 deleteRows();
114
115 if (m_gridLayout == nullptr) {
116 m_gridLayout = new QGridLayout(q);
117 m_gridLayout->setContentsMargins(0, 0, 0, 0);
118 m_gridLayout->setSpacing(q->fontMetrics().height() / 4);
119 }
120
121 QVariantMap data = m_provider->data();
122 QStringList active;
123 if (m_configureVisibleProperties) {
124 active = m_filter->filter(data).keys();
125 auto changedIt = m_visibilityChanged.constBegin();
126 while (changedIt != m_visibilityChanged.constEnd()) {
127 if (changedIt.value()) {
128 active.append(changedIt.key());
129 } else {
130 active.removeAll(changedIt.key());
131 }
132 changedIt++;
133 }
134 m_widgetFactory->setReadOnly(true);
135 m_gridLayout->setColumnStretch(0, 1);
136 m_gridLayout->setColumnStretch(1, 3);
137 m_gridLayout->setColumnStretch(2, 0);
138 m_gridLayout->setColumnStretch(3, 6);
139 } else {
140 data = m_filter->filter(data);
141 m_widgetFactory->setReadOnly(m_provider->isReadOnly());
142 m_gridLayout->setColumnStretch(0, 4);
143 m_gridLayout->setColumnStretch(1, 0);
144 m_gridLayout->setColumnStretch(2, 6);
145 m_gridLayout->setColumnStretch(3, 0);
146 }
147
148 int rowIndex = 0;
149 // Iterate through all remaining items.
150 // Embed the label and the value as new row in the widget
151 const QStringList keys = sortedKeys(data);
152 const int spacerWidth = QFontMetrics(q->font()).size(Qt::TextSingleLine, QStringLiteral(" ")).width();
153
154 const int labelColumn = m_configureVisibleProperties ? 1 : 0;
155
156 for (const auto &key : keys) {
157 Row row;
158 if (m_configureVisibleProperties) {
159 row.checkBox = new QCheckBox(q);
160 if (active.contains(key)) {
161 row.checkBox->setChecked(true);
162 }
163 m_gridLayout->addWidget(row.checkBox, rowIndex, 0, Qt::AlignTop | Qt::AlignRight);
164 QObject::connect(row.checkBox, &QCheckBox::stateChanged, q, [this, key](int state) {
165 this->m_visibilityChanged[key] = (state == Qt::Checked);
166 });
167 } else {
168 row.checkBox = nullptr;
169 }
170
171 row.label = createLabel(key, m_provider->label(key), q);
172 m_gridLayout->addWidget(row.label, rowIndex, labelColumn + 0, Qt::AlignRight);
173
174 m_gridLayout->addItem(new QSpacerItem(spacerWidth, 1), rowIndex, labelColumn + 1);
175
176 row.value = m_widgetFactory->createWidget(key, data[key], q);
177 m_gridLayout->addWidget(row.value, rowIndex, labelColumn + 2, Qt::AlignLeft);
178
179 m_gridLayout->setRowStretch(rowIndex, 0);
180
181 // Remember the label and value-widget as row
182 m_rows.append(row);
183 ++rowIndex;
184 }
185
186 // Add vertical stretch - when the widget is embedded with extra vertical
187 // space, it should be added at the bottom, not distributed between the
188 // items.
189 m_gridLayout->addItem(new QSpacerItem(0, 0), rowIndex, 0, 1, -1);
190 m_gridLayout->setRowStretch(rowIndex, 1);
191
192 q->updateGeometry();
193 Q_EMIT q->metaDataRequestFinished(m_provider->items());
194}
195
196QStringList FileMetaDataWidgetPrivate::sortedKeys(const QVariantMap &data) const
197{
198 // Create a map, where the translated label prefixed with the
199 // sort priority acts as key. The data of each entry is the URI
200 // of the data. By this the all URIs are sorted by the sort priority
201 // and sub sorted by the translated labels.
203 QVariantMap::const_iterator hashIt = data.constBegin();
204 while (hashIt != data.constEnd()) {
205 const QString propName = hashIt.key();
206
207 QString key = m_provider->group(propName);
208 key += m_provider->label(propName);
209
210 map.insert(key, propName);
211 ++hashIt;
212 }
213
214 // Apply the URIs from the map to the list that will get returned.
215 // The list will then be alphabetically ordered by the translated labels of the URIs.
217 QMap<QString, QString>::const_iterator mapIt = map.constBegin();
218 while (mapIt != map.constEnd()) {
219 list.append(mapIt.value());
220 ++mapIt;
221 }
222
223 return list;
224}
225
226void FileMetaDataWidgetPrivate::saveConfig()
227{
228 if (m_visibilityChanged.isEmpty()) {
229 return;
230 }
231
232 KConfig config(QStringLiteral("baloofileinformationrc"), KConfig::NoGlobals);
233 KConfigGroup showGroup = config.group(QStringLiteral("Show"));
234
235 auto changedIt = m_visibilityChanged.constBegin();
236 while (changedIt != m_visibilityChanged.constEnd()) {
237 showGroup.writeEntry(changedIt.key(), changedIt.value());
238 changedIt++;
239 }
240
241 showGroup.sync();
242}
243
244FileMetaDataWidget::FileMetaDataWidget(QWidget *parent)
245 : QWidget(parent)
246 , d(new FileMetaDataWidgetPrivate(this))
247{
248}
249
250FileMetaDataWidget::~FileMetaDataWidget() = default;
251
252void FileMetaDataWidget::setItems(const KFileItemList &items)
253{
254 d->m_provider->setItems(items);
255 d->m_widgetFactory->setItems(items);
256}
257
258KFileItemList FileMetaDataWidget::items() const
259{
260 return d->m_provider->items();
261}
262
263void FileMetaDataWidget::setReadOnly(bool readOnly)
264{
265 d->m_provider->setReadOnly(readOnly);
266 d->m_widgetFactory->setReadOnly(readOnly);
267}
268
269bool FileMetaDataWidget::isReadOnly() const
270{
271 return d->m_provider->isReadOnly();
272}
273void FileMetaDataWidget::setDateFormat(const DateFormats format)
274{
275 d->m_widgetFactory->setDateFormat(format);
276}
277
278DateFormats FileMetaDataWidget::dateFormat() const
279{
280 return d->m_widgetFactory->dateFormat();
281}
282
283QSize FileMetaDataWidget::sizeHint() const
284{
285 if (d->m_gridLayout == nullptr) {
286 return QWidget::sizeHint();
287 }
288
289 // Calculate the required width for the labels and values
290 int leftWidthMax = 0;
291 int rightWidthMax = 0;
292 int rightWidthAverage = 0;
293 for (const FileMetaDataWidgetPrivate::Row &row : std::as_const(d->m_rows)) {
294 const QWidget *valueWidget = row.value;
295 const int rightWidth = valueWidget->sizeHint().width();
296 rightWidthAverage += rightWidth;
297 if (rightWidth > rightWidthMax) {
298 rightWidthMax = rightWidth;
299 }
300
301 const int leftWidth = row.label->sizeHint().width();
302 if (leftWidth > leftWidthMax) {
303 leftWidthMax = leftWidth;
304 }
305 }
306
307 // Some value widgets might return a very huge width for the size hint.
308 // Limit the maximum width to the double width of the overall average
309 // to assure a less messed layout.
310 if (d->m_rows.count() > 1) {
311 rightWidthAverage /= d->m_rows.count();
312 if (rightWidthMax > rightWidthAverage * 2) {
313 rightWidthMax = rightWidthAverage * 2;
314 }
315 }
316
317 // Based on the available width calculate the required height
318 const auto margins = d->m_gridLayout->contentsMargins();
319 int height = margins.top() + margins.bottom() + d->m_gridLayout->spacing() * (d->m_rows.count() - 1);
320 for (const FileMetaDataWidgetPrivate::Row &row : std::as_const(d->m_rows)) {
321 const QWidget *valueWidget = row.value;
322 const int rowHeight = qMax(row.label->heightForWidth(leftWidthMax), valueWidget->heightForWidth(rightWidthMax));
323 height += rowHeight;
324 }
325
326 const int width = margins.left() + margins.right() + leftWidthMax + d->m_gridLayout->spacing() + rightWidthMax;
327
328 return QSize{width, height};
329}
330
331void FileMetaDataWidget::setConfigurationMode(ConfigurationMode mode)
332{
333 if (mode == ConfigurationMode::ReStart) {
334 d->m_configureVisibleProperties = true;
335 } else if (mode == ConfigurationMode::Accept) {
336 d->saveConfig();
337 d->m_configureVisibleProperties = false;
338 } else if (mode == ConfigurationMode::Cancel) {
339 d->m_configureVisibleProperties = false;
340 }
341 d->m_visibilityChanged.clear();
342 d->slotLoadingFinished();
343}
344
345#include "moc_filemetadatawidget.cpp"
Provides the data for the MetaDataWidget.
void loadingFinished()
Emitted once per KFileMetaDataProvider::setItems() after data loading is finished.
virtual QString group(const QString &label) const
Meta data items are sorted alphabetically by their translated label per default.
virtual QString label(const QString &metaDataLabel) const
KConfigGroup group(const QString &group)
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
bool sync() override
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString label(StandardShortcut id)
void stateChanged(int state)
int height() const const
QSize size(int flags, const QString &text, int tabStops, int *tabArray) const const
virtual void addItem(QLayoutItem *item) override
void addWidget(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment)
void setColumnStretch(int column, int stretch)
void setRowStretch(int row, int stretch)
virtual void setSpacing(int spacing) override
void setContentsMargins(const QMargins &margins)
void append(QList< T > &&value)
void clear()
qsizetype removeAll(const AT &t)
const_iterator constBegin() const const
const_iterator constEnd() const const
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
int width() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
AlignTop
TextSingleLine
TextSelectableByMouse
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFontMetrics fontMetrics() const const
QPalette::ColorRole foregroundRole() const const
virtual int heightForWidth(int w) const const
void updateGeometry()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:21 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.