1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2003-03-09
7  * Description : Album properties dialog.
8  *
9  * Copyright (C) 2003-2004 by Renchi Raju <renchi dot raju at gmail dot com>
10  * Copyright (C) 2005      by Tom Albers <tomalbers at kde dot nl>
11  * Copyright (C) 2006-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
12  *
13  * This program is free software; you can redistribute it
14  * and/or modify it under the terms of the GNU General
15  * Public License as published by the Free Software Foundation;
16  * either version 2, or (at your option)
17  * any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * ============================================================ */
25 
26 #include "albumpropsedit.h"
27 
28 // Qt includes
29 
30 #include <QCheckBox>
31 #include <QDateTime>
32 #include <QGridLayout>
33 #include <QLabel>
34 #include <QPointer>
35 #include <QRegExp>
36 #include <QValidator>
37 #include <QApplication>
38 #include <QStyle>
39 #include <QComboBox>
40 #include <QLineEdit>
41 #include <QStandardPaths>
42 #include <QMessageBox>
43 #include <QDialogButtonBox>
44 #include <QVBoxLayout>
45 #include <QPushButton>
46 #include <QPlainTextEdit>
47 
48 // KDE includes
49 
50 #include <klocalizedstring.h>
51 
52 // Local includes
53 
54 #include "dlayoutbox.h"
55 #include "coredb.h"
56 #include "album.h"
57 #include "albummanager.h"
58 #include "applicationsettings.h"
59 #include "collectionmanager.h"
60 #include "coredbaccess.h"
61 #include "dxmlguiwindow.h"
62 #include "dexpanderbox.h"
63 #include "ddatepicker.h"
64 
65 namespace Digikam
66 {
67 
68 class Q_DECL_HIDDEN AlbumDatePicker : public DDatePicker
69 {
70     Q_OBJECT
71 
72 public:
73 
AlbumDatePicker(QWidget * const widget)74     explicit AlbumDatePicker(QWidget* const widget)
75         : DDatePicker(widget)
76     {
77     }
78 
~AlbumDatePicker()79     ~AlbumDatePicker() override
80     {
81     }
82 
dateLineEnterPressed()83     void dateLineEnterPressed()
84     {
85         lineEnterPressed();
86     }
87 };
88 
89 // --------------------------------------------------------------------------------
90 
91 class Q_DECL_HIDDEN AlbumPropsEdit::Private
92 {
93 
94 public:
95 
Private()96     explicit Private()
97       : buttons         (nullptr),
98         topLabel        (nullptr),
99         categoryCombo   (nullptr),
100         parentCombo     (nullptr),
101         titleEdit       (nullptr),
102         commentsEdit    (nullptr),
103         datePicker      (nullptr),
104         album           (nullptr)
105     {
106     }
107 
108     QDialogButtonBox* buttons;
109 
110     QLabel*           topLabel;
111     QComboBox*        categoryCombo;
112     QComboBox*        parentCombo;
113     QLineEdit*        titleEdit;
114     QPlainTextEdit*   commentsEdit;
115 
116     AlbumDatePicker*  datePicker;
117 
118     PAlbum*           album;
119 };
120 
AlbumPropsEdit(PAlbum * const album,bool create)121 AlbumPropsEdit::AlbumPropsEdit(PAlbum* const album, bool create)
122     : QDialog(nullptr),
123       d      (new Private)
124 {
125     setModal(true);
126     setWindowTitle(create ? i18nc("@title: album properties", "New Album") : i18nc("@title: album properties", "Edit Album"));
127 
128     d->buttons          = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
129     d->buttons->button(QDialogButtonBox::Ok)->setDefault(true);
130 
131     d->album            = album;
132     QWidget* const page = new QWidget(this);
133     QLabel* const logo  = new QLabel(page);
134 
135     logo->setPixmap(QIcon::fromTheme(QLatin1String("digikam")).pixmap(QSize(48,48)));
136 
137     d->topLabel         = new QLabel(page);
138     d->topLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
139     d->topLabel->setWordWrap(false);
140 
141     if (create)
142     {
143         slotNewAlbumTextChanged(0);
144     }
145     else
146     {
147         d->topLabel->setText(i18nc("@label: album properties", "\"%1\"\nAlbum Properties", album->title()));
148     }
149 
150     // --------------------------------------------------------
151 
152     DLineWidget* const topLine        = new DLineWidget(Qt::Horizontal);
153 
154     QLabel* const titleLabel          = new QLabel(page);
155     titleLabel->setText(i18nc("@label: album properties", "&Title:"));
156 
157     d->titleEdit                      = new QLineEdit(page);
158     d->titleEdit->setClearButtonEnabled(true);
159     titleLabel->setBuddy(d->titleEdit);
160 
161     QRegExp titleRx(QLatin1String("[^/:]+"));
162     QValidator* const titleValidator  = new QRegExpValidator(titleRx, this);
163     d->titleEdit->setValidator(titleValidator);
164     d->titleEdit->setPlaceholderText(i18nc("@label: album properties", "Enter album title here..."));
165 
166     QLabel* const categoryLabel       = new QLabel(page);
167     categoryLabel->setText(i18nc("@label: album properties", "Ca&tegory:"));
168 
169     d->categoryCombo                  = new QComboBox(page);
170     d->categoryCombo->setEditable(true);
171     categoryLabel->setBuddy(d->categoryCombo);
172 
173     QLabel* const parentLabel         = new QLabel(page);
174     parentLabel->setText(i18nc("@label: album properties", "Ch&ild Of:"));
175 
176     d->parentCombo                    = new QComboBox(page);
177     parentLabel->setBuddy(d->parentCombo);
178 
179     QLabel* const commentsLabel       = new QLabel(page);
180     commentsLabel->setText(i18nc("@label: album properties", "Ca&ption:"));
181 
182     d->commentsEdit                   = new QPlainTextEdit(page);
183     commentsLabel->setBuddy(d->commentsEdit);
184     d->commentsEdit->setWordWrapMode(QTextOption::WordWrap);
185     d->commentsEdit->setPlaceholderText(i18nc("@label: album properties", "Enter album caption here..."));
186 
187     QLabel* const dateLabel           = new QLabel(page);
188     dateLabel->setText(i18nc("@label: album properties", "Album &date:"));
189 
190     d->datePicker                     = new AlbumDatePicker(page);
191     dateLabel->setBuddy(d->datePicker);
192 
193     DHBox* const buttonRow            = new DHBox(page);
194     QPushButton* const dateLowButton  = new QPushButton(i18nc("@action: Selects the date of the oldest image", "&Oldest"),  buttonRow);
195     dateLowButton->setWhatsThis(i18nc("@info", "Use this button to select the date of the oldest image from album."));
196     QPushButton* const dateAvgButton  = new QPushButton(i18nc("@action: Calculates the average date",          "&Average"), buttonRow);
197     dateAvgButton->setWhatsThis(i18nc("@info", "Use this button to calculate the average date of images from album."));
198     QPushButton* const dateHighButton = new QPushButton(i18nc("@action: Selects the date of the newest image", "Newest"),   buttonRow);
199     dateHighButton->setWhatsThis(i18nc("@info", "Use this button to select the date of the newest image from album."));
200 
201     if (create)
202     {
203         setTabOrder(d->titleEdit, d->categoryCombo);
204         setTabOrder(d->categoryCombo, d->parentCombo);
205         setTabOrder(d->parentCombo, d->commentsEdit);
206         setTabOrder(d->commentsEdit, d->datePicker);
207     }
208     else
209     {
210         setTabOrder(d->titleEdit, d->categoryCombo);
211         setTabOrder(d->categoryCombo, d->commentsEdit);
212         setTabOrder(d->commentsEdit, d->datePicker);
213         d->parentCombo->hide();
214         parentLabel->hide();
215     }
216 
217     d->commentsEdit->setTabChangesFocus(true);
218 
219     // --------------------------------------------------------
220 
221     QGridLayout* const grid = new QGridLayout();
222     grid->addWidget(logo,             0, 0, 1, 1);
223     grid->addWidget(d->topLabel,      0, 1, 1, 1);
224     grid->addWidget(topLine,          1, 0, 1, 2);
225     grid->addWidget(titleLabel,       2, 0, 1, 1);
226     grid->addWidget(d->titleEdit,     2, 1, 1, 1);
227     grid->addWidget(categoryLabel,    3, 0, 1, 1);
228     grid->addWidget(d->categoryCombo, 3, 1, 1, 1);
229 
230     if (create)
231     {
232         grid->addWidget(parentLabel,      4, 0, 1, 1);
233         grid->addWidget(d->parentCombo,   4, 1, 1, 1);
234         grid->addWidget(commentsLabel,    5, 0, 1, 1, Qt::AlignLeft | Qt::AlignTop);
235         grid->addWidget(d->commentsEdit,  5, 1, 1, 1);
236         grid->addWidget(dateLabel,        6, 0, 1, 1, Qt::AlignLeft | Qt::AlignTop);
237         grid->addWidget(d->datePicker,    6, 1, 1, 1);
238         grid->addWidget(buttonRow,        7, 1, 1, 1);
239     }
240     else
241     {
242         grid->addWidget(commentsLabel,    4, 0, 1, 1, Qt::AlignLeft | Qt::AlignTop);
243         grid->addWidget(d->commentsEdit,  4, 1, 1, 1);
244         grid->addWidget(dateLabel,        5, 0, 1, 1, Qt::AlignLeft | Qt::AlignTop);
245         grid->addWidget(d->datePicker,    5, 1, 1, 1);
246         grid->addWidget(buttonRow,        6, 1, 1, 1);
247     }
248 
249     grid->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
250     grid->setContentsMargins(QMargins());
251     page->setLayout(grid);
252 
253     QVBoxLayout* const vbx = new QVBoxLayout(this);
254     vbx->addWidget(page);
255     vbx->addWidget(d->buttons);
256     setLayout(vbx);
257 
258     // Initialize ---------------------------------------------
259 
260     ApplicationSettings* const settings = ApplicationSettings::instance();
261 
262     if (settings)
263     {
264         d->categoryCombo->addItem(QString());
265         QStringList Categories = settings->getAlbumCategoryNames();
266         d->categoryCombo->addItems(Categories);
267         int categoryIndex      = Categories.indexOf(album->category());
268 
269         if (categoryIndex != -1)
270         {
271             // + 1 because of the empty item
272 
273             d->categoryCombo->setCurrentIndex(categoryIndex + 1);
274         }
275     }
276 
277     if (create)
278     {
279         d->titleEdit->setText(i18nc("@label: album properties", "New Album"));
280         d->datePicker->setDate(QDate::currentDate());
281         d->parentCombo->addItem(i18nc("@item: album properties", "Selected Album (Default)"));
282         d->parentCombo->addItem(i18nc("@item: top level folder of album","Root of current collection"));
283     }
284     else
285     {
286         d->titleEdit->setText(album->title());
287         d->commentsEdit->setPlainText(album->caption());
288         d->datePicker->setDate(album->date());
289     }
290 
291     d->titleEdit->selectAll();
292     d->titleEdit->setFocus();
293 
294     // -- slots connections -------------------------------------------
295 
296     connect(d->titleEdit, SIGNAL(textChanged(QString)),
297             this, SLOT(slotTitleChanged(QString)));
298 
299     connect(dateLowButton, SIGNAL(clicked()),
300             this, SLOT(slotDateLowButtonClicked()));
301 
302     connect(dateAvgButton, SIGNAL(clicked()),
303             this, SLOT(slotDateAverageButtonClicked()));
304 
305     connect(dateHighButton, SIGNAL(clicked()),
306             this, SLOT(slotDateHighButtonClicked()));
307 
308     connect(d->parentCombo, SIGNAL(activated(int)),
309             this, SLOT(slotNewAlbumTextChanged(int)));
310 
311     connect(d->buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()),
312             this, SLOT(accept()));
313 
314     connect(d->buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()),
315             this, SLOT(reject()));
316 
317     connect(d->buttons->button(QDialogButtonBox::Help), SIGNAL(clicked()),
318             this, SLOT(slotHelp()));
319 }
320 
~AlbumPropsEdit()321 AlbumPropsEdit::~AlbumPropsEdit()
322 {
323     delete d;
324 }
325 
title() const326 QString AlbumPropsEdit::title() const
327 {
328     return d->titleEdit->text().trimmed();
329 }
330 
comments() const331 QString AlbumPropsEdit::comments() const
332 {
333     return d->commentsEdit->toPlainText();
334 }
335 
date() const336 QDate AlbumPropsEdit::date() const
337 {
338     // See bug #267944 : update calendar view if user enter a date in text field.
339 
340     d->datePicker->dateLineEnterPressed();
341 
342     return d->datePicker->date();
343 }
344 
parent() const345 int AlbumPropsEdit::parent() const
346 {
347     return d->parentCombo->currentIndex();
348 }
349 
category() const350 QString AlbumPropsEdit::category() const
351 {
352     QString name = d->categoryCombo->currentText();
353 
354     if (name.isEmpty())
355     {
356         name = i18nc("@info: album properties", "Uncategorized Album");
357     }
358 
359     return name;
360 }
361 
albumCategories() const362 QStringList AlbumPropsEdit::albumCategories() const
363 {
364     QStringList Categories;
365     ApplicationSettings* const settings = ApplicationSettings::instance();
366 
367     if (settings)
368     {
369         Categories = settings->getAlbumCategoryNames();
370     }
371 
372     QString currentCategory = d->categoryCombo->currentText();
373 
374     if (Categories.indexOf(currentCategory) == -1)
375     {
376         Categories.append(currentCategory);
377     }
378 
379     Categories.sort();
380 
381     return Categories;
382 }
383 
editProps(PAlbum * const album,QString & title,QString & comments,QDate & date,QString & category,QStringList & albumCategories)384 bool AlbumPropsEdit::editProps(PAlbum* const album, QString& title,
385                                QString& comments, QDate& date, QString& category,
386                                QStringList& albumCategories)
387 {
388     QPointer<AlbumPropsEdit> dlg = new AlbumPropsEdit(album);
389 
390     bool ok = (dlg->exec() == QDialog::Accepted);
391 
392     title           = dlg->title();
393     comments        = dlg->comments();
394     date            = dlg->date();
395     category        = dlg->category();
396     albumCategories = dlg->albumCategories();
397 
398     delete dlg;
399     return ok;
400 }
401 
createNew(PAlbum * const parent,QString & title,QString & comments,QDate & date,QString & category,QStringList & albumCategories,int & parentSelector)402 bool AlbumPropsEdit::createNew(PAlbum* const parent, QString& title, QString& comments,
403                                QDate& date, QString& category, QStringList& albumCategories,
404                                int& parentSelector)
405 {
406     QPointer<AlbumPropsEdit> dlg = new AlbumPropsEdit(parent, true);
407 
408     bool ok = (dlg->exec() == QDialog::Accepted);
409 
410     title           = dlg->title();
411     comments        = dlg->comments();
412     date            = dlg->date();
413     category        = dlg->category();
414     albumCategories = dlg->albumCategories();
415     parentSelector  = dlg->parent();
416 
417     delete dlg;
418     return ok;
419 }
420 
slotTitleChanged(const QString & newtitle)421 void AlbumPropsEdit::slotTitleChanged(const QString& newtitle)
422 {
423     QRegExp emptyTitle = QRegExp(QLatin1String("^\\s*$"));
424     bool enable        = (!emptyTitle.exactMatch(newtitle) && !newtitle.isEmpty());
425     d->buttons->button(QDialogButtonBox::Ok)->setEnabled(enable);
426 }
427 
slotNewAlbumTextChanged(int index)428 void AlbumPropsEdit::slotNewAlbumTextChanged(int index)
429 {
430     QString title;
431 
432     if (index == 0)
433     {
434         title = d->album->title();
435     }
436     else
437     {
438         title = CollectionManager::instance()->albumRootLabel(d->album->albumRootId());
439     }
440 
441     d->topLabel->setText(i18nc("@label: album properties", "Create new Album in\n\"%1\"", title));
442 }
443 
slotDateLowButtonClicked()444 void AlbumPropsEdit::slotDateLowButtonClicked()
445 {
446     setCursor(Qt::WaitCursor);
447 
448     QDate lowDate = CoreDbAccess().db()->getAlbumLowestDate(d->album->id());
449 
450     if (lowDate.isValid())
451     {
452         d->datePicker->setDate(lowDate);
453     }
454 
455     setCursor(Qt::ArrowCursor);
456 }
457 
slotDateHighButtonClicked()458 void AlbumPropsEdit::slotDateHighButtonClicked()
459 {
460     setCursor(Qt::WaitCursor);
461 
462     QDate highDate = CoreDbAccess().db()->getAlbumHighestDate(d->album->id());
463 
464     if (highDate.isValid())
465     {
466         d->datePicker->setDate(highDate);
467     }
468 
469     setCursor(Qt::ArrowCursor);
470 }
471 
slotDateAverageButtonClicked()472 void AlbumPropsEdit::slotDateAverageButtonClicked()
473 {
474     setCursor(Qt::WaitCursor);
475 
476     QDate avDate = CoreDbAccess().db()->getAlbumAverageDate(d->album->id());
477 
478     setCursor(Qt::ArrowCursor);
479 
480     if (avDate.isValid())
481     {
482         d->datePicker->setDate(avDate);
483     }
484     else
485     {
486         QMessageBox::critical(this, i18nc("@title: album properties", "Could Not Calculate Average"),
487                                     i18nc("@info: album properties", "Could not calculate date average for this album."));
488     }
489 }
490 
slotHelp()491 void AlbumPropsEdit::slotHelp()
492 {
493     DXmlGuiWindow::openHandbook();
494 }
495 
496 } // namespace Digikam
497 
498 #include "albumpropsedit.moc"
499