1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2009-08-11
7 * Description : a combo box containing ICC profiles
8 *
9 * Copyright (C) 2009-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10 *
11 * This program is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation;
14 * either version 2, or (at your option)
15 * 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 "iccprofilescombobox.h"
25
26 // Qt includes
27
28 #include <QFileInfo>
29 #include <QSet>
30 #include <QMenu>
31 #include <QIcon>
32 #include <QAction>
33
34 // KDE includes
35
36 #include <klocalizedstring.h>
37
38 // Local includes
39
40 #include "icctransform.h"
41
42 namespace Digikam
43 {
44
IccProfilesComboBox(QWidget * const parent)45 IccProfilesComboBox::IccProfilesComboBox(QWidget* const parent)
46 : SqueezedComboBox(parent)
47 {
48 }
49
~IccProfilesComboBox()50 IccProfilesComboBox::~IccProfilesComboBox()
51 {
52 }
53
iccProfileLessThan(IccProfile a,IccProfile b)54 bool iccProfileLessThan(IccProfile a, IccProfile b)
55 {
56 return (a.description() < b.description());
57 }
58
59 // if needed outside this class, make it a public static method in a namespace
profileUserString(const IccProfile & p)60 static QString profileUserString(const IccProfile& p)
61 {
62 IccProfile profile(p);
63 QFileInfo info(profile.filePath());
64 QString fileName = info.fileName();
65
66 QString description = profile.description();
67
68 if (!description.isEmpty() && !fileName.isEmpty())
69 {
70 return i18nc("<Profile Description> (<File Name>)", "%1 (%2)", description, fileName);
71 }
72 else if (!fileName.isEmpty())
73 {
74 return fileName;
75 }
76 else
77 {
78 return QString();
79 }
80 }
81
82 /**
83 * NOTE: if needed outside this class, make it a public static method in a namespace
84 */
formatProfiles(const QList<IccProfile> & givenProfiles,QList<IccProfile> * const returnedProfiles,QStringList * const userText)85 static void formatProfiles(const QList<IccProfile>& givenProfiles, QList<IccProfile>* const returnedProfiles, QStringList* const userText)
86 {
87 QList<IccProfile> profiles;
88 QSet<QString> filePaths;
89
90 foreach (IccProfile profile, givenProfiles) // krazy:exclude=foreach
91 {
92 QString filePath = profile.filePath();
93
94 if (!profile.description().isNull() && (filePath.isNull() || !filePaths.contains(filePath)) )
95 {
96 profiles << profile;
97 filePaths << filePath;
98 }
99 }
100
101 std::sort(profiles.begin(), profiles.end(), iccProfileLessThan);
102
103 foreach (IccProfile profile, profiles) // krazy:exclude=foreach
104 {
105 QString description = profileUserString(profile);
106
107 if (description.isNull())
108 {
109 continue;
110 }
111
112 *returnedProfiles << profile;
113 *userText << description;
114 }
115 }
116
addProfilesSqueezed(const QList<IccProfile> & givenProfiles)117 void IccProfilesComboBox::addProfilesSqueezed(const QList<IccProfile>& givenProfiles)
118 {
119 QList<IccProfile> profiles;
120 QStringList userDescription;
121 formatProfiles(givenProfiles, &profiles, &userDescription);
122
123 for (int i = 0 ; i < profiles.size() ; ++i)
124 {
125 addSqueezedItem(userDescription.at(i), QVariant::fromValue(profiles.at(i)));
126 }
127 }
128
addProfileSqueezed(const IccProfile & profile,const QString & d)129 void IccProfilesComboBox::addProfileSqueezed(const IccProfile& profile, const QString& d)
130 {
131 QString description = d;
132
133 if (description.isNull())
134 {
135 description = profileUserString(profile);
136 }
137
138 addSqueezedItem(description, QVariant::fromValue(profile));
139 }
140
replaceProfilesSqueezed(const QList<IccProfile> & profiles)141 void IccProfilesComboBox::replaceProfilesSqueezed(const QList<IccProfile>& profiles)
142 {
143 IccProfile current = currentProfile();
144 clear();
145 addProfilesSqueezed(profiles);
146 setCurrentProfile(current);
147 }
148
setNoProfileIfEmpty(const QString & message)149 void IccProfilesComboBox::setNoProfileIfEmpty(const QString& message)
150 {
151 if (count() == 0)
152 {
153 setEnabled(false);
154 addSqueezedItem(message);
155 setCurrentIndex(0);
156 }
157 }
158
currentProfile() const159 IccProfile IccProfilesComboBox::currentProfile() const
160 {
161 return itemData(currentIndex()).value<IccProfile>();
162 }
163
setCurrentProfile(const IccProfile & profile)164 void IccProfilesComboBox::setCurrentProfile(const IccProfile& profile)
165 {
166 if (profile.isNull())
167 {
168 setCurrentIndex(-1);
169 return;
170 }
171
172 const int size = count();
173
174 for (int i = 0 ; i < size ; ++i)
175 {
176 if (itemData(i).value<IccProfile>() == profile)
177 {
178 setCurrentIndex(i);
179 return;
180 }
181 }
182
183 setCurrentIndex(-1);
184 }
185
186 // ------------------------------------------------------------------------------------------
187
IccProfilesMenuAction(const QIcon & icon,const QString & text,QObject * const parent)188 IccProfilesMenuAction::IccProfilesMenuAction(const QIcon& icon, const QString& text, QObject* const parent)
189 : QMenu (text),
190 m_parent(parent)
191 {
192 setIcon(icon);
193 }
194
IccProfilesMenuAction(const QString & text,QObject * const parent)195 IccProfilesMenuAction::IccProfilesMenuAction(const QString& text, QObject* const parent)
196 : QMenu (text),
197 m_parent(parent)
198 {
199 }
200
replaceProfiles(const QList<IccProfile> & profiles)201 void IccProfilesMenuAction::replaceProfiles(const QList<IccProfile>& profiles)
202 {
203 clear();
204 addProfiles(profiles);
205 }
206
addProfiles(const QList<IccProfile> & givenProfiles)207 void IccProfilesMenuAction::addProfiles(const QList<IccProfile>& givenProfiles)
208 {
209 QList<IccProfile> profiles;
210 QStringList userDescription;
211 formatProfiles(givenProfiles, &profiles, &userDescription);
212
213 for (int i = 0 ; i < profiles.size() ; ++i)
214 {
215 addProfile(profiles.at(i), userDescription.at(i));
216 }
217 }
218
addProfile(const IccProfile & profile,const QString & d)219 void IccProfilesMenuAction::addProfile(const IccProfile& profile, const QString& d)
220 {
221 QString description = d;
222
223 if (description.isNull())
224 {
225 description = profileUserString(profile);
226 }
227
228 QAction* const action = new QAction(d.left(50), m_parent);
229 action->setData(QVariant::fromValue(profile));
230 addAction(action);
231
232 connect(action, &QAction::triggered,
233 this, [this, action]() { slotTriggered(action); });
234 }
235
disableIfEmpty()236 void IccProfilesMenuAction::disableIfEmpty()
237 {
238 if (isEmpty())
239 {
240 setEnabled(false);
241 }
242 }
243
slotTriggered(QObject * obj)244 void IccProfilesMenuAction::slotTriggered(QObject* obj)
245 {
246 QAction* const action = static_cast<QAction*>(obj);
247 IccProfile profile = action->data().value<IccProfile>();
248
249 if (!profile.isNull())
250 {
251 emit triggered(profile);
252 }
253 }
254
parentObject() const255 QObject* IccProfilesMenuAction::parentObject() const
256 {
257 return m_parent;
258 }
259
260 // ------------------------------------------------------------------------------------------
261
IccRenderingIntentComboBox(QWidget * const parent)262 IccRenderingIntentComboBox::IccRenderingIntentComboBox(QWidget* const parent)
263 : QComboBox(parent)
264 {
265 addItem(i18n("Perceptual"), IccTransform::Perceptual);
266 addItem(i18n("Relative Colorimetric"), IccTransform::RelativeColorimetric);
267 addItem(i18n("Absolute Colorimetric"), IccTransform::AbsoluteColorimetric);
268 addItem(i18n("Saturation"), IccTransform::Saturation);
269 setWhatsThis(i18n("<ul><li><p><b>Perceptual intent</b> causes the full gamut of the image to be "
270 "compressed or expanded to fill the gamut of the destination device, so that gray balance is "
271 "preserved but colorimetric accuracy may not be preserved.</p>"
272 "<p>In other words, if certain colors in an image fall outside of the range of colors that the output "
273 "device can render, the image intent will cause all the colors in the image to be adjusted so that "
274 "the every color in the image falls within the range that can be rendered and so that the relationship "
275 "between colors is preserved as much as possible.</p>"
276 "<p>This intent is most suitable for display of photographs and images, and is the default intent.</p></li>"
277 "<li><p><b>Absolute Colorimetric intent</b> causes any colors that fall outside the range that the output device "
278 "can render to be adjusted to the closest color that can be rendered, while all other colors are "
279 "left unchanged.</p>"
280 "<p>This intent preserves the white point and is most suitable for spot colors (Pantone, TruMatch, "
281 "logo colors, ....)</p></li>"
282 "<li><p><b>Relative Colorimetric intent</b> is defined such that any colors that fall outside the range that the "
283 "output device can render are adjusted to the closest color that can be rendered, while all other colors "
284 "are left unchanged. Proof intent does not preserve the white point.</p></li>"
285 "<li><p><b>Saturation intent</b> preserves the saturation of colors in the image at the possible expense of "
286 "hue and lightness.</p>"
287 "<p>Implementation of this intent remains somewhat problematic, and the ICC is still working on methods to "
288 "achieve the desired effects.</p>"
289 "<p>This intent is most suitable for business graphics such as charts, where it is more important that the "
290 "colors be vivid and contrast well with each other rather than a specific color.</p></li></ul>"));
291 }
292
setIntent(int intent)293 void IccRenderingIntentComboBox::setIntent(int intent)
294 {
295 const int size = count();
296
297 for (int i = 0 ; i < size ; ++i)
298 {
299 if (itemData(i).toInt() == intent)
300 {
301 setCurrentIndex(i);
302 return;
303 }
304 }
305
306 setCurrentIndex(-1);
307 }
308
intent() const309 int IccRenderingIntentComboBox::intent() const
310 {
311 return itemData(currentIndex()).toInt();
312 }
313
314 } // namespace Digikam
315