1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2009-08-08
7  * Description : an option to provide date information to the parser
8  *
9  * Copyright (C) 2009-2012 by Andi Clemens <andi dot clemens 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 "dateoption.h"
25 
26 // Qt includes
27 
28 #include <QDateTime>
29 #include <QPointer>
30 #include <QTimer>
31 #include <QValidator>
32 
33 // KDE includes
34 
35 #include <klocalizedstring.h>
36 
37 // Local includes
38 
39 #include "digikam_debug.h"
40 #include "ui_dateoptiondialogwidget.h"
41 
42 namespace Digikam
43 {
44 
getDateFormatLinkText()45 static QString getDateFormatLinkText()
46 {
47     const QString dateFormatLink      = QString::fromUtf8("<a href='https://qt-project.org/doc/qt-5.0/qtcore/qdatetime.html#toString'>%1</a>");
48     const QString dateFormatLinkDescr = i18nc("@info: date format settings", "format settings");
49 
50     return dateFormatLink.arg(dateFormatLinkDescr);
51 }
52 
53 // --------------------------------------------------------
54 
DateFormat()55 DateFormat::DateFormat()
56 {
57     m_map.insert(Standard,      DateFormatDescriptor(i18nc("@item:inlistbox date format", "Standard"),        QLatin1String("yyyyMMddThhmmss")));
58     m_map.insert(ISO,           DateFormatDescriptor(i18nc("@item:inlistbox date format", "ISO"),             Qt::ISODate));
59     m_map.insert(FullText,      DateFormatDescriptor(i18nc("@item:inlistbox date format", "Text"),            Qt::TextDate));
60     m_map.insert(UnixTimeStamp, DateFormatDescriptor(i18nc("@item:inlistbox date format", "Unix Time Stamp"), QVariant()));
61     m_map.insert(Custom,        DateFormatDescriptor(i18nc("@item:inlistbox date format", "Custom"),          QVariant()));
62 }
63 
type(const QString & identifier)64 DateFormat::Type DateFormat::type(const QString& identifier)
65 {
66     if (identifier.isEmpty())
67     {
68         return Standard;
69     }
70 
71     for (int i = 0 ; i < m_map.size() ; ++i)
72     {
73         if (m_map.at(i).first == identifier)
74         {
75             return (Type)i;
76         }
77     }
78 
79     return Standard;
80 }
81 
identifier(Type type)82 QString DateFormat::identifier(Type type)
83 {
84     return m_map.at((int)type).first;
85 }
86 
format(Type type)87 QVariant DateFormat::format(Type type)
88 {
89     return m_map.at((int)type).second;
90 }
91 
format(const QString & identifier)92 QVariant DateFormat::format(const QString& identifier)
93 {
94     if (identifier.isEmpty())
95     {
96         return m_map.at(Standard).second;
97     }
98 
99     foreach (const DateFormatDescriptor& desc, m_map)
100     {
101         if (desc.first == identifier)
102         {
103             return desc.second;
104         }
105     }
106     return QVariant();
107 }
108 
109 // --------------------------------------------------------
110 
DateOptionDialog(Rule * parent)111 DateOptionDialog::DateOptionDialog(Rule* parent)
112     : RuleDialog(parent),
113       ui(new Ui::DateOptionDialogWidget)
114 {
115     QWidget* const mainWidget = new QWidget(this);
116     ui->setupUi(mainWidget);
117 
118     // --------------------------------------------------------
119 
120     // fill the date source combobox
121 
122     ui->dateSourcePicker->addItem(i18nc("@item: Get date information from the image", "Image"),
123                                   QVariant(FromImage));
124 /*
125     ui->dateSourcePicker->addItem(i18nc("Get date information from the current date", "Current Date"),
126                                   QVariant(CurrentDateTime));
127 */
128     ui->dateSourcePicker->addItem(i18nc("@item: Set a fixed date", "Fixed Date"),
129                                   QVariant(FixedDateTime));
130 
131     // fill the date format combobox
132 
133     DateFormat df;
134 
135     foreach (const DateFormat::DateFormatDescriptor& desc, df.map())
136     {
137         ui->dateFormatPicker->addItem(desc.first);
138     }
139 
140     // set the datePicker and timePicker to the current local datetime
141 
142     QDateTime currentDateTime = QDateTime::currentDateTime();
143     ui->datePicker->setDate(currentDateTime.date());
144     ui->timePicker->setTime(currentDateTime.time());
145 
146     ui->dateFormatLink->setOpenExternalLinks(true);
147     ui->dateFormatLink->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
148     ui->dateFormatLink->setText(getDateFormatLinkText());
149 
150     QRegExp validRegExp(QLatin1String("[^/]+"));
151     QValidator* const validator = new QRegExpValidator(validRegExp, this);
152     ui->customFormatInput->setValidator(validator);
153     ui->customFormatInput->setPlaceholderText(i18nc("@info", "Enter custom format"));
154 
155     // --------------------------------------------------------
156 
157     connect(ui->dateSourcePicker, SIGNAL(currentIndexChanged(int)),
158             this, SLOT(slotDateSourceChanged(int)));
159 
160     connect(ui->dateFormatPicker, SIGNAL(currentIndexChanged(int)),
161             this, SLOT(slotDateFormatChanged(int)));
162 
163     connect(ui->customFormatInput, SIGNAL(textChanged(QString)),
164             this, SLOT(slotCustomFormatChanged(QString)));
165 
166     // --------------------------------------------------------
167 
168     ui->dateFormatPicker->setCurrentIndex(DateFormat::Standard);
169     slotDateFormatChanged(ui->dateFormatPicker->currentIndex());
170 
171     // --------------------------------------------------------
172 
173     setSettingsWidget(mainWidget);
174 }
175 
~DateOptionDialog()176 DateOptionDialog::~DateOptionDialog()
177 {
178     delete ui;
179 }
180 
dateSource()181 DateOptionDialog::DateSource DateOptionDialog::dateSource()
182 {
183     QVariant v = ui->dateSourcePicker->itemData(ui->dateSourcePicker->currentIndex());
184     bool ok    = true;
185     int choice = v.toInt(&ok);
186 
187     return static_cast<DateSource>(choice);
188 }
189 
formattedDateTime(const QDateTime & date)190 QString DateOptionDialog::formattedDateTime(const QDateTime& date)
191 {
192     switch (ui->dateFormatPicker->currentIndex())
193     {
194         case DateFormat::Custom:
195             return date.toString(ui->customFormatInput->text());
196             break;
197 
198         case DateFormat::UnixTimeStamp:
199             return QString::fromUtf8("%1").arg(date.toMSecsSinceEpoch());
200             break;
201 
202         default:
203             break;
204     }
205 
206     DateFormat df;
207     QVariant   v;
208 
209     v = df.format(static_cast<DateFormat::Type>(ui->dateFormatPicker->currentIndex()));
210     QString result;
211 
212     if (v.type() == QVariant::String)
213     {
214         result = date.toString(v.toString());
215     }
216     else
217     {
218         result = date.toString((Qt::DateFormat)v.toInt());
219     }
220 
221     return result;
222 }
223 
slotDateSourceChanged(int index)224 void DateOptionDialog::slotDateSourceChanged(int index)
225 {
226     Q_UNUSED(index)
227     ui->fixedDateContainer->setEnabled(dateSource() == FixedDateTime);
228 }
229 
slotDateFormatChanged(int index)230 void DateOptionDialog::slotDateFormatChanged(int index)
231 {
232     bool custom = (index == DateFormat::Custom);
233     ui->customFormatInput->setEnabled(custom);
234     ui->dateFormatLink->setEnabled(custom);
235     ui->dateFormatLink->setVisible(custom);
236 
237     updateExampleLabel();
238 }
239 
slotCustomFormatChanged(const QString &)240 void DateOptionDialog::slotCustomFormatChanged(const QString&)
241 {
242     updateExampleLabel();
243 }
244 
updateExampleLabel()245 void DateOptionDialog::updateExampleLabel()
246 {
247     QString result = i18nc("@info", "example: ") + formattedDateTime(QDateTime::currentDateTime());
248     ui->exampleLabel->setText(result);
249 }
250 
251 // --------------------------------------------------------
252 
DateOption()253 DateOption::DateOption()
254     : Option(i18nc("@info", "Date && Time..."),
255              i18nc("@info", "Add date and time information"),
256              QLatin1String("view-calendar"))
257 {
258     addToken(QLatin1String("[date]"),            i18nc("@item", "Date and time (standard format)"));
259     addToken(QLatin1String("[date:||key||]"),    i18nc("@item", "Date and time") + QLatin1String(" (||key|| = Standard|ISO|UnixTimeStamp|Text)"));
260     addToken(QLatin1String("[date:||format||]"), i18nc("@item", "Date and time") + QLatin1String(" (") + getDateFormatLinkText() + QLatin1Char(')'));
261 
262     QRegExp reg(QLatin1String("\\[date(:(.*))?\\]"));
263     reg.setMinimal(true);
264     setRegExp(reg);
265 }
266 
parseOperation(ParseSettings & settings)267 QString DateOption::parseOperation(ParseSettings& settings)
268 {
269     const QRegExp& reg = regExp();
270 
271     QString token = reg.cap(2);
272 
273     // search for quoted token parameters (indicates custom formatting)
274 
275     const int MIN_TOKEN_SIZE = 2;
276 
277     if ((token.size() > MIN_TOKEN_SIZE) &&
278         (token.startsWith(QLatin1Char('"')) && token.endsWith(QLatin1Char('"'))))
279     {
280         token = token.remove(0, 1);
281         token.chop(1);
282     }
283 
284     // check if the datetime was already set in the parseSettings objects (most likely during the camera import)
285 
286     QDateTime dateTime;
287 
288     if (!(settings.creationTime.isNull()) && (settings.creationTime.isValid()))
289     {
290         dateTime = settings.creationTime;
291     }
292     else
293     {
294         // lets try to re-read the file information
295 
296         ItemInfo info = ItemInfo::fromUrl(settings.fileUrl);
297 
298         if (!info.isNull())
299         {
300             dateTime = info.dateTime();
301         }
302 
303         if (dateTime.isNull() || !dateTime.isValid())
304         {
305             // still no date info, use Qt file information
306 
307             QFileInfo fileInfo(settings.fileUrl.toLocalFile());
308 
309 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
310 
311             dateTime = fileInfo.birthTime();
312 
313 #else
314 
315             dateTime = fileInfo.created();
316 
317 #endif
318         }
319     }
320 
321     // do we have a valid date?
322 
323     if (dateTime.isNull())
324     {
325         return QString();
326     }
327 
328     QString    result;
329     DateFormat df;
330     QVariant   v = df.format(token);
331 
332     if (v.isNull())
333     {
334         // we seem to use custom format settings or UnixTimeStamp here
335 
336         switch (df.type(token))
337         {
338             case DateFormat::UnixTimeStamp:
339                 result = QString::fromUtf8("%1").arg(dateTime.toMSecsSinceEpoch());
340                 break;
341 
342             default:
343                 result = dateTime.toString(token);
344                 break;
345         }
346     }
347     else
348     {
349         if (v.type() == QVariant::String)
350         {
351             result = dateTime.toString(v.toString());
352         }
353         else
354         {
355             result = dateTime.toString((Qt::DateFormat)v.toInt());
356         }
357     }
358 
359     return result;
360 }
361 
slotTokenTriggered(const QString & token)362 void DateOption::slotTokenTriggered(const QString& token)
363 {
364     Q_UNUSED(token)
365 
366     QPointer<DateOptionDialog> dlg = new DateOptionDialog(this);
367 
368     QString dateString;
369 
370     if (dlg->exec() == QDialog::Accepted)
371     {
372         DateFormat df;
373         int index = dlg->ui->dateFormatPicker->currentIndex();
374 
375         // use custom date format?
376 
377         if (dlg->dateSource() == DateOptionDialog::FixedDateTime)
378         {
379             QDateTime date;
380             date.setDate(dlg->ui->datePicker->date());
381             date.setTime(dlg->ui->timePicker->time());
382 
383             QVariant v = (index == DateFormat::Custom) ? dlg->ui->customFormatInput->text()
384                                                        : df.format((DateFormat::Type)index);
385 
386             if (v.isNull())
387             {
388                 // we seem to use UnixTimeStamp here
389 
390                 switch (index)
391                 {
392                     case DateFormat::UnixTimeStamp:
393                         dateString = QString::fromUtf8("%1").arg(date.toMSecsSinceEpoch());
394                         break;
395 
396                     default:
397                         break;
398                 }
399             }
400             else
401             {
402                 if (v.type() == QVariant::String)
403                 {
404                     dateString = date.toString(v.toString());
405                 }
406                 else
407                 {
408                     dateString = date.toString((Qt::DateFormat)v.toInt());
409                 }
410             }
411         }
412 
413         // use predefined keywords for date formatting
414 
415         else
416         {
417             QString tokenStr = QLatin1String("[date:%1]");
418 
419             switch (index)
420             {
421                 case DateFormat::Standard:
422                     dateString = tokenStr.arg(QLatin1String(""));
423                     dateString.remove(QLatin1Char(':'));
424                     break;
425 
426                 case DateFormat::Custom:
427                     dateString = tokenStr.arg(QString::fromUtf8("\"%1\"").arg(dlg->ui->customFormatInput->text()));
428                     break;
429 
430                 default:
431                     QString identifier = df.identifier((DateFormat::Type) index);
432                     dateString         = tokenStr.arg(identifier);
433                     break;
434             }
435         }
436     }
437 
438     delete dlg;
439 
440     emit signalTokenTriggered(dateString);
441 }
442 
443 } // namespace Digikam
444