File indexing completed on 2025-01-19 03:50:48

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2013-03-14
0007  * Description : Table view column helpers: Photo properties
0008  *
0009  * SPDX-FileCopyrightText: 2017-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2013      by Michael G. Hansen <mike at mghansen dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "tableview_column_photo.h"
0017 
0018 // Qt includes
0019 
0020 #include <QFormLayout>
0021 #include <QComboBox>
0022 #include <QLocale>
0023 
0024 // KDE includes
0025 
0026 #include <klocalizedstring.h>
0027 
0028 // Local includes
0029 
0030 #include "digikam_debug.h"
0031 #include "coredbinfocontainers.h"
0032 #include "itempropertiestab.h"
0033 #include "dmetadata.h"
0034 #include "iteminfo.h"
0035 
0036 namespace Digikam
0037 {
0038 
0039 namespace TableViewColumns
0040 {
0041 
0042 ColumnPhotoProperties::ColumnPhotoProperties(TableViewShared* const tableViewShared,
0043                                              const TableViewColumnConfiguration& pConfiguration,
0044                                              const SubColumn pSubColumn,
0045                                              QObject* const parent)
0046     : TableViewColumn(tableViewShared, pConfiguration, parent),
0047       subColumn      (pSubColumn)
0048 {
0049 }
0050 
0051 ColumnPhotoProperties::~ColumnPhotoProperties()
0052 {
0053 }
0054 
0055 QStringList ColumnPhotoProperties::getSubColumns()
0056 {
0057     QStringList columns;
0058     columns << QLatin1String("cameramaker")
0059             << QLatin1String("cameramodel")
0060             << QLatin1String("lens")
0061             << QLatin1String("aperture")
0062             << QLatin1String("focal")
0063             << QLatin1String("exposure")
0064             << QLatin1String("sensitivity")
0065             << QLatin1String("modeprogram")
0066             << QLatin1String("flash")
0067             << QLatin1String("whitebalance");
0068 
0069     return columns;
0070 }
0071 
0072 TableViewColumnDescription ColumnPhotoProperties::getDescription()
0073 {
0074     TableViewColumnDescription description(QLatin1String("photo-properties"), i18nc("@info: tableview", "Photo properties"));
0075     description.setIcon(QLatin1String("camera-photo"));
0076 
0077     description.addSubColumn(TableViewColumnDescription(QLatin1String("cameramaker"),  i18nc("@title: tableview", "Camera maker")));
0078     description.addSubColumn(TableViewColumnDescription(QLatin1String("cameramodel"),  i18nc("@title: tableview", "Camera model")));
0079     description.addSubColumn(TableViewColumnDescription(QLatin1String("lens"),         i18nc("@title: tableview", "Lens")));
0080     description.addSubColumn(TableViewColumnDescription(QLatin1String("aperture"),     i18nc("@title: tableview", "Aperture")));
0081     description.addSubColumn(TableViewColumnDescription(QLatin1String("focal"),        i18nc("@title: tableview", "Focal length")));
0082     description.addSubColumn(TableViewColumnDescription(QLatin1String("exposure"),     i18nc("@title: tableview", "Exposure")));
0083     description.addSubColumn(TableViewColumnDescription(QLatin1String("sensitivity"),  i18nc("@title: tableview", "Sensitivity")));
0084     description.addSubColumn(TableViewColumnDescription(QLatin1String("modeprogram"),  i18nc("@title: tableview", "Mode/program")));
0085     description.addSubColumn(TableViewColumnDescription(QLatin1String("flash"),        i18nc("@title: tableview", "Flash")));
0086     description.addSubColumn(TableViewColumnDescription(QLatin1String("whitebalance"), i18nc("@title: tableview", "White balance")));
0087 
0088     return description;
0089 }
0090 
0091 QString ColumnPhotoProperties::getTitle() const
0092 {
0093     switch (subColumn)
0094     {
0095         case SubColumnCameraMaker:
0096         {
0097             return i18nc("@title: tableview", "Camera maker");
0098         }
0099 
0100         case SubColumnCameraModel:
0101         {
0102             return i18nc("@title: tableview", "Camera model");
0103         }
0104 
0105         case SubColumnLens:
0106         {
0107             return i18nc("@title: tableview", "Lens");
0108         }
0109 
0110         case SubColumnAperture:
0111         {
0112             return i18nc("@title: tableview", "Aperture");
0113         }
0114 
0115         case SubColumnFocal:
0116         {
0117             return i18nc("@title: tableview", "Focal length");
0118         }
0119 
0120         case SubColumnExposure:
0121         {
0122             return i18nc("@title: tableview", "Exposure");
0123         }
0124 
0125         case SubColumnSensitivity:
0126         {
0127             return i18nc("@title: tableview", "Sensitivity");
0128         }
0129 
0130         case SubColumnModeProgram:
0131         {
0132             return i18nc("@title: tableview", "Mode/program");
0133         }
0134 
0135         case SubColumnFlash:
0136         {
0137             return i18nc("@title: tableview", "Flash");
0138         }
0139 
0140         case SubColumnWhiteBalance:
0141         {
0142             return i18nc("@title: tableview", "White balance");
0143         }
0144     }
0145 
0146     return QString();
0147 }
0148 
0149 TableViewColumn::ColumnFlags ColumnPhotoProperties::getColumnFlags() const
0150 {
0151     ColumnFlags flags(ColumnNoFlags);
0152 
0153     if (
0154         (subColumn == SubColumnAperture)  ||
0155         (subColumn == SubColumnFocal)     ||
0156         (subColumn == SubColumnExposure)  ||
0157         (subColumn == SubColumnSensitivity)
0158        )
0159     {
0160         flags |= ColumnCustomSorting;
0161     }
0162 
0163     if (subColumn == SubColumnExposure)
0164     {
0165         flags |= ColumnHasConfigurationWidget;
0166     }
0167 
0168     return flags;
0169 }
0170 
0171 QVariant ColumnPhotoProperties::data(TableViewModel::Item* const item, const int role) const
0172 {
0173     if (role != Qt::DisplayRole)
0174     {
0175         return QVariant();
0176     }
0177 
0178     switch (subColumn)
0179     {
0180         case SubColumnCameraMaker:
0181         {
0182             QString cameraMaker = s->tableViewModel->itemDatabaseFieldRaw(item,
0183                                   DatabaseFields::Set(DatabaseFields::Make)).toString();
0184             ItemPropertiesTab::shortenedMakeInfo(cameraMaker);
0185 
0186             return cameraMaker;
0187         }
0188 
0189         case SubColumnCameraModel:
0190         {
0191             QString cameraModel = s->tableViewModel->itemDatabaseFieldRaw(item,
0192                                   DatabaseFields::Set(DatabaseFields::Model)).toString();
0193             ItemPropertiesTab::shortenedModelInfo(cameraModel);
0194 
0195             return cameraModel;
0196         }
0197 
0198         case SubColumnLens:
0199         {
0200             const QString cameraLens = s->tableViewModel->itemDatabaseFieldRaw(item,
0201                                        DatabaseFields::Set(DatabaseFields::Lens)).toString();
0202 
0203             return cameraLens;
0204         }
0205 
0206         case SubColumnAperture:
0207         {
0208             const QVariant apertureVariant = s->tableViewModel->itemDatabaseFieldRaw(item,
0209                                              DatabaseFields::Set(DatabaseFields::Aperture));
0210             const QString apertureString   = DMetadata::valueToString(apertureVariant, MetadataInfo::Aperture);
0211 
0212             return apertureString;
0213         }
0214 
0215         case SubColumnFocal:
0216         {
0217             /// @todo Make this configurable
0218 
0219             const DatabaseFields::Set requiredSet                 = DatabaseFields::Set(DatabaseFields::FocalLength | DatabaseFields::FocalLength35);
0220             const TableViewModel::DatabaseFieldsHashRaw rawFields = s->tableViewModel->itemDatabaseFieldsRaw(item, requiredSet);
0221             const QVariant focalLengthVariant                     = rawFields.value(DatabaseFields::FocalLength);
0222             const QString focalLengthString                       = DMetadata::valueToString(focalLengthVariant, MetadataInfo::FocalLength);
0223             const QVariant focalLength35Variant                   = rawFields.value(DatabaseFields::FocalLength35);
0224             const QString focalLength35String                     = DMetadata::valueToString(focalLength35Variant, MetadataInfo::FocalLengthIn35mm);
0225 
0226             if (focalLength35String.isEmpty())
0227             {
0228                 return focalLengthString;
0229             }
0230 
0231             if (focalLengthString.isEmpty())
0232             {
0233                 return QString();
0234             }
0235 
0236             /// @todo What if only 35 mm is set?
0237 
0238             return i18nc("@info: tableview", "%1 (%2)", focalLengthString, focalLength35String);
0239         }
0240 
0241         case SubColumnExposure:
0242         {
0243             /// @todo Add a configuration option for fraction vs number, units s vs ms vs mus
0244 
0245             const QVariant exposureVariant = s->tableViewModel->itemDatabaseFieldRaw(item,
0246                                              DatabaseFields::Set(DatabaseFields::ExposureTime));
0247 
0248             if (configuration.getSetting(QLatin1String("format"), QLatin1String("fraction")) == QLatin1String("fraction"))
0249             {
0250                 const QString exposureString = DMetadata::valueToString(exposureVariant, MetadataInfo::ExposureTime);
0251 
0252                 return exposureString;
0253             }
0254 
0255             if (!exposureVariant.isValid())
0256             {
0257                 return QString();
0258             }
0259 
0260             const QString unitKey = configuration.getSetting(QLatin1String("unit"), QLatin1String("seconds"));
0261             double multiplier     = 1.0;
0262             KLocalizedString exposureTimeLocalizedString = ki18nc("@info: tableview", "%1 s");
0263 
0264             if      (unitKey == QLatin1String("milliseconds"))
0265             {
0266                 multiplier                  = 1000.0;
0267                 exposureTimeLocalizedString = ki18nc("@info: tableview", "%1 ms");
0268             }
0269             else if (unitKey == QLatin1String("microseconds"))
0270             {
0271                 multiplier                  = 1000000.0;
0272                 exposureTimeLocalizedString = ki18nc("@info: tableview", "%1 µs");
0273             }
0274 
0275             const double exposureTime = exposureVariant.toDouble() * multiplier;
0276 
0277             /// @todo Seems like we have to check for 0 here, too, as an invalid value
0278 
0279             if (exposureTime == 0)
0280             {
0281                 return QString();
0282             }
0283 
0284             /// @todo Remove trailing zeros?
0285             /// @todo Align right? --> better align at decimal point
0286 
0287             const QString exposureTimeString = exposureTimeLocalizedString.subs(QLocale().toString(exposureTime, 'g', 3)).toString();
0288 
0289             return exposureTimeString;
0290         }
0291 
0292         case SubColumnSensitivity:
0293         {
0294             const QVariant sensitivityVariant = s->tableViewModel->itemDatabaseFieldRaw(item,
0295                                                 DatabaseFields::Set(DatabaseFields::Sensitivity));
0296             const QString sensitivityString   = DMetadata::valueToString(sensitivityVariant, MetadataInfo::Sensitivity);
0297 
0298             if (sensitivityString.isEmpty())
0299             {
0300                 return QString();
0301             }
0302 
0303             return i18nc("@info: tableview", "%1 ISO", sensitivityString);
0304         }
0305 
0306         case SubColumnModeProgram:
0307         {
0308             const DatabaseFields::Set requiredSet                 = DatabaseFields::Set(DatabaseFields::ExposureMode | DatabaseFields::ExposureProgram);
0309             const TableViewModel::DatabaseFieldsHashRaw rawFields = s->tableViewModel->itemDatabaseFieldsRaw(item, requiredSet);
0310             const QVariant exposureModeVariant                    = rawFields.value(DatabaseFields::ExposureMode);
0311             const QString exposureModeString                      = DMetadata::valueToString(exposureModeVariant, MetadataInfo::ExposureMode);
0312             const QVariant exposureProgramVariant                 = rawFields.value(DatabaseFields::ExposureProgram);
0313             const QString exposureProgramString                   = DMetadata::valueToString(exposureProgramVariant, MetadataInfo::ExposureProgram);
0314 
0315             if      (exposureModeString.isEmpty() && exposureProgramString.isEmpty())
0316             {
0317                 return QString();
0318             }
0319             else if (!exposureModeString.isEmpty() && exposureProgramString.isEmpty())
0320             {
0321                 return exposureModeString;
0322             }
0323             else if (exposureModeString.isEmpty() && !exposureProgramString.isEmpty())
0324             {
0325                 return exposureProgramString;
0326             }
0327 
0328             return QString::fromUtf8("%1 / %2").arg(exposureModeString).arg(exposureProgramString);
0329         }
0330 
0331         case SubColumnFlash:
0332         {
0333             const QVariant flashModeVariant = s->tableViewModel->itemDatabaseFieldRaw(item,
0334                                               DatabaseFields::Set(DatabaseFields::FlashMode));
0335             const QString flashModeString   = DMetadata::valueToString(flashModeVariant, MetadataInfo::FlashMode);
0336 
0337             return flashModeString;
0338         }
0339 
0340         case SubColumnWhiteBalance:
0341         {
0342             const QVariant whiteBalanceVariant = s->tableViewModel->itemDatabaseFieldRaw(item,
0343                                                  DatabaseFields::Set(DatabaseFields::WhiteBalance));
0344             const QString whiteBalanceString   = DMetadata::valueToString(whiteBalanceVariant, MetadataInfo::WhiteBalance);
0345 
0346             return whiteBalanceString;
0347         }
0348     }
0349 
0350     return QVariant();
0351 }
0352 
0353 TableViewColumn::ColumnCompareResult ColumnPhotoProperties::compare(TableViewModel::Item* const itemA, TableViewModel::Item* const itemB) const
0354 {
0355     const ItemInfo infoA = s->tableViewModel->infoFromItem(itemA);
0356     const ItemInfo infoB = s->tableViewModel->infoFromItem(itemB);
0357 
0358     switch (subColumn)
0359     {
0360         case SubColumnAperture:
0361         {
0362             const QVariant variantA = s->tableViewModel->itemDatabaseFieldRaw(itemA,
0363                                       DatabaseFields::Set(DatabaseFields::Aperture));
0364             const QVariant variantB = s->tableViewModel->itemDatabaseFieldRaw(itemB,
0365                                       DatabaseFields::Set(DatabaseFields::Aperture));
0366             const double apertureA  = variantA.toDouble();
0367             const double apertureB  = variantB.toDouble();
0368 
0369             return compareHelper<double>(apertureA, apertureB);
0370         }
0371 
0372         case SubColumnFocal:
0373         {
0374             /// @todo This just works if both have focal length set, not if focal length 35 has to be used
0375 
0376             const QVariant variantA   = s->tableViewModel->itemDatabaseFieldRaw(itemA,
0377                                         DatabaseFields::Set(DatabaseFields::FocalLength));
0378             const QVariant variantB   = s->tableViewModel->itemDatabaseFieldRaw(itemB,
0379                                         DatabaseFields::Set(DatabaseFields::FocalLength));
0380             const double focalLengthA = variantA.toDouble();
0381             const double focalLengthB = variantB.toDouble();
0382 
0383             return compareHelper<double>(focalLengthA, focalLengthB);
0384         }
0385 
0386         case SubColumnExposure:
0387         {
0388             const QVariant variantA    = s->tableViewModel->itemDatabaseFieldRaw(itemA,
0389                                          DatabaseFields::Set(DatabaseFields::ExposureTime));
0390             const QVariant variantB    = s->tableViewModel->itemDatabaseFieldRaw(itemB,
0391                                          DatabaseFields::Set(DatabaseFields::ExposureTime));
0392             const double exposureTimeA = variantA.toDouble();
0393             const double exposureTimeB = variantB.toDouble();
0394 
0395             return compareHelper<double>(exposureTimeA, exposureTimeB);
0396         }
0397 
0398         case SubColumnSensitivity:
0399         {
0400             const QVariant variantA   = s->tableViewModel->itemDatabaseFieldRaw(itemA,
0401                                         DatabaseFields::Set(DatabaseFields::Sensitivity));
0402             const QVariant variantB   = s->tableViewModel->itemDatabaseFieldRaw(itemB,
0403                                         DatabaseFields::Set(DatabaseFields::Sensitivity));
0404             const double sensitivityA = variantA.toDouble();
0405             const double sensitivityB = variantB.toDouble();
0406 
0407             return compareHelper<double>(sensitivityA, sensitivityB);
0408         }
0409 
0410         default:
0411         {
0412             qCWarning(DIGIKAM_GENERAL_LOG) << "item: unimplemented comparison, subColumn=" << subColumn;
0413 
0414             return CmpEqual;
0415         }
0416     }
0417 }
0418 
0419 TableViewColumnConfigurationWidget* ColumnPhotoProperties::getConfigurationWidget(QWidget* const parentWidget) const
0420 {
0421     TableViewColumnConfiguration myConfiguration = getConfiguration();
0422 
0423     return (new ColumnPhotoConfigurationWidget(s, myConfiguration, parentWidget));
0424 }
0425 
0426 // ---------------------------------------------------------------------------------------------------------------------
0427 
0428 ColumnPhotoConfigurationWidget::ColumnPhotoConfigurationWidget(TableViewShared* const sharedObject,
0429                                                                const TableViewColumnConfiguration& columnConfiguration,
0430                                                                QWidget* const parentWidget)
0431     : TableViewColumnConfigurationWidget(sharedObject, columnConfiguration, parentWidget),
0432       subColumn                         (ColumnPhotoProperties::SubColumnExposure),
0433       selectorExposureTimeFormat        (nullptr),
0434       selectorExposureTimeUnit          (nullptr)
0435 {
0436     ColumnPhotoProperties::getSubColumnIndex<ColumnPhotoProperties>(configuration.columnId, &subColumn);
0437 
0438     switch (subColumn)
0439     {
0440         case ColumnPhotoProperties::SubColumnExposure:
0441         {
0442             QFormLayout* const box1    = new QFormLayout();
0443             selectorExposureTimeFormat = new QComboBox(this);
0444             selectorExposureTimeFormat->addItem(i18nc("@info: tableview", "Fraction"), QLatin1String("fraction"));
0445             selectorExposureTimeFormat->addItem(i18nc("@info: tableview", "Rational"), QLatin1String("rational"));
0446             box1->addRow(i18nc("@info: tableview", "Format"), selectorExposureTimeFormat);
0447 
0448             selectorExposureTimeUnit = new QComboBox(this);
0449             selectorExposureTimeUnit->addItem(i18nc("@info: tableview", "Seconds"),      QLatin1String("seconds"));
0450             selectorExposureTimeUnit->addItem(i18nc("@info: tableview", "Milliseconds"), QLatin1String("milliseconds"));
0451             selectorExposureTimeUnit->addItem(i18nc("@info: tableview", "Microseconds"), QLatin1String("microseconds"));
0452             box1->addRow(i18nc("@info: tableview", "Unit"), selectorExposureTimeUnit);
0453 
0454             setLayout(box1);
0455 
0456             const int indexF = selectorExposureTimeFormat->findData(configuration.getSetting(QLatin1String("format"), QLatin1String("fraction")));
0457             selectorExposureTimeFormat->setCurrentIndex((indexF >= 0) ? indexF : 0);
0458             const int indexU = selectorExposureTimeUnit->findData(configuration.getSetting(QLatin1String("unit"), QLatin1String("seconds")));
0459             selectorExposureTimeUnit->setCurrentIndex((indexU >= 0) ? indexU : 0);
0460 
0461             slotUpdateUI();
0462 
0463             connect(selectorExposureTimeFormat, SIGNAL(currentIndexChanged(int)),
0464                     this, SLOT(slotUpdateUI()));
0465             break;
0466         }
0467 
0468         default:
0469         {
0470             break;
0471         }
0472     }
0473 }
0474 
0475 ColumnPhotoConfigurationWidget::~ColumnPhotoConfigurationWidget()
0476 {
0477 }
0478 
0479 TableViewColumnConfiguration ColumnPhotoConfigurationWidget::getNewConfiguration()
0480 {
0481     const QString formatKey = selectorExposureTimeFormat->itemData(selectorExposureTimeFormat->currentIndex()).toString();
0482     configuration.columnSettings.insert(QLatin1String("format"), formatKey);
0483 
0484     const QString unitKey   = selectorExposureTimeUnit->itemData(selectorExposureTimeUnit->currentIndex()).toString();
0485     configuration.columnSettings.insert(QLatin1String("unit"), unitKey);
0486 
0487     return configuration;
0488 }
0489 
0490 void ColumnPhotoProperties::setConfiguration(const TableViewColumnConfiguration& newConfiguration)
0491 {
0492     configuration = newConfiguration;
0493 
0494     Q_EMIT signalAllDataChanged();
0495 }
0496 
0497 void ColumnPhotoConfigurationWidget::slotUpdateUI()
0498 {
0499     if (selectorExposureTimeFormat)
0500     {
0501         const QString currentKey = selectorExposureTimeFormat->itemData(selectorExposureTimeFormat->currentIndex()).toString();
0502         const bool needsUnits    = (currentKey == QLatin1String("rational"));
0503 
0504         selectorExposureTimeUnit->setEnabled(needsUnits);
0505     }
0506 }
0507 
0508 } // namespace TableViewColumns
0509 
0510 } // namespace Digikam
0511 
0512 #include "moc_tableview_column_photo.cpp"