1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2004-11-17
7 * Description : item properties side bar (without support of digiKam database).
8 *
9 * Copyright (C) 2004-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
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 "itempropertiessidebar.h"
25
26 // Qt includes
27
28 #include <QRect>
29 #include <QSplitter>
30 #include <QFileInfo>
31 #include <QApplication>
32 #include <QIcon>
33 #include <QLocale>
34 #include <QMimeDatabase>
35 #include <QMimeType>
36
37 // KDE includes
38
39 #include <klocalizedstring.h>
40 #include <kconfiggroup.h>
41
42 // Local includes
43
44 #include "drawdecoder.h"
45 #include "digikam_config.h"
46 #include "digikam_debug.h"
47 #include "dimg.h"
48 #include "dmetadata.h"
49 #include "itempropertiestab.h"
50 #include "itemselectionpropertiestab.h"
51 #include "itempropertiesmetadatatab.h"
52 #include "itempropertiescolorstab.h"
53 #include "itempropertiesversionstab.h"
54
55 #ifdef HAVE_MARBLE
56 # include "itempropertiesgpstab.h"
57 #endif // HAVE_MARBLE
58
59 namespace Digikam
60 {
61
ItemPropertiesSideBar(QWidget * const parent,SidebarSplitter * const splitter,Qt::Edge side,bool mimimizedDefault)62 ItemPropertiesSideBar::ItemPropertiesSideBar(QWidget* const parent,
63 SidebarSplitter* const splitter,
64 Qt::Edge side,
65 bool mimimizedDefault)
66 : Sidebar (parent, splitter, side, mimimizedDefault),
67 m_dirtyPropertiesTab(false),
68 m_dirtyMetadataTab (false),
69 m_dirtyColorTab (false),
70 m_dirtyGpsTab (false),
71 m_dirtyHistoryTab (false),
72 m_currentRect (QRect()),
73 m_image (nullptr)
74 {
75 m_propertiesStackedView = new QStackedWidget(parent);
76 m_propertiesTab = new ItemPropertiesTab(parent);
77 m_selectionPropertiesTab = new ItemSelectionPropertiesTab(parent);
78 m_metadataTab = new ItemPropertiesMetadataTab(parent);
79 m_colorTab = new ItemPropertiesColorsTab(parent);
80
81 m_propertiesStackedView->addWidget(m_propertiesTab);
82 m_propertiesStackedView->addWidget(m_selectionPropertiesTab);
83
84 // NOTE: Special case with Showfoto which will only be able to load image, not video.
85
86 if (QApplication::applicationName() != QLatin1String("digikam"))
87 {
88 m_propertiesTab->setVideoInfoDisable(true);
89 }
90
91 appendTab(m_propertiesStackedView, QIcon::fromTheme(QLatin1String("configure")), i18nc("@title: item properties", "Properties"));
92 appendTab(m_metadataTab, QIcon::fromTheme(QLatin1String("format-text-code")), i18nc("@title: item properties", "Metadata")); // krazy:exclude=iconnames
93 appendTab(m_colorTab, QIcon::fromTheme(QLatin1String("fill-color")), i18nc("@title: item properties", "Colors"));
94
95 #ifdef HAVE_MARBLE
96
97 m_gpsTab = new ItemPropertiesGPSTab(parent);
98 appendTab(m_gpsTab, QIcon::fromTheme(QLatin1String("globe")), i18nc("@title: item properties", "Map"));
99
100 #endif // HAVE_MARBLE
101
102 connect(m_metadataTab, SIGNAL(signalSetupMetadataFilters(int)),
103 this, SIGNAL(signalSetupMetadataFilters(int)));
104
105 connect(m_metadataTab, SIGNAL(signalSetupExifTool()),
106 this, SIGNAL(signalSetupExifTool()));
107
108 // --- NOTE: use dynamic binding as slotChangedTab() is a virtual method which can be re-implemented in derived classes.
109
110 connect(this, &ItemPropertiesSideBar::signalChangedTab,
111 this, &ItemPropertiesSideBar::slotChangedTab);
112 }
113
~ItemPropertiesSideBar()114 ItemPropertiesSideBar::~ItemPropertiesSideBar()
115 {
116 }
117
itemChanged(const QUrl & url,const QRect & rect,DImg * const img)118 void ItemPropertiesSideBar::itemChanged(const QUrl& url, const QRect& rect, DImg* const img)
119 {
120 if (!url.isValid())
121 {
122 return;
123 }
124
125 m_currentURL = url;
126 m_currentRect = rect;
127 m_image = img;
128 m_dirtyPropertiesTab = false;
129 m_dirtyMetadataTab = false;
130 m_dirtyColorTab = false;
131 m_dirtyGpsTab = false;
132 m_dirtyHistoryTab = false;
133
134 slotChangedTab(getActiveTab());
135 }
136
slotNoCurrentItem()137 void ItemPropertiesSideBar::slotNoCurrentItem()
138 {
139 m_currentURL = QUrl();
140
141 m_selectionPropertiesTab->setCurrentURL();
142 m_propertiesTab->setCurrentURL();
143 m_metadataTab->setCurrentURL();
144 m_colorTab->setData();
145
146 #ifdef HAVE_MARBLE
147
148 m_gpsTab->setCurrentURL();
149
150 #endif // HAVE_MARBLE
151
152 m_dirtyPropertiesTab = false;
153 m_dirtyMetadataTab = false;
154 m_dirtyColorTab = false;
155 m_dirtyGpsTab = false;
156 m_dirtyHistoryTab = false;
157 }
158
slotImageSelectionChanged(const QRect & rect)159 void ItemPropertiesSideBar::slotImageSelectionChanged(const QRect& rect)
160 {
161 m_currentRect = rect;
162
163 if (m_dirtyColorTab)
164 {
165 m_colorTab->setSelection(rect);
166 }
167 else
168 {
169 slotChangedTab(m_colorTab);
170 }
171 }
172
slotChangedTab(QWidget * tab)173 void ItemPropertiesSideBar::slotChangedTab(QWidget* tab)
174 {
175 if (!m_currentURL.isValid())
176 {
177
178 #ifdef HAVE_MARBLE
179
180 m_gpsTab->setActive(tab == m_gpsTab);
181
182 #endif // HAVE_MARBLE
183
184 return;
185 }
186
187 setCursor(Qt::WaitCursor);
188
189 if ((tab == m_propertiesStackedView) && !m_dirtyPropertiesTab)
190 {
191 m_propertiesTab->setCurrentURL(m_currentURL);
192 setImagePropertiesInformation(m_currentURL);
193 m_dirtyPropertiesTab = true;
194 }
195 else if ((tab == m_metadataTab) && !m_dirtyMetadataTab)
196 {
197 m_metadataTab->setCurrentURL(m_currentURL);
198 m_dirtyMetadataTab = true;
199 }
200 else if ((tab == m_colorTab) && !m_dirtyColorTab)
201 {
202 m_colorTab->setData(m_currentURL, m_currentRect, m_image);
203 m_dirtyColorTab = true;
204 }
205
206 #ifdef HAVE_MARBLE
207
208 else if ((tab == m_gpsTab) && !m_dirtyGpsTab)
209 {
210 m_gpsTab->setCurrentURL(m_currentURL);
211 m_dirtyGpsTab = true;
212 }
213
214 m_gpsTab->setActive(tab == m_gpsTab);
215
216 #endif // HAVE_MARBLE
217
218 unsetCursor();
219 }
220
setImagePropertiesInformation(const QUrl & url)221 void ItemPropertiesSideBar::setImagePropertiesInformation(const QUrl& url)
222 {
223 if (!url.isValid())
224 {
225 return;
226 }
227
228 QString str;
229 QString unavailable(QString::fromUtf8("<i>%1</i>").arg(i18nc("@info: item properties", "unavailable")));
230 QFileInfo fileInfo(url.toLocalFile());
231 QScopedPointer<DMetadata> metaData(new DMetadata(url.toLocalFile()));
232
233 // -- File system information -----------------------------------------
234
235 QDateTime modifiedDate = fileInfo.lastModified();
236 str = QLocale().toString(modifiedDate, QLocale::ShortFormat);
237 m_propertiesTab->setFileModifiedDate(str);
238
239 str = QString::fromUtf8("%1 (%2)").arg(ItemPropertiesTab::humanReadableBytesCount(fileInfo.size()))
240 .arg(QLocale().toString(fileInfo.size()));
241 m_propertiesTab->setFileSize(str);
242 m_propertiesTab->setFileOwner(QString::fromUtf8("%1 - %2").arg(fileInfo.owner()).arg(fileInfo.group()));
243 m_propertiesTab->setFilePermissions(ItemPropertiesTab::permissionsString(fileInfo));
244
245 // -- Image Properties --------------------------------------------------
246
247 QSize dims;
248 QString bitDepth, colorMode;
249 QString rawFilesExt = DRawDecoder::rawFiles();
250 QString ext = fileInfo.suffix().toUpper();
251
252 if (!ext.isEmpty() && rawFilesExt.toUpper().contains(ext))
253 {
254 m_propertiesTab->setImageMime(i18nc("@info: item properties", "RAW Image"));
255 bitDepth = QLatin1String("48");
256 dims = metaData->getItemDimensions();
257 colorMode = i18nc("@info: item properties", "Uncalibrated");
258 }
259 else
260 {
261 m_propertiesTab->setImageMime(QMimeDatabase().mimeTypeForFile(fileInfo).comment());
262
263 dims = metaData->getPixelSize();
264
265 DImg img;
266 img.loadItemInfo(url.toLocalFile(), false, false, false, false);
267 bitDepth.setNum(img.originalBitDepth());
268 colorMode = DImg::colorModelToString(img.originalColorModel());
269 }
270
271 QString mpixels = QLocale().toString(dims.width()*dims.height()/1000000.0, 'f', 1);
272 str = (!dims.isValid()) ? i18nc("@info: item properties", "Unknown")
273 : i18nc("@info: item properties", "%1x%2 (%3Mpx)",
274 dims.width(), dims.height(), mpixels);
275 m_propertiesTab->setItemDimensions(str);
276
277 if (!dims.isValid())
278 {
279 str = i18nc("@info: item properties", "Unknown");
280 }
281 else
282 {
283 m_propertiesTab->aspectRatioToString(dims.width(), dims.height(), str);
284 }
285
286 m_propertiesTab->setImageRatio(str);
287 m_propertiesTab->setImageColorMode(colorMode.isEmpty() ? unavailable : colorMode);
288 m_propertiesTab->setImageBitDepth(bitDepth.isEmpty() ? unavailable : i18nc("@info: item properties", "%1 bpp", bitDepth));
289 m_propertiesTab->setHasSidecar(DMetadata::hasSidecar(url.toLocalFile()) ? i18nc("@info: item properties", "Yes")
290 : i18nc("@info: item properties", "No"));
291
292 // -- Photograph information ------------------------------------------
293
294 PhotoInfoContainer photoInfo = metaData->getPhotographInformation();
295
296 m_propertiesTab->setPhotoInfoDisable(photoInfo.isEmpty());
297 ItemPropertiesTab::shortenedMakeInfo(photoInfo.make);
298 ItemPropertiesTab::shortenedModelInfo(photoInfo.model);
299 m_propertiesTab->setPhotoMake(photoInfo.make.isEmpty() ? unavailable : photoInfo.make);
300 m_propertiesTab->setPhotoModel(photoInfo.model.isEmpty() ? unavailable : photoInfo.model);
301
302 if (photoInfo.dateTime.isValid())
303 {
304 str = QLocale().toString(photoInfo.dateTime, QLocale::ShortFormat);
305 m_propertiesTab->setPhotoDateTime(str);
306 }
307 else
308 {
309 m_propertiesTab->setPhotoDateTime(unavailable);
310 }
311
312 m_propertiesTab->setPhotoLens(photoInfo.lens.isEmpty() ? unavailable : photoInfo.lens);
313 m_propertiesTab->setPhotoAperture(photoInfo.aperture.isEmpty() ? unavailable : photoInfo.aperture);
314
315 if (photoInfo.focalLength35mm.isEmpty())
316 {
317 m_propertiesTab->setPhotoFocalLength(photoInfo.focalLength.isEmpty() ? unavailable : photoInfo.focalLength);
318 }
319 else
320 {
321 str = i18nc("@info: item properties", "%1 (%2)", photoInfo.focalLength, photoInfo.focalLength35mm);
322 m_propertiesTab->setPhotoFocalLength(str);
323 }
324
325 m_propertiesTab->setPhotoExposureTime(photoInfo.exposureTime.isEmpty() ? unavailable : photoInfo.exposureTime);
326 m_propertiesTab->setPhotoSensitivity(photoInfo.sensitivity.isEmpty() ? unavailable : i18nc("@info: item properties", "%1 ISO", photoInfo.sensitivity));
327
328 if (photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty())
329 {
330 m_propertiesTab->setPhotoExposureMode(unavailable);
331 }
332 else if (!photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty())
333 {
334 m_propertiesTab->setPhotoExposureMode(photoInfo.exposureMode);
335 }
336 else if (photoInfo.exposureMode.isEmpty() && !photoInfo.exposureProgram.isEmpty())
337 {
338 m_propertiesTab->setPhotoExposureMode(photoInfo.exposureProgram);
339 }
340 else
341 {
342 str = QString::fromUtf8("%1 / %2").arg(photoInfo.exposureMode).arg(photoInfo.exposureProgram);
343 m_propertiesTab->setPhotoExposureMode(str);
344 }
345
346 m_propertiesTab->setPhotoFlash(photoInfo.flash.isEmpty() ? unavailable : photoInfo.flash);
347 m_propertiesTab->setPhotoWhiteBalance(photoInfo.whiteBalance.isEmpty() ? unavailable : photoInfo.whiteBalance);
348
349 // -- Audio/Video information ------------------------------------------
350
351 VideoInfoContainer videoInfo = metaData->getVideoInformation();
352
353 m_propertiesTab->setVideoInfoDisable(videoInfo.isEmpty());
354
355 m_propertiesTab->setVideoAspectRatio(videoInfo.aspectRatio.isEmpty() ? unavailable : videoInfo.aspectRatio);
356 m_propertiesTab->setVideoDuration(videoInfo.duration.isEmpty() ? unavailable : videoInfo.duration);
357 m_propertiesTab->setVideoFrameRate(videoInfo.frameRate.isEmpty() ? unavailable : videoInfo.frameRate);
358 m_propertiesTab->setVideoVideoCodec(videoInfo.videoCodec.isEmpty() ? unavailable : videoInfo.videoCodec);
359 m_propertiesTab->setVideoAudioBitRate(videoInfo.audioBitRate.isEmpty() ? unavailable : videoInfo.audioBitRate);
360 m_propertiesTab->setVideoAudioChannelType(videoInfo.audioChannelType.isEmpty() ? unavailable : videoInfo.audioChannelType);
361 m_propertiesTab->setVideoAudioCodec(videoInfo.audioCodec.isEmpty() ? unavailable : videoInfo.audioCodec);
362
363 // -- Caption, ratings, tag information ---------------------
364
365 CaptionsMap captions = metaData->getItemComments();
366 QString caption;
367
368 if (captions.contains(QLatin1String("x-default")))
369 {
370 caption = captions.value(QLatin1String("x-default")).caption;
371 }
372 else if (!captions.isEmpty())
373 {
374 caption = captions.begin().value().caption;
375 }
376
377 m_propertiesTab->setCaption(caption);
378
379 m_propertiesTab->setRating(metaData->getItemRating());
380
381 QStringList tagPaths;
382 metaData->getItemTagsPath(tagPaths);
383 m_propertiesTab->setTags(tagPaths);
384 m_propertiesTab->showOrHideCaptionAndTags();
385 }
386
doLoadState()387 void ItemPropertiesSideBar::doLoadState()
388 {
389 Sidebar::doLoadState();
390
391 /// @todo m_propertiesTab should load its settings from our group
392
393 m_propertiesTab->setObjectName(QLatin1String("Image Properties SideBar Expander"));
394
395 KConfigGroup group = getConfigGroup();
396
397 m_propertiesTab->readSettings(group);
398
399 #ifdef HAVE_MARBLE
400
401 const KConfigGroup groupGPSTab = KConfigGroup(&group, entryName(QLatin1String("GPS Properties Tab")));
402 m_gpsTab->readSettings(groupGPSTab);
403
404 #endif // HAVE_MARBLE
405
406 const KConfigGroup groupColorTab = KConfigGroup(&group, entryName(QLatin1String("Color Properties Tab")));
407 m_colorTab->readSettings(groupColorTab);
408
409 const KConfigGroup groupMetadataTab = KConfigGroup(&group, entryName(QLatin1String("Metadata Properties Tab")));
410 m_metadataTab->readSettings(groupMetadataTab);
411 }
412
doSaveState()413 void ItemPropertiesSideBar::doSaveState()
414 {
415 Sidebar::doSaveState();
416
417 KConfigGroup group = getConfigGroup();
418
419 m_propertiesTab->writeSettings(group);
420
421 #ifdef HAVE_MARBLE
422
423 KConfigGroup groupGPSTab = KConfigGroup(&group, entryName(QLatin1String("GPS Properties Tab")));
424 m_gpsTab->writeSettings(groupGPSTab);
425
426 #endif // HAVE_MARBLE
427
428 KConfigGroup groupColorTab = KConfigGroup(&group, entryName(QLatin1String("Color Properties Tab")));
429 m_colorTab->writeSettings(groupColorTab);
430
431 KConfigGroup groupMetadataTab = KConfigGroup(&group, entryName(QLatin1String("Metadata Properties Tab")));
432 m_metadataTab->writeSettings(groupMetadataTab);
433 }
434
slotLoadMetadataFilters()435 void ItemPropertiesSideBar::slotLoadMetadataFilters()
436 {
437 m_metadataTab->loadFilters();
438 }
439
440 } // namespace Digikam
441