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