1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2006-10-18
7 * Description : EXIF date and time settings page.
8 *
9 * Copyright (C) 2006-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) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * ============================================================ */
22
23 #include "exifdatetime.h"
24
25 // Qt includes
26
27 #include <QCheckBox>
28 #include <QPushButton>
29 #include <QGridLayout>
30 #include <QApplication>
31 #include <QStyle>
32 #include <QSpinBox>
33 #include <QDateTimeEdit>
34
35 // KDE includes
36
37 #include <klocalizedstring.h>
38
39 // Local includes
40
41 #include "dlayoutbox.h"
42 #include "dexpanderbox.h"
43
44 namespace DigikamGenericMetadataEditPlugin
45 {
46
47 class Q_DECL_HIDDEN EXIFDateTime::Private
48 {
49 public:
50
Private()51 explicit Private()
52 {
53 dateCreatedSel = nullptr;
54 dateOriginalSel = nullptr;
55 dateDigitalizedSel = nullptr;
56 dateCreatedSubSecEdit = nullptr;
57 dateOriginalSubSecEdit = nullptr;
58 dateDigitalizedSubSecEdit = nullptr;
59
60 dateCreatedCheck = nullptr;
61 dateOriginalCheck = nullptr;
62 dateDigitalizedCheck = nullptr;
63 dateCreatedSubSecCheck = nullptr;
64 dateOriginalSubSecCheck = nullptr;
65 dateDigitalizedSubSecCheck = nullptr;
66 syncXMPDateCheck = nullptr;
67 syncIPTCDateCheck = nullptr;
68
69 setTodayCreatedBtn = nullptr;
70 setTodayOriginalBtn = nullptr;
71 setTodayDigitalizedBtn = nullptr;
72 }
73
74 QCheckBox* dateCreatedCheck;
75 QCheckBox* dateOriginalCheck;
76 QCheckBox* dateDigitalizedCheck;
77 QCheckBox* dateCreatedSubSecCheck;
78 QCheckBox* dateOriginalSubSecCheck;
79 QCheckBox* dateDigitalizedSubSecCheck;
80 QCheckBox* syncXMPDateCheck;
81 QCheckBox* syncIPTCDateCheck;
82
83 QPushButton* setTodayCreatedBtn;
84 QPushButton* setTodayOriginalBtn;
85 QPushButton* setTodayDigitalizedBtn;
86
87 QSpinBox* dateCreatedSubSecEdit;
88 QSpinBox* dateOriginalSubSecEdit;
89 QSpinBox* dateDigitalizedSubSecEdit;
90
91 QDateTimeEdit* dateCreatedSel;
92 QDateTimeEdit* dateOriginalSel;
93 QDateTimeEdit* dateDigitalizedSel;
94 };
95
EXIFDateTime(QWidget * const parent)96 EXIFDateTime::EXIFDateTime(QWidget* const parent)
97 : QWidget(parent),
98 d(new Private)
99 {
100 QGridLayout* const grid = new QGridLayout(this);
101
102 QString dateTimeFormat = QLocale().dateTimeFormat(QLocale::ShortFormat);
103
104 if (!dateTimeFormat.contains(QLatin1String("yyyy")))
105 {
106 dateTimeFormat.replace(QLatin1String("yy"),
107 QLatin1String("yyyy"));
108 }
109
110 if (!dateTimeFormat.contains(QLatin1String("ss")))
111 {
112 dateTimeFormat.replace(QLatin1String("mm"),
113 QLatin1String("mm:ss"));
114 }
115
116 // --------------------------------------------------------
117
118 d->dateCreatedCheck = new QCheckBox(i18n("Creation date and time"), this);
119 d->dateCreatedSubSecCheck = new QCheckBox(i18n("Creation sub-second"), this);
120
121 d->dateCreatedSel = new QDateTimeEdit(this);
122 d->dateCreatedSel->setDisplayFormat(dateTimeFormat);
123
124 d->dateCreatedSubSecEdit = new QSpinBox(this);
125 d->dateCreatedSubSecEdit->setMinimum(0);
126 d->dateCreatedSubSecEdit->setMaximum(999);
127 d->dateCreatedSubSecEdit->setSingleStep(1);
128 d->dateCreatedSubSecEdit->setValue(0);
129
130 d->syncXMPDateCheck = new QCheckBox(i18n("Sync XMP creation date"), this);
131 d->syncIPTCDateCheck = new QCheckBox(i18n("Sync IPTC creation date"), this);
132
133 d->setTodayCreatedBtn = new QPushButton();
134 d->setTodayCreatedBtn->setIcon(QIcon::fromTheme(QLatin1String("view-calendar")));
135 d->setTodayCreatedBtn->setWhatsThis(i18n("Set creation date to today"));
136
137 if (!DMetadata::supportXmp())
138 d->syncXMPDateCheck->setEnabled(false);
139
140 d->dateCreatedSel->setWhatsThis(i18n("Set here the date and time of image creation. "
141 "In this standard it is the date and time the file was changed."));
142 d->dateCreatedSubSecEdit->setWhatsThis(i18n("Set here the fractions of seconds for the date "
143 "and time of image creation."));
144
145 slotSetTodayCreated();
146
147 // --------------------------------------------------------
148
149 d->dateOriginalCheck = new QCheckBox(i18n("Original date and time"), this);
150 d->dateOriginalSubSecCheck = new QCheckBox(i18n("Original sub-second"), this);
151
152 d->dateOriginalSel = new QDateTimeEdit(this);
153 d->dateOriginalSel->setDisplayFormat(dateTimeFormat);
154
155 d->dateOriginalSubSecEdit = new QSpinBox(this);
156 d->dateOriginalSubSecEdit->setMinimum(0);
157 d->dateOriginalSubSecEdit->setMaximum(999);
158 d->dateOriginalSubSecEdit->setSingleStep(1);
159 d->dateOriginalSubSecEdit->setValue(0);
160
161 d->setTodayOriginalBtn = new QPushButton();
162 d->setTodayOriginalBtn->setIcon(QIcon::fromTheme(QLatin1String("view-calendar")));
163 d->setTodayOriginalBtn->setWhatsThis(i18n("Set original date to today"));
164
165 d->dateOriginalSel->setWhatsThis(i18n("Set here the date and time when the original image "
166 "data was generated. For a digital still camera the date and "
167 "time the picture was taken are recorded."));
168 d->dateOriginalSubSecEdit->setWhatsThis(i18n("Set here the fractions of seconds for the date "
169 "and time when the original image data was generated."));
170
171 slotSetTodayOriginal();
172
173 // --------------------------------------------------------
174
175 d->dateDigitalizedCheck = new QCheckBox(i18n("Digitization date and time"), this);
176 d->dateDigitalizedSubSecCheck = new QCheckBox(i18n("Digitization sub-second"), this);
177
178 d->dateDigitalizedSel = new QDateTimeEdit(this);
179 d->dateDigitalizedSel->setDisplayFormat(dateTimeFormat);
180
181 d->dateDigitalizedSubSecEdit = new QSpinBox(this);
182 d->dateDigitalizedSubSecEdit->setMinimum(0);
183 d->dateDigitalizedSubSecEdit->setMaximum(999);
184 d->dateDigitalizedSubSecEdit->setSingleStep(1);
185 d->dateDigitalizedSubSecEdit->setValue(0);
186
187 d->setTodayDigitalizedBtn = new QPushButton();
188 d->setTodayDigitalizedBtn->setIcon(QIcon::fromTheme(QLatin1String("view-calendar")));
189 d->setTodayDigitalizedBtn->setWhatsThis(i18n("Set digitization date to today"));
190
191 d->dateDigitalizedSel->setWhatsThis(i18n("Set here the date and time when the image was "
192 "stored as digital data. If, for example, an image was "
193 "captured by a digital still camera and at the same "
194 "time the file was recorded, then Original and Digitization "
195 "date and time will have the same contents."));
196 d->dateDigitalizedSubSecEdit->setWhatsThis(i18n("Set here the fractions of seconds for the date "
197 "and time when the image was stored as digital data."));
198
199 slotSetTodayDigitalized();
200
201 // --------------------------------------------------------
202
203 grid->addWidget(d->dateCreatedCheck, 0, 0, 1, 1);
204 grid->addWidget(d->dateCreatedSubSecCheck, 0, 1, 1, 3);
205 grid->addWidget(d->dateCreatedSel, 1, 0, 1, 1);
206 grid->addWidget(d->dateCreatedSubSecEdit, 1, 1, 1, 1);
207 grid->addWidget(d->setTodayCreatedBtn, 1, 3, 1, 1);
208 grid->addWidget(d->syncXMPDateCheck, 2, 0, 1, 4);
209 grid->addWidget(d->syncIPTCDateCheck, 3, 0, 1, 4);
210 grid->addWidget(new DLineWidget(Qt::Horizontal, this), 4, 0, 1, 4);
211 grid->addWidget(d->dateOriginalCheck, 5, 0, 1, 1);
212 grid->addWidget(d->dateOriginalSubSecCheck, 5, 1, 1, 3);
213 grid->addWidget(d->dateOriginalSel, 6, 0, 1, 1);
214 grid->addWidget(d->dateOriginalSubSecEdit, 6, 1, 1, 1);
215 grid->addWidget(d->setTodayOriginalBtn, 6, 3, 1, 1);
216 grid->addWidget(new DLineWidget(Qt::Horizontal, this), 7, 0, 1, 4);
217 grid->addWidget(d->dateDigitalizedCheck, 8, 0, 1, 1);
218 grid->addWidget(d->dateDigitalizedSubSecCheck, 8, 1, 1, 3);
219 grid->addWidget(d->dateDigitalizedSel, 9, 0, 1, 1);
220 grid->addWidget(d->dateDigitalizedSubSecEdit, 9, 1, 1, 1);
221 grid->addWidget(d->setTodayDigitalizedBtn, 9, 3, 1, 1);
222 grid->setColumnStretch(2, 10);
223 grid->setRowStretch(10, 10);
224 grid->setContentsMargins(QMargins());
225 grid->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
226
227 // --------------------------------------------------------
228
229 connect(d->dateCreatedCheck, SIGNAL(toggled(bool)),
230 d->dateCreatedSel, SLOT(setEnabled(bool)));
231
232 connect(d->dateOriginalCheck, SIGNAL(toggled(bool)),
233 d->dateOriginalSel, SLOT(setEnabled(bool)));
234
235 connect(d->dateDigitalizedCheck, SIGNAL(toggled(bool)),
236 d->dateDigitalizedSel, SLOT(setEnabled(bool)));
237
238 connect(d->dateCreatedSubSecCheck, SIGNAL(toggled(bool)),
239 d->dateCreatedSubSecEdit, SLOT(setEnabled(bool)));
240
241 connect(d->dateOriginalSubSecCheck, SIGNAL(toggled(bool)),
242 d->dateOriginalSubSecEdit, SLOT(setEnabled(bool)));
243
244 connect(d->dateDigitalizedSubSecCheck, SIGNAL(toggled(bool)),
245 d->dateDigitalizedSubSecEdit, SLOT(setEnabled(bool)));
246
247 connect(d->dateCreatedCheck, SIGNAL(toggled(bool)),
248 d->syncXMPDateCheck, SLOT(setEnabled(bool)));
249
250 connect(d->dateCreatedCheck, SIGNAL(toggled(bool)),
251 d->syncIPTCDateCheck, SLOT(setEnabled(bool)));
252
253 // --------------------------------------------------------
254
255 connect(d->dateCreatedCheck, SIGNAL(toggled(bool)),
256 this, SIGNAL(signalModified()));
257
258 connect(d->dateOriginalCheck, SIGNAL(toggled(bool)),
259 this, SIGNAL(signalModified()));
260
261 connect(d->dateDigitalizedCheck, SIGNAL(toggled(bool)),
262 this, SIGNAL(signalModified()));
263
264 connect(d->dateCreatedSubSecCheck, SIGNAL(toggled(bool)),
265 this, SIGNAL(signalModified()));
266
267 connect(d->dateOriginalSubSecCheck, SIGNAL(toggled(bool)),
268 this, SIGNAL(signalModified()));
269
270 connect(d->dateDigitalizedSubSecCheck, SIGNAL(toggled(bool)),
271 this, SIGNAL(signalModified()));
272
273 // --------------------------------------------------------
274
275 connect(d->dateCreatedSubSecEdit, SIGNAL(valueChanged(int)),
276 this, SIGNAL(signalModified()));
277
278 connect(d->dateOriginalSubSecEdit, SIGNAL(valueChanged(int)),
279 this, SIGNAL(signalModified()));
280
281 connect(d->dateDigitalizedSubSecEdit, SIGNAL(valueChanged(int)),
282 this, SIGNAL(signalModified()));
283
284 connect(d->dateCreatedSel, SIGNAL(dateTimeChanged(QDateTime)),
285 this, SIGNAL(signalModified()));
286
287 connect(d->dateOriginalSel, SIGNAL(dateTimeChanged(QDateTime)),
288 this, SIGNAL(signalModified()));
289
290 connect(d->dateDigitalizedSel, SIGNAL(dateTimeChanged(QDateTime)),
291 this, SIGNAL(signalModified()));
292
293 // --------------------------------------------------------
294
295 connect(d->setTodayCreatedBtn, SIGNAL(clicked()),
296 this, SLOT(slotSetTodayCreated()));
297
298 connect(d->setTodayOriginalBtn, SIGNAL(clicked()),
299 this, SLOT(slotSetTodayOriginal()));
300
301 connect(d->setTodayDigitalizedBtn, SIGNAL(clicked()),
302 this, SLOT(slotSetTodayDigitalized()));
303 }
304
~EXIFDateTime()305 EXIFDateTime::~EXIFDateTime()
306 {
307 delete d;
308 }
309
slotSetTodayCreated()310 void EXIFDateTime::slotSetTodayCreated()
311 {
312 d->dateCreatedSel->setDateTime(QDateTime::currentDateTime());
313 d->dateCreatedSubSecEdit->setValue(0);
314 }
315
slotSetTodayOriginal()316 void EXIFDateTime::slotSetTodayOriginal()
317 {
318 d->dateOriginalSel->setDateTime(QDateTime::currentDateTime());
319 d->dateOriginalSubSecEdit->setValue(0);
320 }
321
slotSetTodayDigitalized()322 void EXIFDateTime::slotSetTodayDigitalized()
323 {
324 d->dateDigitalizedSel->setDateTime(QDateTime::currentDateTime());
325 d->dateDigitalizedSubSecEdit->setValue(0);
326 }
327
syncXMPDateIsChecked() const328 bool EXIFDateTime::syncXMPDateIsChecked() const
329 {
330 return d->syncXMPDateCheck->isChecked();
331 }
332
syncIPTCDateIsChecked() const333 bool EXIFDateTime::syncIPTCDateIsChecked() const
334 {
335 return d->syncIPTCDateCheck->isChecked();
336 }
337
setCheckedSyncXMPDate(bool c)338 void EXIFDateTime::setCheckedSyncXMPDate(bool c)
339 {
340 d->syncXMPDateCheck->setChecked(c);
341 }
342
setCheckedSyncIPTCDate(bool c)343 void EXIFDateTime::setCheckedSyncIPTCDate(bool c)
344 {
345 d->syncIPTCDateCheck->setChecked(c);
346 }
347
getEXIFCreationDate() const348 QDateTime EXIFDateTime::getEXIFCreationDate() const
349 {
350 return d->dateCreatedSel->dateTime();
351 }
352
readMetadata(const DMetadata & meta)353 void EXIFDateTime::readMetadata(const DMetadata& meta)
354 {
355 blockSignals(true);
356
357 QDateTime datetime;
358 QString datetimeStr, data;
359
360 d->dateCreatedSel->setDateTime(QDateTime::currentDateTime());
361 d->dateCreatedCheck->setChecked(false);
362 datetimeStr = meta.getExifTagString("Exif.Image.DateTime", false);
363
364 if (!datetimeStr.isEmpty())
365 {
366 datetime = QDateTime::fromString(datetimeStr, Qt::ISODate);
367
368 if (datetime.isValid())
369 {
370 d->dateCreatedSel->setDateTime(datetime);
371 d->dateCreatedCheck->setChecked(true);
372 }
373 }
374
375 d->dateCreatedSel->setEnabled(d->dateCreatedCheck->isChecked());
376 d->syncXMPDateCheck->setEnabled(d->dateCreatedCheck->isChecked());
377 d->syncIPTCDateCheck->setEnabled(d->dateCreatedCheck->isChecked());
378
379 d->dateCreatedSubSecEdit->setValue(0);
380 d->dateCreatedSubSecCheck->setChecked(false);
381 data = meta.getExifTagString("Exif.Photo.SubSecTime", false);
382
383 if (!data.isNull())
384 {
385 bool ok = false;
386 int subsec = data.toInt(&ok);
387
388 if (ok)
389 {
390 d->dateCreatedSubSecEdit->setValue(subsec);
391 d->dateCreatedSubSecCheck->setChecked(true);
392 }
393 }
394
395 d->dateCreatedSubSecEdit->setEnabled(d->dateCreatedSubSecCheck->isChecked());
396
397 d->dateOriginalSel->setDateTime(QDateTime::currentDateTime());
398 d->dateOriginalCheck->setChecked(false);
399 datetimeStr = meta.getExifTagString("Exif.Photo.DateTimeOriginal", false);
400
401 if (!datetimeStr.isEmpty())
402 {
403 datetime = QDateTime::fromString(datetimeStr, Qt::ISODate);
404
405 if (datetime.isValid())
406 {
407 d->dateOriginalSel->setDateTime(datetime);
408 d->dateOriginalCheck->setChecked(true);
409 }
410 }
411
412 d->dateOriginalSel->setEnabled(d->dateOriginalCheck->isChecked());
413
414 d->dateOriginalSubSecEdit->setValue(0);
415 d->dateOriginalSubSecCheck->setChecked(false);
416 data = meta.getExifTagString("Exif.Photo.SubSecTimeOriginal", false);
417
418 if (!data.isNull())
419 {
420 bool ok = false;
421 int subsec = data.toInt(&ok);
422
423 if (ok)
424 {
425 d->dateOriginalSubSecEdit->setValue(subsec);
426 d->dateOriginalSubSecCheck->setChecked(true);
427 }
428 }
429
430 d->dateOriginalSubSecEdit->setEnabled(d->dateOriginalSubSecCheck->isChecked());
431
432 d->dateDigitalizedSel->setDateTime(QDateTime::currentDateTime());
433 d->dateDigitalizedCheck->setChecked(false);
434 datetimeStr = meta.getExifTagString("Exif.Photo.DateTimeDigitized", false);
435
436 if (!datetimeStr.isEmpty())
437 {
438 datetime = QDateTime::fromString(datetimeStr, Qt::ISODate);
439
440 if (datetime.isValid())
441 {
442 d->dateDigitalizedSel->setDateTime(datetime);
443 d->dateDigitalizedCheck->setChecked(true);
444 }
445 }
446
447 d->dateDigitalizedSel->setEnabled(d->dateDigitalizedCheck->isChecked());
448
449 d->dateDigitalizedSubSecEdit->setValue(0);
450 d->dateDigitalizedSubSecCheck->setChecked(false);
451 data = meta.getExifTagString("Exif.Photo.SubSecTimeDigitized", false);
452
453 if (!data.isNull())
454 {
455 bool ok = false;
456 int subsec = data.toInt(&ok);
457
458 if (ok)
459 {
460 d->dateDigitalizedSubSecEdit->setValue(subsec);
461 d->dateDigitalizedSubSecCheck->setChecked(true);
462 }
463 }
464
465 d->dateDigitalizedSubSecEdit->setEnabled(d->dateDigitalizedSubSecCheck->isChecked());
466
467 blockSignals(false);
468 }
469
applyMetadata(const DMetadata & meta)470 void EXIFDateTime::applyMetadata(const DMetadata& meta)
471 {
472 QString exifDateTimeFormat = QLatin1String("yyyy:MM:dd hh:mm:ss");
473 QString xmpDateTimeFormat = QLatin1String("yyyy-MM-ddThh:mm:ss");
474
475 if (d->dateCreatedCheck->isChecked())
476 {
477 meta.setExifTagString("Exif.Image.DateTime",
478 d->dateCreatedSel->dateTime().toString(exifDateTimeFormat));
479
480 if (meta.supportXmp() && d->syncXMPDateCheck->isChecked())
481 {
482 meta.setXmpTagString("Xmp.exif.DateTimeOriginal",
483 d->dateCreatedSel->dateTime().toString(xmpDateTimeFormat));
484 meta.setXmpTagString("Xmp.photoshop.DateCreated",
485 d->dateCreatedSel->dateTime().toString(xmpDateTimeFormat));
486 meta.setXmpTagString("Xmp.tiff.DateTime",
487 d->dateCreatedSel->dateTime().toString(xmpDateTimeFormat));
488 meta.setXmpTagString("Xmp.xmp.CreateDate",
489 d->dateCreatedSel->dateTime().toString(xmpDateTimeFormat));
490 meta.setXmpTagString("Xmp.xmp.MetadataDate",
491 d->dateCreatedSel->dateTime().toString(xmpDateTimeFormat));
492 meta.setXmpTagString("Xmp.xmp.ModifyDate",
493 d->dateCreatedSel->dateTime().toString(xmpDateTimeFormat));
494 }
495
496 if (syncIPTCDateIsChecked())
497 {
498 meta.setIptcTagString("Iptc.Application2.DateCreated",
499 d->dateCreatedSel->dateTime().date().toString(Qt::ISODate));
500 meta.setIptcTagString("Iptc.Application2.TimeCreated",
501 d->dateCreatedSel->dateTime().time().toString(Qt::ISODate));
502 }
503 }
504 else
505 meta.removeExifTag("Exif.Image.DateTime");
506
507 if (d->dateCreatedSubSecCheck->isChecked())
508 meta.setExifTagString("Exif.Photo.SubSecTime",
509 QString::number(d->dateCreatedSubSecEdit->value()));
510 else
511 meta.removeExifTag("Exif.Photo.SubSecTime");
512
513 if (d->dateOriginalCheck->isChecked())
514 meta.setExifTagString("Exif.Photo.DateTimeOriginal",
515 d->dateOriginalSel->dateTime().toString(exifDateTimeFormat));
516 else
517 meta.removeExifTag("Exif.Photo.DateTimeOriginal");
518
519 if (d->dateOriginalSubSecCheck->isChecked())
520 meta.setExifTagString("Exif.Photo.SubSecTimeOriginal",
521 QString::number(d->dateOriginalSubSecEdit->value()));
522 else
523 meta.removeExifTag("Exif.Photo.SubSecTimeOriginal");
524
525 if (d->dateDigitalizedCheck->isChecked())
526 meta.setExifTagString("Exif.Photo.DateTimeDigitized",
527 d->dateDigitalizedSel->dateTime().toString(exifDateTimeFormat));
528 else
529 meta.removeExifTag("Exif.Photo.DateTimeDigitized");
530
531 if (d->dateDigitalizedSubSecCheck->isChecked())
532 meta.setExifTagString("Exif.Photo.SubSecTimeDigitized",
533 QString::number(d->dateDigitalizedSubSecEdit->value()));
534 else
535 meta.removeExifTag("Exif.Photo.SubSecTimeDigitized");
536 }
537
538 } // namespace DigikamGenericMetadataEditPlugin
539