File indexing completed on 2025-03-09 03:57:07

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-08-08
0007  * Description : an option to provide date information to the parser
0008  *
0009  * SPDX-FileCopyrightText: 2009-2012 by Andi Clemens <andi dot clemens at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "dateoption.h"
0016 
0017 // Qt includes
0018 
0019 #include <QDateTime>
0020 #include <QPointer>
0021 #include <QTimer>
0022 #include <QValidator>
0023 #include <QRegularExpression>
0024 
0025 // KDE includes
0026 
0027 #include <klocalizedstring.h>
0028 
0029 // Local includes
0030 
0031 #include "digikam_debug.h"
0032 #include "ui_dateoptiondialogwidget.h"
0033 
0034 namespace Digikam
0035 {
0036 
0037 static QString getDateFormatLinkText()
0038 {
0039     const QString dateFormatLink      = QString::fromUtf8("<a href='https://doc.qt.io/qt-5.15/qdatetime.html#toString'>%1</a>");
0040     const QString dateFormatLinkDescr = i18nc("@info: date format settings", "format settings");
0041 
0042     return dateFormatLink.arg(dateFormatLinkDescr);
0043 }
0044 
0045 // --------------------------------------------------------
0046 
0047 DateFormat::DateFormat()
0048 {
0049     m_map.insert(Standard,      DateFormatDescriptor(i18nc("@item:inlistbox date format", "Standard"),        QLatin1String("yyyyMMddThhmmss")));
0050     m_map.insert(ISO,           DateFormatDescriptor(i18nc("@item:inlistbox date format", "ISO"),             Qt::ISODate));
0051     m_map.insert(FullText,      DateFormatDescriptor(i18nc("@item:inlistbox date format", "Text"),            Qt::TextDate));
0052     m_map.insert(UnixTimeStamp, DateFormatDescriptor(i18nc("@item:inlistbox date format", "Unix Time Stamp"), QVariant()));
0053     m_map.insert(Custom,        DateFormatDescriptor(i18nc("@item:inlistbox date format", "Custom"),          QVariant()));
0054 }
0055 
0056 DateFormat::Type DateFormat::type(const QString& identifier)
0057 {
0058     if (identifier.isEmpty())
0059     {
0060         return Standard;
0061     }
0062 
0063     for (int i = 0 ; i < m_map.size() ; ++i)
0064     {
0065         if (m_map.at(i).first == identifier)
0066         {
0067             return (Type)i;
0068         }
0069     }
0070 
0071     return Standard;
0072 }
0073 
0074 QString DateFormat::identifier(Type type)
0075 {
0076     return m_map.at((int)type).first;
0077 }
0078 
0079 QVariant DateFormat::format(Type type)
0080 {
0081     return m_map.at((int)type).second;
0082 }
0083 
0084 QVariant DateFormat::format(const QString& identifier)
0085 {
0086     if (identifier.isEmpty())
0087     {
0088         return m_map.at(Standard).second;
0089     }
0090 
0091     Q_FOREACH (const DateFormatDescriptor& desc, m_map)
0092     {
0093         if (desc.first == identifier)
0094         {   // cppcheck-suppress useStlAlgorithm
0095             return desc.second;
0096         }
0097     }
0098     return QVariant();
0099 }
0100 
0101 // --------------------------------------------------------
0102 
0103 DateOptionDialog::DateOptionDialog(Rule* parent)
0104     : RuleDialog(parent),
0105       ui        (new Ui::DateOptionDialogWidget)
0106 {
0107     QWidget* const mainWidget = new QWidget(this);
0108     ui->setupUi(mainWidget);
0109 
0110     // --------------------------------------------------------
0111 
0112     // fill the date source combobox
0113 
0114     ui->dateSourcePicker->addItem(i18nc("@item: Get date information from the image", "Image"),
0115                                   QVariant(FromImage));
0116 /*
0117     ui->dateSourcePicker->addItem(i18nc("Get date information from the current date", "Current Date"),
0118                                   QVariant(CurrentDateTime));
0119 */
0120     ui->dateSourcePicker->addItem(i18nc("@item: Set a fixed date", "Fixed Date"),
0121                                   QVariant(FixedDateTime));
0122 
0123     // fill the date format combobox
0124 
0125     DateFormat df;
0126 
0127     Q_FOREACH (const DateFormat::DateFormatDescriptor& desc, df.map())
0128     {
0129         ui->dateFormatPicker->addItem(desc.first);
0130     }
0131 
0132     // set the datePicker and timePicker to the current local datetime
0133 
0134     QDateTime currentDateTime = QDateTime::currentDateTime();
0135     ui->datePicker->setDate(currentDateTime.date());
0136     ui->timePicker->setTime(currentDateTime.time());
0137 
0138     ui->dateFormatLink->setOpenExternalLinks(true);
0139     ui->dateFormatLink->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
0140     ui->dateFormatLink->setText(getDateFormatLinkText());
0141 
0142     QRegularExpression validRegExp(QLatin1String("[^/]+"));
0143     QValidator* const validator = new QRegularExpressionValidator(validRegExp, this);
0144     ui->customFormatInput->setValidator(validator);
0145     ui->customFormatInput->setPlaceholderText(i18nc("@info", "Enter custom format"));
0146 
0147     // --------------------------------------------------------
0148 
0149     connect(ui->dateSourcePicker, SIGNAL(currentIndexChanged(int)),
0150             this, SLOT(slotDateSourceChanged(int)));
0151 
0152     connect(ui->dateFormatPicker, SIGNAL(currentIndexChanged(int)),
0153             this, SLOT(slotDateFormatChanged(int)));
0154 
0155     connect(ui->customFormatInput, SIGNAL(textChanged(QString)),
0156             this, SLOT(slotCustomFormatChanged(QString)));
0157 
0158     // --------------------------------------------------------
0159 
0160     ui->dateFormatPicker->setCurrentIndex(DateFormat::Standard);
0161     slotDateFormatChanged(ui->dateFormatPicker->currentIndex());
0162 
0163     // --------------------------------------------------------
0164 
0165     setSettingsWidget(mainWidget);
0166 }
0167 
0168 DateOptionDialog::~DateOptionDialog()
0169 {
0170     delete ui;
0171 }
0172 
0173 DateOptionDialog::DateSource DateOptionDialog::dateSource()
0174 {
0175     QVariant v = ui->dateSourcePicker->itemData(ui->dateSourcePicker->currentIndex());
0176     bool ok    = true;
0177     int choice = v.toInt(&ok);
0178 
0179     return (static_cast<DateSource>(choice));
0180 }
0181 
0182 QString DateOptionDialog::formattedDateTime(const QDateTime& date)
0183 {
0184     switch (ui->dateFormatPicker->currentIndex())
0185     {
0186         case DateFormat::Custom:
0187         {
0188             return (date.toString(ui->customFormatInput->text()));
0189         }
0190 
0191         case DateFormat::UnixTimeStamp:
0192         {
0193             return (QString::fromUtf8("%1").arg(date.toMSecsSinceEpoch()));
0194         }
0195 
0196         default:
0197         {
0198             break;
0199         }
0200     }
0201 
0202     DateFormat df;
0203     QVariant   v;
0204 
0205     v = df.format(static_cast<DateFormat::Type>(ui->dateFormatPicker->currentIndex()));
0206     QString result;
0207 
0208 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0209 
0210     if (v.typeId() == QVariant::String)
0211 
0212 #else
0213 
0214     if (v.type() == QVariant::String)
0215 
0216 #endif
0217 
0218     {
0219         result = date.toString(v.toString());
0220     }
0221     else
0222     {
0223         result = date.toString((Qt::DateFormat)v.toInt());
0224     }
0225 
0226     return result;
0227 }
0228 
0229 void DateOptionDialog::slotDateSourceChanged(int index)
0230 {
0231     Q_UNUSED(index)
0232     ui->fixedDateContainer->setEnabled(dateSource() == FixedDateTime);
0233 }
0234 
0235 void DateOptionDialog::slotDateFormatChanged(int index)
0236 {
0237     bool custom = (index == DateFormat::Custom);
0238     ui->customFormatInput->setEnabled(custom);
0239     ui->dateFormatLink->setEnabled(custom);
0240     ui->dateFormatLink->setVisible(custom);
0241 
0242     updateExampleLabel();
0243 }
0244 
0245 void DateOptionDialog::slotCustomFormatChanged(const QString&)
0246 {
0247     updateExampleLabel();
0248 }
0249 
0250 void DateOptionDialog::updateExampleLabel()
0251 {
0252     QString result = i18nc("@info", "example: ") + formattedDateTime(QDateTime::currentDateTime());
0253     ui->exampleLabel->setText(result);
0254 }
0255 
0256 // --------------------------------------------------------
0257 
0258 DateOption::DateOption()
0259     : Option(i18nc("@info", "Date && Time..."),
0260              i18nc("@info", "Add date and time information"),
0261              QLatin1String("view-calendar"))
0262 {
0263     addToken(QLatin1String("[date]"),            i18nc("@item", "Date and time (standard format)"));
0264     addToken(QLatin1String("[date:||key||]"),    i18nc("@item", "Date and time") + QLatin1String(" (||key|| = Standard|ISO|UnixTimeStamp|Text)"));
0265     addToken(QLatin1String("[date:||format||]"), i18nc("@item", "Date and time") + QLatin1String(" (") + getDateFormatLinkText() + QLatin1Char(')'));
0266 
0267     QRegularExpression reg(QLatin1String("\\[date(:(.*))?\\]"));
0268     reg.setPatternOptions(QRegularExpression::InvertedGreedinessOption);
0269     setRegExp(reg);
0270 }
0271 
0272 QString DateOption::parseOperation(ParseSettings& settings, const QRegularExpressionMatch& match)
0273 {
0274 
0275     QString token = match.captured(2);
0276 
0277     // search for quoted token parameters (indicates custom formatting)
0278 
0279     const int MIN_TOKEN_SIZE = 2;
0280 
0281     if ((token.size() > MIN_TOKEN_SIZE) &&
0282         (token.startsWith(QLatin1Char('"')) && token.endsWith(QLatin1Char('"'))))
0283     {
0284         token = token.remove(0, 1);
0285         token.chop(1);
0286     }
0287 
0288     // check if the datetime was already set in the parseSettings objects (most likely during the camera import)
0289 
0290     QDateTime dateTime;
0291 
0292     if (!(settings.creationTime.isNull()) && (settings.creationTime.isValid()))
0293     {
0294         dateTime = settings.creationTime;
0295     }
0296     else
0297     {
0298         // lets try to re-read the file information
0299 
0300         ItemInfo info = ItemInfo::fromUrl(settings.fileUrl);
0301 
0302         if (!info.isNull())
0303         {
0304             dateTime = info.dateTime();
0305         }
0306 
0307         if (dateTime.isNull() || !dateTime.isValid())
0308         {
0309             // still no date info, use Qt file information
0310 
0311             QFileInfo fileInfo(settings.fileUrl.toLocalFile());
0312             dateTime = fileInfo.birthTime();
0313         }
0314     }
0315 
0316     // do we have a valid date?
0317 
0318     if (dateTime.isNull())
0319     {
0320         return QString();
0321     }
0322 
0323     QString    result;
0324     DateFormat df;
0325     QVariant   v = df.format(token);
0326 
0327     if (v.isNull())
0328     {
0329         // we seem to use custom format settings or UnixTimeStamp here
0330 
0331         switch (df.type(token))
0332         {
0333             case DateFormat::UnixTimeStamp:
0334             {
0335                 result = QString::fromUtf8("%1").arg(dateTime.toMSecsSinceEpoch());
0336                 break;
0337             }
0338 
0339             default:
0340             {
0341                 result = dateTime.toString(token);
0342                 break;
0343             }
0344         }
0345     }
0346     else
0347     {
0348 
0349 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0350 
0351         if (v.typeId() == QVariant::String)
0352 
0353 #else
0354 
0355         if (v.type() == QVariant::String)
0356 
0357 #endif
0358 
0359         {
0360             result = dateTime.toString(v.toString());
0361         }
0362         else
0363         {
0364             result = dateTime.toString((Qt::DateFormat)v.toInt());
0365         }
0366     }
0367 
0368     return result;
0369 }
0370 
0371 void DateOption::slotTokenTriggered(const QString& token)
0372 {
0373     Q_UNUSED(token)
0374 
0375     QPointer<DateOptionDialog> dlg = new DateOptionDialog(this);
0376 
0377     QString dateString;
0378 
0379     if (dlg->exec() == QDialog::Accepted)
0380     {
0381         DateFormat df;
0382         int index = dlg->ui->dateFormatPicker->currentIndex();
0383 
0384         // use custom date format?
0385 
0386         if (dlg->dateSource() == DateOptionDialog::FixedDateTime)
0387         {
0388             QDateTime date;
0389             date.setDate(dlg->ui->datePicker->date());
0390             date.setTime(dlg->ui->timePicker->time());
0391 
0392             QVariant v = (index == DateFormat::Custom) ? dlg->ui->customFormatInput->text()
0393                                                        : df.format((DateFormat::Type)index);
0394 
0395             if (v.isNull())
0396             {
0397                 // we seem to use UnixTimeStamp here
0398 
0399                 switch (index)
0400                 {
0401                     case DateFormat::UnixTimeStamp:
0402                     {
0403                         dateString = QString::fromUtf8("%1").arg(date.toMSecsSinceEpoch());
0404                         break;
0405                     }
0406 
0407                     default:
0408                     {
0409                         break;
0410                     }
0411                 }
0412             }
0413             else
0414             {
0415 
0416 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0417 
0418                 if (v.typeId() == QVariant::String)
0419 
0420 #else
0421 
0422                 if (v.type() == QVariant::String)
0423 
0424 #endif
0425 
0426                 {
0427                     dateString = date.toString(v.toString());
0428                 }
0429                 else
0430                 {
0431                     dateString = date.toString((Qt::DateFormat)v.toInt());
0432                 }
0433             }
0434         }
0435 
0436         // use predefined keywords for date formatting
0437 
0438         else
0439         {
0440             QString tokenStr = QLatin1String("[date:%1]");
0441 
0442             switch (index)
0443             {
0444                 case DateFormat::Standard:
0445                 {
0446                     dateString = tokenStr.arg(QLatin1String(""));
0447                     dateString.remove(QLatin1Char(':'));
0448                     break;
0449                 }
0450 
0451                 case DateFormat::Custom:
0452                 {
0453                     dateString = tokenStr.arg(QString::fromUtf8("\"%1\"").arg(dlg->ui->customFormatInput->text()));
0454                     break;
0455                 }
0456 
0457                 default:
0458                 {
0459                     QString identifier = df.identifier((DateFormat::Type) index);
0460                     dateString         = tokenStr.arg(identifier);
0461                     break;
0462                 }
0463             }
0464         }
0465     }
0466 
0467     delete dlg;
0468 
0469     Q_EMIT signalTokenTriggered(dateString);
0470 }
0471 
0472 } // namespace Digikam
0473 
0474 #include "moc_dateoption.cpp"