File indexing completed on 2025-01-19 03:51:26

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2004-05-16
0007  * Description : dialog to set time stamp of picture files.
0008  *
0009  * SPDX-FileCopyrightText: 2012      by Smit Mehta <smit dot meh at gmail dot com>
0010  * SPDX-FileCopyrightText: 2003-2005 by Jesper Pedersen <blackie at kde dot org>
0011  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  * SPDX-FileCopyrightText: 2018-2021 by Maik Qualmann <metzpinguin at gmail dot com>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "timeadjustdialog.h"
0019 
0020 // Qt includes
0021 
0022 #include <QButtonGroup>
0023 #include <QCheckBox>
0024 #include <QCloseEvent>
0025 #include <QComboBox>
0026 #include <QCursor>
0027 #include <QFile>
0028 #include <QFileInfo>
0029 #include <QGridLayout>
0030 #include <QGroupBox>
0031 #include <QLabel>
0032 #include <QMap>
0033 #include <QTimer>
0034 #include <QPointer>
0035 #include <QPushButton>
0036 #include <QRadioButton>
0037 #include <QSpinBox>
0038 #include <QDateTime>
0039 #include <QTimeEdit>
0040 #include <QToolButton>
0041 #include <QVBoxLayout>
0042 #include <QApplication>
0043 #include <QMessageBox>
0044 #include <QWindow>
0045 #include <QScopedPointer>
0046 
0047 // KDE includes
0048 
0049 #include <kconfiggroup.h>
0050 #include <ksharedconfig.h>
0051 #include <klocalizedstring.h>
0052 
0053 // Local includes
0054 
0055 #include "dmetadata.h"
0056 #include "dprogresswdg.h"
0057 #include "digikam_debug.h"
0058 #include "dxmlguiwindow.h"
0059 #include "timeadjustlist.h"
0060 #include "digikam_version.h"
0061 #include "timeadjustthread.h"
0062 
0063 namespace DigikamGenericTimeAdjustPlugin
0064 {
0065 
0066 class Q_DECL_HIDDEN TimeAdjustDialog::Private
0067 {
0068 
0069 public:
0070 
0071     explicit Private()
0072       : settingsView(nullptr),
0073         previewTimer(nullptr),
0074         updateTimer (nullptr),
0075         exitAfterOk (false),
0076         progressBar (nullptr),
0077         listView    (nullptr),
0078         thread      (nullptr),
0079         iface       (nullptr)
0080     {
0081     }
0082 
0083     TimeAdjustSettings*   settingsView;
0084 
0085     QMap<QUrl, int>       itemsUsedMap;           // Map of item urls and index.
0086 
0087     QTimer*               previewTimer;
0088     QTimer*               updateTimer;
0089 
0090     bool                  exitAfterOk;
0091 
0092     DProgressWdg*         progressBar;
0093     TimeAdjustList*       listView;
0094 
0095     TimeAdjustThread*     thread;
0096 
0097     DInfoInterface*       iface;
0098 };
0099 
0100 TimeAdjustDialog::TimeAdjustDialog(QWidget* const parent, DInfoInterface* const iface)
0101     : DPluginDialog(parent, QLatin1String("Time Adjust Dialog")),
0102       d            (new Private)
0103 {
0104     setWindowTitle(i18nc("@title:window", "Adjust Time & Date"));
0105     setMinimumSize(900, 500);
0106     setModal(true);
0107 
0108     d->iface = iface;
0109 
0110     m_buttons->addButton(QDialogButtonBox::Yes);
0111     m_buttons->addButton(QDialogButtonBox::Apply);
0112     m_buttons->addButton(QDialogButtonBox::Close);
0113     m_buttons->button(QDialogButtonBox::Yes)->setText(i18n("OK"));
0114     m_buttons->button(QDialogButtonBox::Yes)->setAutoDefault(false);
0115     m_buttons->button(QDialogButtonBox::Apply)->setAutoDefault(false);
0116     m_buttons->button(QDialogButtonBox::Close)->setAutoDefault(false);
0117 
0118     m_buttons->button(QDialogButtonBox::Yes)->setToolTip(i18nc("@info:tooltip",
0119                                                                "Write the corrected date and time for each image and close dialog"));
0120     m_buttons->button(QDialogButtonBox::Apply)->setToolTip(i18nc("@info:tooltip",
0121                                                                  "Write the corrected date and time for each image"));
0122 
0123 
0124     QWidget* const mainWidget = new QWidget(this);
0125     QVBoxLayout* const vbx    = new QVBoxLayout(this);
0126     vbx->addWidget(mainWidget);
0127     vbx->addWidget(m_buttons);
0128     setLayout(vbx);
0129 
0130     QGridLayout* const mainLayout = new QGridLayout(mainWidget);
0131     d->listView                   = new TimeAdjustList(mainWidget);
0132     d->settingsView               = new TimeAdjustSettings(mainWidget, true);
0133     d->progressBar                = new DProgressWdg(mainWidget);
0134     d->progressBar->reset();
0135     d->progressBar->hide();
0136 
0137     // ----------------------------------------------------------------------------
0138 
0139     mainLayout->addWidget(d->listView,     0, 0, 2, 1);
0140     mainLayout->addWidget(d->settingsView, 0, 1, 1, 1);
0141     mainLayout->addWidget(d->progressBar,  1, 1, 1, 1);
0142     mainLayout->setColumnStretch(0, 10);
0143     mainLayout->setRowStretch(0, 10);
0144     mainLayout->setContentsMargins(QMargins());
0145 
0146     // ----------------------------------------------------------------------------
0147 
0148     d->previewTimer = new QTimer(this);
0149     d->previewTimer->setSingleShot(true);
0150     d->previewTimer->setInterval(500);
0151 
0152     d->updateTimer = new QTimer(this);
0153     d->updateTimer->setSingleShot(true);
0154     d->updateTimer->setInterval(250);
0155 
0156     connect(d->previewTimer, SIGNAL(timeout()),
0157             this, SLOT(slotPreviewTimer()));
0158 
0159     connect(d->updateTimer, SIGNAL(timeout()),
0160             this, SLOT(slotUpdateTimer()));
0161 
0162     // -- Thread Slots/Signals ----------------------------------------------
0163 
0164     d->thread = new TimeAdjustThread(this, d->iface);
0165 
0166     connect(d->thread, SIGNAL(signalProcessEnded(QUrl,QDateTime,QDateTime,int)),
0167             this, SLOT(slotProcessEnded(QUrl,QDateTime,QDateTime,int)));
0168 
0169     connect(d->thread, SIGNAL(signalPreviewReady(QUrl,QDateTime,QDateTime)),
0170             this, SLOT(slotPreviewReady(QUrl,QDateTime,QDateTime)));
0171 
0172     connect(d->thread, SIGNAL(signalDateTimeForUrl(QUrl,QDateTime,bool)),
0173             d->iface, SLOT(slotDateTimeForUrl(QUrl,QDateTime,bool)));
0174 
0175     connect(d->progressBar, SIGNAL(signalProgressCanceled()),
0176             this, SLOT(slotCancelThread()));
0177 
0178     connect(d->thread, SIGNAL(signalProcessStarted(QUrl)),
0179             this, SLOT(slotProcessStarted(QUrl)));
0180 
0181     connect(d->thread, SIGNAL(finished()),
0182             this, SLOT(slotThreadFinished()));
0183 
0184     // -- Dialog Slots/Signals -----------------------------------------------
0185 
0186     connect(m_buttons->button(QDialogButtonBox::Close), SIGNAL(clicked()),
0187             this, SLOT(slotCancelThread()));
0188 
0189     connect(m_buttons->button(QDialogButtonBox::Apply), SIGNAL(clicked()),
0190             this, SLOT(slotUpdateTimestamps()));
0191 
0192     connect(m_buttons->button(QDialogButtonBox::Yes), SIGNAL(clicked()),
0193             this, SLOT(slotOkExitTimestamps()));
0194 
0195     connect(d->settingsView, SIGNAL(signalSettingsChangedTool()),
0196             this, SLOT(slotPreviewTimestamps()));
0197 
0198     connect(d->settingsView, SIGNAL(signalSettingsChanged()),
0199             this, SLOT(slotPreviewTimestamps()));
0200 
0201     connect(this, SIGNAL(finished(int)),
0202             this, SLOT(slotDialogFinished()));
0203 
0204     connect(d->listView->listView(), &DItemsListView::itemSelectionChanged,
0205             this, [this]()
0206         {
0207             d->settingsView->setCurrentItemUrl(d->listView->getCurrentUrl());
0208         }
0209     );
0210 
0211     // -----------------------------------------------------------------------
0212 
0213     setBusy(false);
0214     readSettings();
0215 
0216     int index = 0;
0217 
0218     d->listView->setIface(d->iface);
0219     d->listView->loadImagesFromCurrentSelection();
0220 
0221     Q_FOREACH (const QUrl& url, d->listView->imageUrls())
0222     {
0223         d->itemsUsedMap.insert(url, index);
0224         ++index;
0225     }
0226 
0227     slotPreviewTimestamps();
0228 }
0229 
0230 TimeAdjustDialog::~TimeAdjustDialog()
0231 {
0232     if (d->thread->isRunning())
0233     {
0234         d->thread->cancel();
0235         d->thread->wait();
0236     }
0237 
0238     delete d;
0239 }
0240 
0241 void TimeAdjustDialog::closeEvent(QCloseEvent* e)
0242 {
0243     if (d->thread->isRunning())
0244     {
0245         d->thread->cancel();
0246         d->thread->wait();
0247 
0248         e->ignore();
0249 
0250         return;
0251     }
0252 
0253     slotDialogFinished();
0254 
0255     e->accept();
0256 }
0257 
0258 void TimeAdjustDialog::slotDialogFinished()
0259 {
0260     saveSettings();
0261 }
0262 
0263 void TimeAdjustDialog::readSettings()
0264 {
0265     TimeAdjustContainer prm;
0266 
0267     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0268     KConfigGroup group        = config->group(QLatin1String("Time Adjust Settings"));
0269 
0270     prm.customDate     = group.readEntry(QLatin1String("Custom Date"),                   QDateTime());
0271     prm.customDate.setTimeSpec(Qt::UTC);
0272     prm.customTime     = group.readEntry(QLatin1String("Custom Time"),                   QDateTime());
0273     prm.customTime.setTimeSpec(Qt::UTC);
0274     prm.adjustmentType = group.readEntry(QLatin1String("Adjustment Type"),               0);
0275     prm.adjustmentDays = group.readEntry(QLatin1String("Adjustment Days"),               0);
0276     prm.adjustmentTime = group.readEntry(QLatin1String("Adjustment Time"),               QDateTime());
0277     prm.adjustmentTime.setTimeSpec(Qt::UTC);
0278 
0279     prm.updIfAvailable = group.readEntry(QLatin1String("Update Only If Available Time"), true);
0280     prm.updFileModDate = group.readEntry(QLatin1String("Update File Modification Time"), true);
0281     prm.updEXIFModDate = group.readEntry(QLatin1String("Update EXIF Modification Time"), true);
0282     prm.updEXIFOriDate = group.readEntry(QLatin1String("Update EXIF Original Time"),     true);
0283     prm.updEXIFDigDate = group.readEntry(QLatin1String("Update EXIF Digitization Time"), true);
0284     prm.updEXIFThmDate = group.readEntry(QLatin1String("Update EXIF Thumbnail Time"),    true);
0285     prm.updIPTCDate    = group.readEntry(QLatin1String("Update IPTC Time"),              true);
0286     prm.updXMPVideo    = group.readEntry(QLatin1String("Update XMP Video Time"),         true);
0287     prm.updXMPDate     = group.readEntry(QLatin1String("Update XMP Creation Time"),      true);
0288 
0289     prm.dateSource     = group.readEntry(QLatin1String("Use Timestamp Type"),            0);
0290     prm.metadataSource = group.readEntry(QLatin1String("Meta Timestamp Type"),           0);
0291     prm.fileDateSource = group.readEntry(QLatin1String("File Timestamp Type"),           0);
0292 
0293     d->settingsView->setSettings(prm);
0294 }
0295 
0296 void TimeAdjustDialog::saveSettings()
0297 {
0298     TimeAdjustContainer prm   = d->settingsView->settings();
0299 
0300     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0301     KConfigGroup group = config->group(QLatin1String("Time Adjust Settings"));
0302 
0303     group.writeEntry(QLatin1String("Custom Date"),                   prm.customDate);
0304     group.writeEntry(QLatin1String("Custom Time"),                   prm.customTime);
0305 
0306     group.writeEntry(QLatin1String("Adjustment Type"),               prm.adjustmentType);
0307     group.writeEntry(QLatin1String("Adjustment Days"),               prm.adjustmentDays);
0308     group.writeEntry(QLatin1String("Adjustment Time"),               prm.adjustmentTime);
0309 
0310     group.writeEntry(QLatin1String("Update Only If Available Time"), prm.updIfAvailable);
0311     group.writeEntry(QLatin1String("Update File Modification Time"), prm.updFileModDate);
0312     group.writeEntry(QLatin1String("Update EXIF Modification Time"), prm.updEXIFModDate);
0313     group.writeEntry(QLatin1String("Update EXIF Original Time"),     prm.updEXIFOriDate);
0314     group.writeEntry(QLatin1String("Update EXIF Digitization Time"), prm.updEXIFDigDate);
0315     group.writeEntry(QLatin1String("Update EXIF Thumbnail Time"),    prm.updEXIFThmDate);
0316     group.writeEntry(QLatin1String("Update IPTC Time"),              prm.updIPTCDate);
0317     group.writeEntry(QLatin1String("Update XMP Video Time"),         prm.updXMPVideo);
0318     group.writeEntry(QLatin1String("Update XMP Creation Time"),      prm.updXMPDate);
0319 
0320     group.writeEntry(QLatin1String("Use Timestamp Type"),            prm.dateSource);
0321     group.writeEntry(QLatin1String("Meta Timestamp Type"),           prm.metadataSource);
0322     group.writeEntry(QLatin1String("File Timestamp Type"),           prm.fileDateSource);
0323 }
0324 
0325 void TimeAdjustDialog::slotPreviewTimestamps()
0326 {
0327     if (d->thread->isRunning())
0328     {
0329         d->thread->cancel();
0330         d->thread->wait();
0331     }
0332 
0333     d->previewTimer->start();
0334 }
0335 
0336 void TimeAdjustDialog::slotUpdateTimestamps()
0337 {
0338     if (d->thread->isRunning())
0339     {
0340         d->thread->cancel();
0341         d->thread->wait();
0342     }
0343 
0344     d->updateTimer->start();
0345 }
0346 
0347 void TimeAdjustDialog::slotOkExitTimestamps()
0348 {
0349     d->exitAfterOk = true;
0350 
0351    slotUpdateTimestamps();
0352 }
0353 
0354 void TimeAdjustDialog::slotPreviewTimer()
0355 {
0356     d->listView->setWaitStatus();
0357 
0358     d->thread->setSettings(d->settingsView->settings());
0359     d->thread->setPreviewDates(d->itemsUsedMap);
0360     d->thread->start();
0361 }
0362 
0363 void TimeAdjustDialog::slotUpdateTimer()
0364 {
0365     d->listView->setWaitStatus();
0366 
0367     d->progressBar->show();
0368     d->progressBar->setMaximum(d->itemsUsedMap.keys().size());
0369     d->progressBar->progressScheduled(i18nc("@info", "Adjust Time and Date"), true, true);
0370     d->progressBar->progressThumbnailChanged(QIcon::fromTheme(QLatin1String("appointment-new")).pixmap(22, 22));
0371 
0372     d->thread->setSettings(d->settingsView->settings());
0373     d->thread->setUpdatedDates(d->itemsUsedMap);
0374     d->thread->start();
0375 
0376     setBusy(true);
0377 }
0378 
0379 void TimeAdjustDialog::slotCancelThread()
0380 {
0381     if (d->thread->isRunning())
0382     {
0383         d->thread->cancel();
0384         d->thread->wait();
0385     }
0386 
0387     d->exitAfterOk = false;
0388 
0389     if (m_buttons->button(QDialogButtonBox::Yes)->isEnabled())
0390     {
0391         accept();
0392     }
0393 }
0394 
0395 void TimeAdjustDialog::setBusy(bool busy)
0396 {
0397     if (busy)
0398     {
0399         m_buttons->button(QDialogButtonBox::Close)->setText(i18nc("@action", "Cancel"));
0400         m_buttons->button(QDialogButtonBox::Close)->setIcon(QIcon::fromTheme(QLatin1String("dialog-cancel")));
0401         m_buttons->button(QDialogButtonBox::Close)->setToolTip(i18nc("@info", "Cancel current operation"));
0402     }
0403     else
0404     {
0405         m_buttons->button(QDialogButtonBox::Close)->setText(i18nc("@action", "Close"));
0406         m_buttons->button(QDialogButtonBox::Close)->setIcon(QIcon::fromTheme(QLatin1String("window-close")));
0407         m_buttons->button(QDialogButtonBox::Close)->setToolTip(i18nc("@info", "Close window"));
0408     }
0409 
0410     m_buttons->button(QDialogButtonBox::Apply)->setEnabled(!busy);
0411     m_buttons->button(QDialogButtonBox::Yes)->setEnabled(!busy);
0412     d->settingsView->setEnabled(!busy);
0413 }
0414 
0415 void TimeAdjustDialog::slotProcessStarted(const QUrl& url)
0416 {
0417     d->listView->processing(url);
0418 }
0419 
0420 void TimeAdjustDialog::slotPreviewReady(const QUrl& url,
0421                                         const QDateTime& org,
0422                                         const QDateTime& adj)
0423 {
0424     d->listView->setStatus(url, org, adj);
0425 }
0426 
0427 void TimeAdjustDialog::slotProcessEnded(const QUrl& url,
0428                                         const QDateTime& org,
0429                                         const QDateTime& adj, int status)
0430 {
0431     d->listView->processed(url, (status == TimeAdjustList::NOPROCESS_ERROR));
0432     d->listView->setStatus(url, org, adj, status);
0433 
0434     d->progressBar->setValue(d->progressBar->value() + 1);
0435 }
0436 
0437 void TimeAdjustDialog::slotThreadFinished()
0438 {
0439     setBusy(false);
0440     d->progressBar->hide();
0441     d->progressBar->progressCompleted();
0442 
0443     if (d->exitAfterOk)
0444     {
0445         accept();
0446     }
0447 }
0448 
0449 } // namespace DigikamGenericTimeAdjustPlugin
0450 
0451 #include "moc_timeadjustdialog.cpp"