File indexing completed on 2025-04-27 03:58:26

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-08-11
0007  * Description : a combo box containing ICC profiles
0008  *
0009  * SPDX-FileCopyrightText: 2009-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "iccprofilescombobox.h"
0016 
0017 // Qt includes
0018 
0019 #include <QFileInfo>
0020 #include <QSet>
0021 #include <QAction>
0022 
0023 // KDE includes
0024 
0025 #include <klocalizedstring.h>
0026 
0027 // Local includes
0028 
0029 #include "icctransform.h"
0030 
0031 namespace Digikam
0032 {
0033 
0034 IccProfilesComboBox::IccProfilesComboBox(QWidget* const parent)
0035     : SqueezedComboBox(parent)
0036 {
0037 }
0038 
0039 IccProfilesComboBox::~IccProfilesComboBox()
0040 {
0041 }
0042 
0043 bool iccProfileLessThan(IccProfile a, IccProfile b)
0044 {
0045     return (a.description() < b.description());
0046 }
0047 
0048 // if needed outside this class, make it a public static method in a namespace
0049 static QString profileUserString(const IccProfile& p)
0050 {
0051     IccProfile profile(p);
0052     QFileInfo info(profile.filePath());
0053     QString fileName    = info.fileName();
0054 
0055     QString description = profile.description();
0056 
0057     if      (!description.isEmpty() && !fileName.isEmpty())
0058     {
0059         return i18nc("<Profile Description> (<File Name>)", "%1 (%2)", description, fileName);
0060     }
0061     else if (!fileName.isEmpty())
0062     {
0063         return fileName;
0064     }
0065     else
0066     {
0067         return QString();
0068     }
0069 }
0070 
0071 /**
0072  * NOTE: if needed outside this class, make it a public static method in a namespace
0073  */
0074 static void formatProfiles(const QList<IccProfile>& givenProfiles, QList<IccProfile>* const returnedProfiles, QStringList* const userText)
0075 {
0076     QList<IccProfile> profiles;
0077     QSet<QString>     filePaths;
0078 
0079     Q_FOREACH (IccProfile profile, givenProfiles) // krazy:exclude=foreach
0080     {
0081         QString filePath = profile.filePath();
0082 
0083         if (!profile.description().isNull() && (filePath.isNull() || !filePaths.contains(filePath)) )
0084         {
0085             profiles  << profile;
0086             filePaths << filePath;
0087         }
0088     }
0089 
0090     std::sort(profiles.begin(), profiles.end(), iccProfileLessThan);
0091 
0092     Q_FOREACH (IccProfile profile, profiles) // krazy:exclude=foreach
0093     {
0094         QString description = profileUserString(profile);
0095 
0096         if (description.isNull())
0097         {
0098             continue;
0099         }
0100 
0101         *returnedProfiles << profile;
0102         *userText         << description;
0103     }
0104 }
0105 
0106 void IccProfilesComboBox::addProfilesSqueezed(const QList<IccProfile>& givenProfiles)
0107 {
0108     QList<IccProfile> profiles;
0109     QStringList       userDescription;
0110     formatProfiles(givenProfiles, &profiles, &userDescription);
0111 
0112     for (int i = 0 ; i < profiles.size() ; ++i)
0113     {
0114         addSqueezedItem(userDescription.at(i), QVariant::fromValue(profiles.at(i)));
0115     }
0116 }
0117 
0118 void IccProfilesComboBox::addProfileSqueezed(const IccProfile& profile, const QString& d)
0119 {
0120     QString description = d;
0121 
0122     if (description.isNull())
0123     {
0124         description = profileUserString(profile);
0125     }
0126 
0127     addSqueezedItem(description, QVariant::fromValue(profile));
0128 }
0129 
0130 void IccProfilesComboBox::replaceProfilesSqueezed(const QList<IccProfile>& profiles)
0131 {
0132     IccProfile current = currentProfile();
0133     clear();
0134     addProfilesSqueezed(profiles);
0135     setCurrentProfile(current);
0136 }
0137 
0138 void IccProfilesComboBox::setNoProfileIfEmpty(const QString& message)
0139 {
0140     if (count() == 0)
0141     {
0142         setEnabled(false);
0143         addSqueezedItem(message);
0144         setCurrentIndex(0);
0145     }
0146 }
0147 
0148 IccProfile IccProfilesComboBox::currentProfile() const
0149 {
0150     return itemData(currentIndex()).value<IccProfile>();
0151 }
0152 
0153 void IccProfilesComboBox::setCurrentProfile(const IccProfile& profile)
0154 {
0155     if (profile.isNull())
0156     {
0157         setCurrentIndex(-1);
0158         return;
0159     }
0160 
0161     const int size = count();
0162 
0163     for (int i = 0 ; i < size ; ++i)
0164     {
0165         if (itemData(i).value<IccProfile>() == profile)
0166         {
0167             setCurrentIndex(i);
0168             return;
0169         }
0170     }
0171 
0172     setCurrentIndex(-1);
0173 }
0174 
0175 // ------------------------------------------------------------------------------------------
0176 
0177 IccProfilesMenuAction::IccProfilesMenuAction(const QIcon& icon, const QString& text, QObject* const parent)
0178     : QMenu   (text),
0179       m_parent(parent)
0180 {
0181     setIcon(icon);
0182 }
0183 
0184 IccProfilesMenuAction::IccProfilesMenuAction(const QString& text, QObject* const parent)
0185     : QMenu   (text),
0186       m_parent(parent)
0187 {
0188 }
0189 
0190 void IccProfilesMenuAction::replaceProfiles(const QList<IccProfile>& profiles)
0191 {
0192     clear();
0193     addProfiles(profiles);
0194 }
0195 
0196 void IccProfilesMenuAction::addProfiles(const QList<IccProfile>& givenProfiles)
0197 {
0198     QList<IccProfile> profiles;
0199     QStringList userDescription;
0200     formatProfiles(givenProfiles, &profiles, &userDescription);
0201 
0202     for (int i = 0 ; i < profiles.size() ; ++i)
0203     {
0204         addProfile(profiles.at(i), userDescription.at(i));
0205     }
0206 }
0207 
0208 void IccProfilesMenuAction::addProfile(const IccProfile& profile, const QString& d)
0209 {
0210     QString description = d;
0211 
0212     if (description.isNull())
0213     {
0214         description = profileUserString(profile);
0215     }
0216 
0217     QAction* const action = new QAction(d.left(50), m_parent);
0218     action->setData(QVariant::fromValue(profile));
0219     addAction(action);
0220 
0221     connect(action, &QAction::triggered,
0222             this, [this, action]() { slotTriggered(action); });
0223 }
0224 
0225 void IccProfilesMenuAction::disableIfEmpty()
0226 {
0227     if (isEmpty())
0228     {
0229         setEnabled(false);
0230     }
0231 }
0232 
0233 void IccProfilesMenuAction::slotTriggered(QObject* obj)
0234 {
0235     QAction* const action = static_cast<QAction*>(obj);
0236     IccProfile profile    = action->data().value<IccProfile>();
0237 
0238     if (!profile.isNull())
0239     {
0240         Q_EMIT triggered(profile);
0241     }
0242 }
0243 
0244 QObject* IccProfilesMenuAction::parentObject() const
0245 {
0246     return m_parent;
0247 }
0248 
0249 // ------------------------------------------------------------------------------------------
0250 
0251 IccRenderingIntentComboBox::IccRenderingIntentComboBox(QWidget* const parent)
0252     : QComboBox(parent)
0253 {
0254     addItem(i18n("Perceptual"),            IccTransform::Perceptual);
0255     addItem(i18n("Relative Colorimetric"), IccTransform::RelativeColorimetric);
0256     addItem(i18n("Absolute Colorimetric"), IccTransform::AbsoluteColorimetric);
0257     addItem(i18n("Saturation"),            IccTransform::Saturation);
0258     setWhatsThis(i18n("<ul><li><p><b>Perceptual intent</b> causes the full gamut of the image to be "
0259                       "compressed or expanded to fill the gamut of the destination device, so that gray balance is "
0260                       "preserved but colorimetric accuracy may not be preserved.</p>"
0261                       "<p>In other words, if certain colors in an image fall outside of the range of colors that the output "
0262                       "device can render, the image intent will cause all the colors in the image to be adjusted so that "
0263                       "the every color in the image falls within the range that can be rendered and so that the relationship "
0264                       "between colors is preserved as much as possible.</p>"
0265                       "<p>This intent is most suitable for display of photographs and images, and is the default intent.</p></li>"
0266                       "<li><p><b>Absolute Colorimetric intent</b> causes any colors that fall outside the range that the output device "
0267                       "can render to be adjusted to the closest color that can be rendered, while all other colors are "
0268                       "left unchanged.</p>"
0269                       "<p>This intent preserves the white point and is most suitable for spot colors (Pantone, TruMatch, "
0270                       "logo colors, ....)</p></li>"
0271                       "<li><p><b>Relative Colorimetric intent</b> is defined such that any colors that fall outside the range that the "
0272                       "output device can render are adjusted to the closest color that can be rendered, while all other colors "
0273                       "are left unchanged. Proof intent does not preserve the white point.</p></li>"
0274                       "<li><p><b>Saturation intent</b> preserves the saturation of colors in the image at the possible expense of "
0275                       "hue and lightness.</p>"
0276                       "<p>Implementation of this intent remains somewhat problematic, and the ICC is still working on methods to "
0277                       "achieve the desired effects.</p>"
0278                       "<p>This intent is most suitable for business graphics such as charts, where it is more important that the "
0279                       "colors be vivid and contrast well with each other rather than a specific color.</p></li></ul>"));
0280 }
0281 
0282 void IccRenderingIntentComboBox::setIntent(int intent)
0283 {
0284     const int size = count();
0285 
0286     for (int i = 0 ; i < size ; ++i)
0287     {
0288         if (itemData(i).toInt() == intent)
0289         {
0290             setCurrentIndex(i);
0291             return;
0292         }
0293     }
0294 
0295     setCurrentIndex(-1);
0296 }
0297 
0298 int IccRenderingIntentComboBox::intent() const
0299 {
0300     return itemData(currentIndex()).toInt();
0301 }
0302 
0303 } // namespace Digikam
0304 
0305 #include "moc_iccprofilescombobox.cpp"