1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2013-02-25
7  * Description : Table view column helpers: Geographic columns
8  *
9  * Copyright (C) 2017-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C) 2013      by Michael G. Hansen <mike at mghansen dot de>
11  *
12  * This program is free software; you can redistribute it
13  * and/or modify it under the terms of the GNU General
14  * Public License as published by the Free Software Foundation;
15  * either version 2, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * ============================================================ */
23 
24 #include "tableview_column_geo.h"
25 
26 // Qt includes
27 
28 #include <QFormLayout>
29 #include <QComboBox>
30 #include <QLocale>
31 
32 // KDE includes
33 
34 #include <klocalizedstring.h>
35 
36 // Local includes
37 
38 #include "digikam_debug.h"
39 #include "iteminfo.h"
40 
41 namespace
42 {
43 
FormatAltitude(const qreal altitudeInMeters,const QLocale::MeasurementSystem & measureSystem)44 QString FormatAltitude(const qreal altitudeInMeters, const QLocale::MeasurementSystem& measureSystem)
45 {
46     if (measureSystem == QLocale::MetricSystem)
47     {
48         const QString altitudeInMetersString = QLocale().toString(altitudeInMeters);
49 
50         return QString::fromUtf8("%1 m").arg(altitudeInMetersString);
51     }
52     else
53     {
54         const qreal altitudeInFeet         = altitudeInMeters /* m */ / ( 0.3048 /* m/foot */ );
55         const QString altitudeInFeetString = QLocale().toString(altitudeInFeet, 'g', 2);
56 
57         return QString::fromUtf8("%1 ft").arg(altitudeInFeetString);
58     }
59 }
60 
61 } // namespace
62 
63 namespace Digikam
64 {
65 
66 namespace TableViewColumns
67 {
68 
ColumnGeoProperties(TableViewShared * const tableViewShared,const TableViewColumnConfiguration & pConfiguration,const SubColumn pSubColumn,QObject * const parent)69 ColumnGeoProperties::ColumnGeoProperties(TableViewShared* const tableViewShared,
70                                          const TableViewColumnConfiguration& pConfiguration,
71                                          const SubColumn pSubColumn,
72                                          QObject* const parent)
73     : TableViewColumn(tableViewShared, pConfiguration, parent),
74       subColumn      (pSubColumn)
75 {
76 }
77 
~ColumnGeoProperties()78 ColumnGeoProperties::~ColumnGeoProperties()
79 {
80 }
81 
getSubColumns()82 QStringList ColumnGeoProperties::getSubColumns()
83 {
84     QStringList columns;
85     columns << QLatin1String("geohascoordinates")
86             << QLatin1String("geocoordinates")
87             << QLatin1String("geoaltitude");
88 
89     return columns;
90 }
91 
getDescription()92 TableViewColumnDescription ColumnGeoProperties::getDescription()
93 {
94     TableViewColumnDescription description(QLatin1String("geo-properties"), i18n("Geo properties"));
95     description.setIcon(QLatin1String("globe"));
96     description.addSubColumn(TableViewColumnDescription(QLatin1String("geohascoordinates"), i18n("Geotagged")));
97     description.addSubColumn(TableViewColumnDescription(QLatin1String("geocoordinates"),    i18n("Coordinates")));
98     description.addSubColumn(TableViewColumnDescription(QLatin1String("geoaltitude"),       i18n("Altitude")));
99 
100     return description;
101 }
102 
getTitle() const103 QString ColumnGeoProperties::getTitle() const
104 {
105     switch (subColumn)
106     {
107         case SubColumnHasCoordinates:
108         {
109             return i18n("Geotagged");
110         }
111 
112         case SubColumnCoordinates:
113         {
114             return i18n("Coordinates");
115         }
116 
117         case SubColumnAltitude:
118         {
119             return i18n("Altitude");
120         }
121     }
122 
123     return QString();
124 }
125 
getColumnFlags() const126 TableViewColumn::ColumnFlags ColumnGeoProperties::getColumnFlags() const
127 {
128     ColumnFlags flags(ColumnNoFlags);
129 
130     if (subColumn == SubColumnAltitude)
131     {
132         flags |= (ColumnCustomSorting | ColumnHasConfigurationWidget);
133     }
134 
135     return flags;
136 }
137 
data(TableViewModel::Item * const item,const int role) const138 QVariant ColumnGeoProperties::data(TableViewModel::Item* const item, const int role) const
139 {
140     if (
141         (role != Qt::DisplayRole) &&
142         (role != Qt::TextAlignmentRole)
143        )
144     {
145         return QVariant();
146     }
147 
148     if (role == Qt::TextAlignmentRole)
149     {
150         switch (subColumn)
151         {
152             case SubColumnAltitude:
153             {
154                 return QVariant(Qt::Alignment(Qt::AlignRight | Qt::AlignVCenter));
155             }
156 
157             default:
158             {
159                 return QVariant();
160             }
161         }
162     }
163 
164     const ItemInfo info = s->tableViewModel->infoFromItem(item);
165 
166     switch (subColumn)
167     {
168         case SubColumnHasCoordinates:
169         {
170             return info.hasCoordinates() ? i18nc("@info: tableview", "Yes") : i18nc("@info: tableview", "No");
171         }
172 
173         case SubColumnCoordinates:
174         {
175             if (!info.hasCoordinates())
176             {
177                 return QString();
178             }
179 
180             return QString::fromUtf8("%1\n%2").arg(QLocale().toString(info.latitudeNumber(),  'g', 7))
181                                               .arg(QLocale().toString(info.longitudeNumber(), 'g', 7));
182         }
183 
184         case SubColumnAltitude:
185         {
186             /// @todo Needs custom sorting
187 
188             if ((!info.hasCoordinates()) || (!info.hasAltitude()))
189             {
190                 return QString();
191             }
192 
193             /// @todo Use an enum instead to avoid lots of string comparisons
194 
195             const QString formatKey                  = configuration.getSetting(QLatin1String("format"), QLatin1String("metric"));
196             QLocale::MeasurementSystem measureSystem = QLocale().measurementSystem();
197 
198             if      (formatKey == QLatin1String("metric"))
199             {
200                 measureSystem = QLocale::MetricSystem;
201             }
202             else if (formatKey == QLatin1String("imperial"))
203             {
204                 measureSystem = QLocale::ImperialSystem;
205             }
206 
207             const QString formattedAltitude = FormatAltitude(info.altitudeNumber(), measureSystem);
208 
209             return formattedAltitude;
210         }
211     }
212 
213     return QVariant();
214 }
215 
compare(TableViewModel::Item * const itemA,TableViewModel::Item * const itemB) const216 TableViewColumn::ColumnCompareResult ColumnGeoProperties::compare(TableViewModel::Item* const itemA,
217                                                                   TableViewModel::Item* const itemB) const
218 {
219     const ItemInfo infoA = s->tableViewModel->infoFromItem(itemA);
220     const ItemInfo infoB = s->tableViewModel->infoFromItem(itemB);
221 
222     if (subColumn == SubColumnAltitude)
223     {
224         const bool hasAltitudeA = infoA.hasAltitude();
225         const bool hasAltitudeB = infoB.hasAltitude();
226 
227         if (hasAltitudeA && hasAltitudeB)
228         {
229             const double altitudeA = infoA.altitudeNumber();
230             const double altitudeB = infoB.altitudeNumber();
231 
232             return (compareHelper<double>(altitudeA, altitudeB));
233         }
234 
235         return (compareHelper<int>(int(hasAltitudeA), int(hasAltitudeB)));
236     }
237 
238     qCWarning(DIGIKAM_GENERAL_LOG) << "geo: unimplemented comparison, subColumn=" << subColumn;
239 
240     return CmpEqual;
241 }
242 
getConfigurationWidget(QWidget * const parentWidget) const243 TableViewColumnConfigurationWidget* ColumnGeoProperties::getConfigurationWidget(QWidget* const parentWidget) const
244 {
245     TableViewColumnConfiguration myConfiguration = getConfiguration();
246 
247     return (new ColumnGeoConfigurationWidget(s, myConfiguration, parentWidget));
248 }
249 
250 // ----------------------------------------------------------------------------------------------------------------
251 
ColumnGeoConfigurationWidget(TableViewShared * const sharedObject,const TableViewColumnConfiguration & columnConfiguration,QWidget * const parentWidget)252 ColumnGeoConfigurationWidget::ColumnGeoConfigurationWidget(TableViewShared* const sharedObject,
253                                                            const TableViewColumnConfiguration& columnConfiguration,
254                                                            QWidget* const parentWidget)
255     : TableViewColumnConfigurationWidget(sharedObject, columnConfiguration, parentWidget),
256       subColumn(ColumnGeoProperties::SubColumnHasCoordinates),
257       selectorAltitudeUnit(nullptr)
258 {
259     ColumnGeoProperties::getSubColumnIndex<ColumnGeoProperties>(configuration.columnId, &subColumn);
260 
261     switch (subColumn)
262     {
263         case ColumnGeoProperties::SubColumnAltitude:
264         {
265             QFormLayout* const box1 = new QFormLayout();
266             selectorAltitudeUnit    = new QComboBox(this);
267             selectorAltitudeUnit->addItem(i18n("Metric units"),   QLatin1String("metric"));
268             selectorAltitudeUnit->addItem(i18n("Imperial units"), QLatin1String("imperial"));
269             box1->addRow(i18n("Display format"), selectorAltitudeUnit);
270 
271             setLayout(box1);
272 
273             const int index = selectorAltitudeUnit->findData(configuration.getSetting(QLatin1String("format"), QLatin1String("metric")));
274             selectorAltitudeUnit->setCurrentIndex((index >= 0) ? index : 0);
275             break;
276         }
277 
278         default:
279         {
280             break;
281         }
282     }
283 }
284 
~ColumnGeoConfigurationWidget()285 ColumnGeoConfigurationWidget::~ColumnGeoConfigurationWidget()
286 {
287 }
288 
getNewConfiguration()289 TableViewColumnConfiguration ColumnGeoConfigurationWidget::getNewConfiguration()
290 {
291     const QString formatKey = selectorAltitudeUnit->itemData(selectorAltitudeUnit->currentIndex()).toString();
292     configuration.columnSettings.insert(QLatin1String("format"), formatKey);
293 
294     return configuration;
295 }
296 
setConfiguration(const TableViewColumnConfiguration & newConfiguration)297 void ColumnGeoProperties::setConfiguration(const TableViewColumnConfiguration& newConfiguration)
298 {
299     configuration = newConfiguration;
300 
301     emit signalAllDataChanged();
302 }
303 
304 } // namespace TableViewColumns
305 
306 } // namespace Digikam
307