1 // SPDX-FileCopyrightText: 2012-2020 The KPhotoAlbum Development Team
2 // SPDX-FileCopyrightText: 2021 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
3 //
4 // SPDX-License-Identifier: GPL-2.0-or-later
5 
6 #include "Grid.h"
7 
8 #include <kpaexif/Info.h>
9 
10 #include <kpabase/SettingsData.h>
11 
12 #include <QColor>
13 #include <QGridLayout>
14 #include <QLabel>
15 #include <QPainter>
16 #include <QPalette>
17 #include <QResizeEvent>
18 #include <QScrollBar>
19 #include <QTimer>
20 
Grid(QWidget * parent)21 Exif::Grid::Grid(QWidget *parent)
22     : QScrollArea(parent)
23 {
24     setFocusPolicy(Qt::WheelFocus);
25     setWidgetResizable(true);
26     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
27     viewport()->installEventFilter(this);
28     setMinimumSize(800, 400);
29 }
30 
31 class Background : public QWidget
32 {
33 protected:
paintEvent(QPaintEvent * event)34     void paintEvent(QPaintEvent *event) override
35     {
36         QPainter painter(this);
37         painter.fillRect(event->rect(), QColor(palette().window().color()));
38     }
39 };
40 
setupUI(const QString & charset)41 void Exif::Grid::setupUI(const QString &charset)
42 {
43     delete this->widget();
44     m_labels.clear();
45     Background *widget = new Background;
46 
47     QGridLayout *layout = new QGridLayout(widget);
48     layout->setSpacing(0);
49     int row = 0;
50 
51     const QMap<QString, QStringList> map = Exif::Info::instance()->infoForDialog(m_fileName, charset);
52     const StringSet groups = exifGroups(map);
53 
54     for (const QString &group : groups) {
55         layout->addWidget(headerLabel(group), row++, 0, 1, 4);
56 
57         // Items of group
58         const QMap<QString, QStringList> items = itemsForGroup(group, map);
59         QStringList sorted = items.keys();
60         sorted.sort();
61         int elements = sorted.size();
62         int perCol = (elements + 1) / 2;
63         int count = 0;
64         for (const QString &key : sorted) {
65             const int subrow = (count % perCol);
66             const QPalette::ColorRole role = (subrow & 1) ? QPalette::Base : QPalette::AlternateBase;
67             QPair<QLabel *, QLabel *> pair = infoLabelPair(exifNameNoGroup(key), items[key].join(QLatin1String(", ")), role);
68 
69             int col = (count / perCol) * 2;
70             layout->addWidget(pair.first, row + subrow, col);
71             layout->addWidget(pair.second, row + subrow, col + 1);
72             count++;
73         }
74         row += perCol;
75     }
76 
77     setWidget(widget);
78     widget->show();
79     QTimer::singleShot(0, this, SLOT(updateWidgetSize()));
80 }
81 
headerLabel(const QString & title)82 QLabel *Exif::Grid::headerLabel(const QString &title)
83 {
84     QLabel *label = new QLabel(title);
85 
86     label->setBackgroundRole(QPalette::Dark);
87     label->setForegroundRole(QPalette::BrightText);
88     label->setAutoFillBackground(true);
89     label->setAlignment(Qt::AlignCenter);
90 
91     return label;
92 }
93 
infoLabelPair(const QString & title,const QString & value,const QPalette::ColorRole backgroundRole)94 QPair<QLabel *, QLabel *> Exif::Grid::infoLabelPair(const QString &title, const QString &value, const QPalette::ColorRole backgroundRole)
95 {
96     QLabel *keyLabel = new QLabel(title);
97     QLabel *valueLabel = new QLabel(value);
98 
99     keyLabel->setBackgroundRole(backgroundRole);
100     valueLabel->setBackgroundRole(backgroundRole);
101     keyLabel->setAutoFillBackground(true);
102     valueLabel->setAutoFillBackground(true);
103     m_labels.append(qMakePair(keyLabel, valueLabel));
104     return qMakePair(keyLabel, valueLabel);
105 }
106 
updateWidgetSize()107 void Exif::Grid::updateWidgetSize()
108 {
109     widget()->setFixedSize(viewport()->width(), widget()->height());
110 }
111 
exifGroups(const QMap<QString,QStringList> & exifInfo)112 StringSet Exif::Grid::exifGroups(const QMap<QString, QStringList> &exifInfo)
113 {
114     StringSet result;
115     for (QMap<QString, QStringList>::ConstIterator it = exifInfo.begin(); it != exifInfo.end(); ++it) {
116         result.insert(groupName(it.key()));
117     }
118     return result;
119 }
120 
itemsForGroup(const QString & group,const QMap<QString,QStringList> & exifInfo)121 QMap<QString, QStringList> Exif::Grid::itemsForGroup(const QString &group, const QMap<QString, QStringList> &exifInfo)
122 {
123     QMap<QString, QStringList> result;
124     for (QMap<QString, QStringList>::ConstIterator it = exifInfo.begin(); it != exifInfo.end(); ++it) {
125         if (groupName(it.key()) == group)
126             result.insert(it.key(), it.value());
127     }
128     return result;
129 }
130 
groupName(const QString & exifName)131 QString Exif::Grid::groupName(const QString &exifName)
132 {
133     QStringList list = exifName.split(QString::fromLatin1("."));
134     list.pop_back();
135     return list.join(QString::fromLatin1("."));
136 }
137 
exifNameNoGroup(const QString & fullName)138 QString Exif::Grid::exifNameNoGroup(const QString &fullName)
139 {
140     return fullName.split(QString::fromLatin1(".")).last();
141 }
142 
scroll(int dy)143 void Exif::Grid::scroll(int dy)
144 {
145     verticalScrollBar()->setValue(verticalScrollBar()->value() + dy);
146 }
147 
updateSearchString(const QString & search)148 void Exif::Grid::updateSearchString(const QString &search)
149 {
150     for (QPair<QLabel *, QLabel *> tuple : m_labels) {
151         const bool matches = tuple.first->text().contains(search, Qt::CaseInsensitive) && search.length() != 0;
152         const auto fgRole = matches ? QPalette::HighlightedText : QPalette::Text;
153         const auto bgRole = matches ? QPalette::Highlight : QPalette::Base;
154         tuple.first->setForegroundRole(fgRole);
155         tuple.first->setBackgroundRole(bgRole);
156         tuple.second->setForegroundRole(fgRole);
157         tuple.second->setBackgroundRole(bgRole);
158         QFont fnt = tuple.first->font();
159         fnt.setBold(matches);
160         tuple.first->setFont(fnt);
161         tuple.second->setFont(fnt);
162     }
163 }
164 
keyPressEvent(QKeyEvent * e)165 void Exif::Grid::keyPressEvent(QKeyEvent *e)
166 {
167     switch (e->key()) {
168     case Qt::Key_Down:
169         scroll(20);
170         return;
171     case Qt::Key_Up:
172         scroll(-20);
173         return;
174     case Qt::Key_PageDown:
175         scroll(viewport()->height() - 20);
176         return;
177     case Qt::Key_PageUp:
178         scroll(-(viewport()->height() - 20));
179         return;
180     case Qt::Key_Escape:
181         QScrollArea::keyPressEvent(e); // Propagate to close dialog.
182         return;
183     }
184 }
185 
eventFilter(QObject * object,QEvent * event)186 bool Exif::Grid::eventFilter(QObject *object, QEvent *event)
187 {
188     if (object == viewport() && event->type() == QEvent::Resize) {
189         QResizeEvent *re = static_cast<QResizeEvent *>(event);
190         widget()->setFixedSize(re->size().width(), widget()->height());
191     }
192     return false;
193 }
194 
setFileName(const DB::FileName & fileName)195 void Exif::Grid::setFileName(const DB::FileName &fileName)
196 {
197     m_fileName = fileName;
198     setupUI(Settings::SettingsData::instance()->iptcCharset());
199 }
200 // vi:expandtab:tabstop=4 shiftwidth=4:
201