diff --git a/core/app/date/datefolderview.cpp b/core/app/date/datefolderview.cpp index bb388e5b0c..0c01873653 100644 --- a/core/app/date/datefolderview.cpp +++ b/core/app/date/datefolderview.cpp @@ -1,213 +1,215 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-04-27 * Description : a folder view for date albums. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2009-2010 by Johannes Wienke * Copyright (C) 2014 by Michael G. Hansen * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "datefolderview.h" // KDE includes #include // Local includes #include "digikam_debug.h" #include "album.h" #include "coredb.h" #include "applicationsettings.h" #include "datetreeview.h" #include "monthwidget.h" namespace Digikam { class Q_DECL_HIDDEN DateFolderView::Private { public: explicit Private() : active(false), selected(), dateTreeView(nullptr), monthview(nullptr) { } - bool active; + bool active; - QString selected; + QString selected; - DateTreeView* dateTreeView; - MonthWidget* monthview; + DateTreeView* dateTreeView; + MonthWidget* monthview; }; DateFolderView::DateFolderView(QWidget* const parent, DateAlbumModel* const dateAlbumModel) : DVBox(parent), StateSavingObject(this), d(new Private) { setObjectName(QLatin1String("DateFolderView")); d->dateTreeView = new DateTreeView(this); d->dateTreeView->setAlbumModel(dateAlbumModel); d->dateTreeView->setAlbumManagerCurrentAlbum(true); d->monthview = new MonthWidget(this); connect(d->dateTreeView, SIGNAL(currentAlbumChanged(Album*)), this, SLOT(slotSelectionChanged(Album*))); // Loading of DAlbums may take longer that setting up the gui. Therefore // the first call to setActive may not set the current album in the album // manager as it is not yet loaded. To achieve this, we wait for loading // DAlbums and set the active album in the album manager if this tab is // active + connect(AlbumManager::instance(), SIGNAL(signalAllDAlbumsLoaded()), this, SLOT(slotAllAlbumsLoaded())); } DateFolderView::~DateFolderView() { saveState(); } void DateFolderView::setItemModel(ItemFilterModel* const model) { d->monthview->setItemModel(model); } void DateFolderView::setActive(const bool val) { if (d->active == val) { return; } d->active = val; if (d->active) { AlbumManager::instance()->setCurrentAlbums(QList() << d->dateTreeView->currentAlbum()); slotSelectionChanged(d->dateTreeView->currentAlbum()); } else { d->monthview->setActive(false); } } void DateFolderView::slotSelectionChanged(Album* selectedAlbum) { if (!d->active) { qCDebug(DIGIKAM_GENERAL_LOG) << "Not active, returning without action"; return; } d->monthview->setActive(false); DAlbum* const dalbum = dynamic_cast (selectedAlbum); if (!dalbum) { return; } if (dalbum->range() == DAlbum::Month) { QDate date = dalbum->date(); d->monthview->setActive(true); d->monthview->setYearMonth(date.year(), date.month()); } if (d->active) { AlbumManager::instance()->setCurrentAlbums(QList() << dalbum); } } void DateFolderView::slotAllAlbumsLoaded() { if (d->active) { - AlbumManager::instance()->setCurrentAlbums(QList() - << d->dateTreeView->currentAlbum()); + AlbumManager::instance()->setCurrentAlbums(QList() << d->dateTreeView->currentAlbum()); slotSelectionChanged(d->dateTreeView->currentAlbum()); } } void DateFolderView::setConfigGroup(const KConfigGroup& group) { StateSavingObject::setConfigGroup(group); d->dateTreeView->setConfigGroup(group); } void DateFolderView::doLoadState() { d->dateTreeView->loadState(); } void DateFolderView::doSaveState() { d->dateTreeView->saveState(); } void DateFolderView::gotoDate(const QDate& dt) { qCDebug(DIGIKAM_GENERAL_LOG) << "Going to date " << dt; QModelIndex dateIndex = d->dateTreeView->albumModel()->monthIndexForDate(dt); if (!dateIndex.isValid()) { qCDebug(DIGIKAM_GENERAL_LOG) << "Cannot find an album for date " << dt; + return; } DAlbum* const dateAlbum = d->dateTreeView->albumModel()->albumForIndex(dateIndex); if (!dateAlbum) { qCWarning(DIGIKAM_GENERAL_LOG) << "Could not retrieve an album for index " << dateIndex; + return; } qCDebug(DIGIKAM_GENERAL_LOG) << "Got date album " << dateAlbum; d->dateTreeView->setCurrentAlbums(QList() << dateAlbum); } void DateFolderView::changeAlbumFromHistory(DAlbum* const album) { d->dateTreeView->setCurrentAlbums(QList() << album); } AlbumPointer DateFolderView::currentAlbum() const { - return AlbumPointer (d->dateTreeView->currentAlbum()); + return AlbumPointer(d->dateTreeView->currentAlbum()); } } // namespace Digikam diff --git a/core/app/date/datefolderview.h b/core/app/date/datefolderview.h index 0f31281152..5bf5b78a04 100644 --- a/core/app/date/datefolderview.h +++ b/core/app/date/datefolderview.h @@ -1,88 +1,89 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-04-27 * Description : a folder view for date albums. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2009-2010 by Johannes Wienke * Copyright (C) 2014 by Michael G. Hansen * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DATE_FOLDER_VIEW_H #define DIGIKAM_DATE_FOLDER_VIEW_H // Qt includes #include // Local includes #include "dlayoutbox.h" #include "albummanager.h" #include "statesavingobject.h" namespace Digikam { class Album; class DAlbum; class DateAlbumModel; class ItemFilterModel; template class AlbumPointer; -class DateFolderView : public DVBox, public StateSavingObject +class DateFolderView : public DVBox, + public StateSavingObject { Q_OBJECT public: explicit DateFolderView(QWidget* const parent, DateAlbumModel* const dateAlbumModel); ~DateFolderView(); void setItemModel(ItemFilterModel* const model); void setActive(const bool val); void gotoDate(const QDate& dt); void changeAlbumFromHistory(DAlbum* const album); AlbumPointer currentAlbum() const; void doLoadState(); void doSaveState(); virtual void setConfigGroup(const KConfigGroup& group); private Q_SLOTS: void slotSelectionChanged(Album* selectedAlbum); void slotAllAlbumsLoaded(); private: class Private; const QScopedPointer d; }; } // namespace Digikam #endif // DIGIKAM_DATE_FOLDER_VIEW_H diff --git a/core/app/date/ddateedit.cpp b/core/app/date/ddateedit.cpp index a416c27a77..5d8a75c862 100644 --- a/core/app/date/ddateedit.cpp +++ b/core/app/date/ddateedit.cpp @@ -1,523 +1,546 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2002-01-10 * Description : a combo box to list date. * this widget come from libkdepim. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2002 by Cornelius Schumacher * Copyright (C) 2003-2004 by Reinhold Kainhofer * Copyright (C) 2004 by Tobias Koenig * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "ddateedit.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "ddatepickerpopup.h" namespace Digikam { class Q_DECL_HIDDEN DateValidator : public QValidator { public: DateValidator(const QStringList& keywords, const QString& dateFormat, QWidget* const parent) : QValidator(parent), mKeywords(keywords), mDateFormat(dateFormat) { } virtual State validate(QString& str, int&) const { int length = str.length(); // empty string is intermediate so one can clear the edit line and start from scratch + if (length <= 0) { return Intermediate; } if (mKeywords.contains(str.toLower())) { return Acceptable; } bool ok = QDate::fromString(str, mDateFormat).isValid(); if (ok) { return Acceptable; } else { return Intermediate; } } private: QStringList mKeywords; QString mDateFormat; }; // ----------------------------------------------------------------------------------- class Q_DECL_HIDDEN DDateEdit::Private { public: explicit Private() : readOnly(false), textChanged(false), discardNextMousePress(false), popup(nullptr) { } bool readOnly; bool textChanged; bool discardNextMousePress; QDate date; QString dateFormat; QMap keywordMap; DDatePickerPopup* popup; }; DDateEdit::DDateEdit(QWidget* const parent, const QString& name) : QComboBox(parent), d(new Private) { setObjectName(name); + // need at least one entry for popup to work + setMaxCount(1); setEditable(true); d->date = QDate::currentDate(); d->dateFormat = QLocale().dateFormat(QLocale::ShortFormat); if (!d->dateFormat.contains(QLatin1String("yyyy"))) { d->dateFormat.replace(QLatin1String("yy"), QLatin1String("yyyy")); } QString today = d->date.toString(d->dateFormat); addItem(today); setCurrentIndex(0); setMinimumSize(sizeHint()); setMinimumSize(minimumSizeHint()); connect(lineEdit(), SIGNAL(returnPressed()), this, SLOT(lineEnterPressed())); connect(this, SIGNAL(currentTextChanged(QString)), this, SLOT(slotTextChanged(QString))); d->popup = new DDatePickerPopup(DDatePickerPopup::DatePicker | DDatePickerPopup::Words); d->popup->hide(); d->popup->installEventFilter(this); connect(d->popup, SIGNAL(dateChanged(QDate)), this, SLOT(dateSelected(QDate))); // handle keyword entry + setupKeywords(); lineEdit()->installEventFilter(this); setValidator(new DateValidator(d->keywordMap.keys(), d->dateFormat, this)); d->textChanged = false; } DDateEdit::~DDateEdit() { delete d->popup; d->popup = nullptr; delete d; } void DDateEdit::setDate(const QDate& date) { assignDate(date); updateView(); } QDate DDateEdit::date() const { return d->date; } void DDateEdit::setReadOnly(bool readOnly) { d->readOnly = readOnly; lineEdit()->setReadOnly(readOnly); } bool DDateEdit::isReadOnly() const { return d->readOnly; } void DDateEdit::showPopup() { if (d->readOnly) { return; } QScreen* screen = qApp->primaryScreen(); if (QWidget* const widget = nativeParentWidget()) { if (QWindow* const window = widget->windowHandle()) + { screen = window->screen(); + } } QRect desk = screen->geometry(); QPoint popupPoint = mapToGlobal(QPoint(0, 0)); int dateFrameHeight = d->popup->sizeHint().height(); - if (popupPoint.y() + height() + dateFrameHeight > desk.bottom()) + if ((popupPoint.y() + height() + dateFrameHeight) > desk.bottom()) { popupPoint.setY(popupPoint.y() - dateFrameHeight); } else { popupPoint.setY(popupPoint.y() + height()); } int dateFrameWidth = d->popup->sizeHint().width(); - if (popupPoint.x() + dateFrameWidth > desk.right()) + if ((popupPoint.x() + dateFrameWidth) > desk.right()) { popupPoint.setX(desk.right() - dateFrameWidth); } if (popupPoint.x() < desk.left()) { popupPoint.setX(desk.left()); } if (popupPoint.y() < desk.top()) { popupPoint.setY(desk.top()); } if (d->date.isValid()) { d->popup->setDate(d->date); } else { d->popup->setDate(QDate::currentDate()); } d->popup->popup(popupPoint); // The combo box is now shown pressed. Make it show not pressed again // by causing its (invisible) list box to emit a 'selected' signal. // First, ensure that the list box contains the date currently displayed. + QDate date = parseDate(); assignDate(date); updateView(); + // Now, simulate an Enter to unpress it + QAbstractItemView* const lb = view(); if (lb) { lb->setCurrentIndex(lb->model()->index(0, 0)); QKeyEvent* const keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); QApplication::postEvent(lb, keyEvent); } } void DDateEdit::dateSelected(const QDate& date) { // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. + if (this->assignDate(date)) { updateView(); emit dateChanged(date); if (date.isValid()) { d->popup->hide(); } } } void DDateEdit::dateEntered(const QDate& date) { if (assignDate(date)) { updateView(); emit dateChanged(date); } } void DDateEdit::lineEnterPressed() { bool replaced = false; QDate date = parseDate(&replaced); // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. if (this->assignDate(date)) { if (replaced) { updateView(); } emit dateChanged(date); } } QDate DDateEdit::parseDate(bool* replaced) const { QString text = currentText(); QDate result; if (replaced) { (*replaced) = false; } - if (text.isEmpty()) + if (text.isEmpty()) { result = QDate(); } else if (d->keywordMap.contains(text.toLower())) { QDate today = QDate::currentDate(); int i = d->keywordMap[text.toLower()]; if (i >= 100) { /* * A day name has been entered. Convert to offset from today. * This uses some math tricks to figure out the offset in days * to the next date the given day of the week occurs. There * are two cases, that the new day is >= the current day, which means * the new day has not occurred yet or that the new day < the current day, * which means the new day is already passed (so we need to find the * day in the next week). */ - i -= 100; + i -= 100; int currentDay = today.dayOfWeek(); if (i >= currentDay) { i -= currentDay; } else { i += 7 - currentDay; } } result = today.addDays(i); if (replaced) { (*replaced) = true; } } else { result = QDate::fromString(text, d->dateFormat); } return result; } bool DDateEdit::eventFilter(QObject* object, QEvent* event) { if (object == lineEdit()) { // We only process the focus out event if the text has changed // since we got focus - if ((event->type() == QEvent::FocusOut) && d->textChanged) + + if ((event->type() == QEvent::FocusOut) && d->textChanged) { lineEnterPressed(); d->textChanged = false; } else if (event->type() == QEvent::KeyPress) { // Up and down arrow keys step the date + QKeyEvent* const keyEvent = (QKeyEvent*)event; if (keyEvent->key() == Qt::Key_Return) { lineEnterPressed(); return true; } int step = 0; - if (keyEvent->key() == Qt::Key_Up) + if (keyEvent->key() == Qt::Key_Up) { step = 1; } else if (keyEvent->key() == Qt::Key_Down) { step = -1; } if (step && !d->readOnly) { QDate date = parseDate(); if (date.isValid()) { date = date.addDays(step); if (assignDate(date)) { updateView(); + emit dateChanged(date); + return true; } } } } } else { // It's a date picker event + switch (event->type()) { case QEvent::MouseButtonDblClick: case QEvent::MouseButtonPress: { QMouseEvent* const mouseEvent = (QMouseEvent*)event; if (!d->popup->rect().contains(mouseEvent->pos())) { QPoint globalPos = d->popup->mapToGlobal(mouseEvent->pos()); if (QApplication::widgetAt(globalPos) == this) { // The date picker is being closed by a click on the // DDateEdit widget. Avoid popping it up again immediately. + d->discardNextMousePress = true; } } break; } + default: + { break; + } } } return false; } void DDateEdit::mousePressEvent(QMouseEvent* e) { - if (e->button() == Qt::LeftButton && d->discardNextMousePress) + if ((e->button() == Qt::LeftButton) && d->discardNextMousePress) { d->discardNextMousePress = false; return; } QComboBox::mousePressEvent(e); } void DDateEdit::slotTextChanged(const QString&) { QDate date = parseDate(); // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. + if (this->assignDate(date)) { emit dateChanged(date); } d->textChanged = true; } void DDateEdit::setupKeywords() { // Create the keyword list. This will be used to match against when the user // enters information. + d->keywordMap.insert(i18n("tomorrow"), 1); d->keywordMap.insert(i18n("today"), 0); d->keywordMap.insert(i18n("yesterday"), -1); QString dayName; - for (int i = 1; i <= 7; ++i) + for (int i = 1 ; i <= 7 ; ++i) { dayName = QLocale().standaloneDayName(i, QLocale::LongFormat).toLower(); d->keywordMap.insert(dayName, i + 100); } } bool DDateEdit::assignDate(const QDate& date) { d->date = date; d->textChanged = false; + return true; } void DDateEdit::updateView() { QString dateString; if (d->date.isValid()) { dateString = d->date.toString(d->dateFormat); } // We do not want to generate a signal here, // since we explicitly setting the date + bool blocked = signalsBlocked(); blockSignals(true); removeItem(0); insertItem(0, dateString); blockSignals(blocked); } } // namespace Digikam diff --git a/core/app/date/ddateedit.h b/core/app/date/ddateedit.h index b1db30df55..3af95c5935 100644 --- a/core/app/date/ddateedit.h +++ b/core/app/date/ddateedit.h @@ -1,143 +1,143 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2002-01-10 * Description : a combo box to list date. * this widget come from libkdepim. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2002 by Cornelius Schumacher * Copyright (C) 2003-2004 by Reinhold Kainhofer * Copyright (C) 2004 by Tobias Koenig * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DDATE_EDIT_H #define DIGIKAM_DDATE_EDIT_H // Qt includes #include #include #include namespace Digikam { /** * A date editing widget that consists of an editable combo box. * The combo box contains the date in text form, and clicking the combo * box arrow will display a 'popup' style date picker. * * This widget also supports advanced features like allowing the user * to type in the day name to get the date. The following keywords * are supported (in the native language): tomorrow, yesterday, today, * Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday. * */ class DDateEdit : public QComboBox { Q_OBJECT public: explicit DDateEdit(QWidget* const parent=nullptr, const QString& name=QString()); virtual ~DDateEdit(); /** * @return The date entered. This date could be invalid, * you have to check validity yourself. */ QDate date() const; /** * Sets whether the widget is read-only for the user. If read-only, * the date picker pop-up is inactive, and the displayed date cannot be edited. * * @param readOnly True to set the widget read-only, false to set it read-write. */ void setReadOnly(bool readOnly); /** * @return True if the widget is read-only, false if read-write. */ - bool isReadOnly() const; + bool isReadOnly() const; - virtual void showPopup() override; + virtual void showPopup() override; Q_SIGNALS: /** * This signal is emitted whenever the user modifies the date. * The passed date can be invalid. */ void dateChanged(const QDate& date); public Q_SLOTS: /** * Sets the date. * * @param date The new date to display. This date must be valid or * it will not be set */ void setDate(const QDate& date); protected Q_SLOTS: void lineEnterPressed(); void slotTextChanged(const QString&); void dateEntered(const QDate&); void dateSelected(const QDate&); protected: - virtual bool eventFilter(QObject*, QEvent*) override; - virtual void mousePressEvent(QMouseEvent*) override; + virtual bool eventFilter(QObject*, QEvent*) override; + virtual void mousePressEvent(QMouseEvent*) override; /** * Sets the date, without altering the display. * This method is used internally to set the widget's date value. * As a virtual method, it allows derived classes to perform additional validation * on the date value before it is set. Derived classes should return true if * QDate::isValid(@p date) returns false. * * @param date The new date to set. * @return True if the date was set, false if it was considered invalid and * remains unchanged. */ virtual bool assignDate(const QDate& date); /** * Fills the keyword map. Re-implement it if you want additional * keywords. */ void setupKeywords(); private: - QDate parseDate(bool* = nullptr) const; + QDate parseDate(bool* = nullptr) const; void updateView(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DDATE_EDIT_H diff --git a/core/app/date/ddatepicker.cpp b/core/app/date/ddatepicker.cpp index b26b704dcf..b14ff9a87c 100644 --- a/core/app/date/ddatepicker.cpp +++ b/core/app/date/ddatepicker.cpp @@ -1,585 +1,607 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : A date selection widget. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "ddatepicker.h" #include "ddatepicker_p.h" // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "ddatetable_p.h" #include "dpopupframe.h" namespace Digikam { DDatePicker::DDatePicker(QWidget* const parent) : QFrame(parent), d(new Private(this)) { initWidget(QDate::currentDate()); } DDatePicker::DDatePicker(const QDate& dt, QWidget* const parent) : QFrame(parent), d(new Private(this)) { initWidget(dt); } void DDatePicker::initWidget(const QDate& dt) { const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QBoxLayout* const topLayout = new QVBoxLayout(this); topLayout->setSpacing(0); topLayout->setContentsMargins(0, 0, 0, 0); d->navigationLayout = new QHBoxLayout(); d->navigationLayout->setSpacing(0); d->navigationLayout->setContentsMargins(0, 0, 0, 0); topLayout->addLayout(d->navigationLayout); d->navigationLayout->addStretch(); d->yearBackward = new QToolButton(this); d->yearBackward->setAutoRaise(true); d->navigationLayout->addWidget(d->yearBackward); d->monthBackward = new QToolButton(this); d->monthBackward ->setAutoRaise(true); d->navigationLayout->addWidget(d->monthBackward); d->navigationLayout->addSpacing(spacingHint); d->selectMonth = new QToolButton(this); d->selectMonth ->setAutoRaise(true); d->navigationLayout->addWidget(d->selectMonth); d->selectYear = new QToolButton(this); d->selectYear->setCheckable(true); d->selectYear->setAutoRaise(true); d->navigationLayout->addWidget(d->selectYear); d->navigationLayout->addSpacing(spacingHint); d->monthForward = new QToolButton(this); d->monthForward ->setAutoRaise(true); d->navigationLayout->addWidget(d->monthForward); d->yearForward = new QToolButton(this); d->yearForward ->setAutoRaise(true); d->navigationLayout->addWidget(d->yearForward); d->navigationLayout->addStretch(); d->line = new QLineEdit(this); d->val = new DatePickerValidator(this); d->table = new DDateTable(this); setFocusProxy(d->table); d->fontsize = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize(); if (d->fontsize == -1) { d->fontsize = QFontInfo(QFontDatabase::systemFont(QFontDatabase::GeneralFont)).pointSize(); } - d->fontsize++; // Make a little bigger + d->fontsize++; // Make a little bigger - d->selectWeek = new QComboBox(this); // read only week selection + d->selectWeek = new QComboBox(this); // read only week selection d->selectWeek->setFocusPolicy(Qt::NoFocus); d->todayButton = new QToolButton(this); d->todayButton->setIcon(QIcon::fromTheme(QLatin1String("go-jump-today"))); d->yearForward->setToolTip(i18n("Next year")); d->yearBackward->setToolTip(i18n("Previous year")); d->monthForward->setToolTip(i18n("Next month")); d->monthBackward->setToolTip(i18n("Previous month")); d->selectWeek->setToolTip(i18n("Select a week")); d->selectMonth->setToolTip(i18n("Select a month")); d->selectYear->setToolTip(i18n("Select a year")); d->todayButton->setToolTip(i18n("Select the current day")); // ----- setFontSize(d->fontsize); d->line->setValidator(d->val); d->line->installEventFilter(this); if (QApplication::isRightToLeft()) { d->yearForward->setIcon(QIcon::fromTheme(QLatin1String("arrow-left-double"))); d->yearBackward->setIcon(QIcon::fromTheme(QLatin1String("arrow-right-double"))); d->monthForward->setIcon(QIcon::fromTheme(QLatin1String("go-previous"))); d->monthBackward->setIcon(QIcon::fromTheme(QLatin1String("go-next"))); } else { d->yearForward->setIcon(QIcon::fromTheme(QLatin1String("arrow-right-double"))); d->yearBackward->setIcon(QIcon::fromTheme(QLatin1String("arrow-left-double"))); d->monthForward->setIcon(QIcon::fromTheme(QLatin1String("go-next"))); d->monthBackward->setIcon(QIcon::fromTheme(QLatin1String("go-previous"))); } connect(d->table, SIGNAL(dateChanged(QDate)), this, SLOT(dateChangedSlot(QDate))); connect(d->table, &DDateTable::tableClicked, this, &DDatePicker::tableClickedSlot); connect(d->monthForward, &QAbstractButton::clicked, this, &DDatePicker::monthForwardClicked); connect(d->monthBackward, &QAbstractButton::clicked, this, &DDatePicker::monthBackwardClicked); connect(d->yearForward, &QAbstractButton::clicked, this, &DDatePicker::yearForwardClicked); connect(d->yearBackward, &QAbstractButton::clicked, this, &DDatePicker::yearBackwardClicked); connect(d->selectWeek, SIGNAL(activated(int)), this, SLOT(weekSelected(int))); connect(d->todayButton, &QAbstractButton::clicked, this, &DDatePicker::todayButtonClicked); connect(d->selectMonth, &QAbstractButton::clicked, this, &DDatePicker::selectMonthClicked); connect(d->selectYear, &QAbstractButton::toggled, this, &DDatePicker::selectYearClicked); connect(d->line, &QLineEdit::returnPressed, this, &DDatePicker::lineEnterPressed); topLayout->addWidget(d->table); QBoxLayout* const bottomLayout = new QHBoxLayout(); bottomLayout->setContentsMargins(0, 0, 0, 0); bottomLayout->setSpacing(0); topLayout->addLayout(bottomLayout); bottomLayout->addWidget(d->todayButton); bottomLayout->addWidget(d->line); bottomLayout->addWidget(d->selectWeek); d->table->setDate(dt); dateChangedSlot(dt); // needed because table emits changed only when newDate != oldDate } DDatePicker::~DDatePicker() { delete d; } bool DDatePicker::eventFilter(QObject* o, QEvent* e) { if (e->type() == QEvent::KeyPress) { QKeyEvent* const k = (QKeyEvent *)e; - if ((k->key() == Qt::Key_PageUp) || + if ( + (k->key() == Qt::Key_PageUp) || (k->key() == Qt::Key_PageDown) || (k->key() == Qt::Key_Up) || - (k->key() == Qt::Key_Down)) + (k->key() == Qt::Key_Down) + ) { QApplication::sendEvent(d->table, e); d->table->setFocus(); + return true; // eat event } } return QFrame::eventFilter(o, e); } void DDatePicker::resizeEvent(QResizeEvent* e) { QWidget::resizeEvent(e); } void DDatePicker::dateChangedSlot(const QDate& dt) { QString dateFormat = locale().dateFormat(QLocale::ShortFormat); if (!dateFormat.contains(QLatin1String("yyyy"))) { dateFormat.replace(QLatin1String("yy"), QLatin1String("yyyy")); } d->line->setText(dt.toString(dateFormat)); d->selectMonth->setText(locale().standaloneMonthName(dt.month(), QLocale::LongFormat)); d->fillWeeksCombo(); // calculate the item num in the week combo box; normalize selected day so as if 1.1. is the first day of the week + QDate firstDay(dt.year(), 1, 1); + // If we cannot successfully create the 1st of the year, this can only mean that // the 1st is before the earliest valid date in the current calendar system, so use // the earliestValidDate as the first day. // In particular covers the case of Gregorian where 1/1/-4713 is not a valid QDate + d->selectWeek->setCurrentIndex((dt.dayOfYear() + firstDay.dayOfWeek() - 2) / 7); d->selectYear->setText(QString::number(dt.year()).rightJustified(4, QLatin1Char('0'))); emit dateChanged(dt); } void DDatePicker::tableClickedSlot() { emit dateSelected(date()); emit tableClicked(); } const QDate &DDatePicker::date() const { return d->table->date(); } bool DDatePicker::setDate(const QDate& dt) { // the table setDate does validity checking for us // this also emits dateChanged() which then calls our dateChangedSlot() + return d->table->setDate(dt); } void DDatePicker::monthForwardClicked() { - if (! setDate(date().addMonths(1))) + if (!setDate(date().addMonths(1))) { QApplication::beep(); } d->table->setFocus(); } void DDatePicker::monthBackwardClicked() { - if (! setDate(date().addMonths(-1))) + if (!setDate(date().addMonths(-1))) { QApplication::beep(); } d->table->setFocus(); } void DDatePicker::yearForwardClicked() { - if (! setDate(d->table->date().addYears(1))) + if (!setDate(d->table->date().addYears(1))) { QApplication::beep(); } d->table->setFocus(); } void DDatePicker::yearBackwardClicked() { - if (! setDate(d->table->date().addYears(-1))) + if (!setDate(d->table->date().addYears(-1))) { QApplication::beep(); } d->table->setFocus(); } void DDatePicker::weekSelected(int index) { QDate targetDay = d->selectWeek->itemData(index).toDateTime().date(); - if (! setDate(targetDay)) + if (!setDate(targetDay)) { QApplication::beep(); } d->table->setFocus(); } void DDatePicker::selectMonthClicked() { QDate thisDate(date()); d->table->setFocus(); QMenu popup(d->selectMonth); + // Populate the pick list with all the month names, this may change by year - // JPL do we need to do something here for months that fall outside valid range? + // Do we need to do something here for months that fall outside valid range? + const int monthsInYear = QDate(thisDate.year() + 1, 1, 1).addDays(-1).month(); for (int m = 1 ; m <= monthsInYear ; ++m) { popup.addAction(locale().standaloneMonthName(m))->setData(m); } QAction* item = popup.actions()[ thisDate.month() - 1 ]; // if this happens the above should already given an assertion + if (item) { popup.setActiveAction(item); } // cancelled + if ((item = popup.exec(d->selectMonth->mapToGlobal(QPoint(0, 0)), item)) == nullptr) { return; } // We need to create a valid date in the month selected so we can find out how many days are // in the month. + QDate newDate(thisDate.year(), item->data().toInt(), 1); // If we have succeeded in creating a date in the new month, then try to create the new date, // checking we don't set a day after the last day of the month + newDate.setDate(newDate.year(), newDate.month(), qMin(thisDate.day(), newDate.daysInMonth())); // Set the date, if it's invalid in any way then alert user and don't update - if (! setDate(newDate)) + + if (!setDate(newDate)) { QApplication::beep(); } } void DDatePicker::selectYearClicked() { if (!d->selectYear->isChecked()) { return; } QDate thisDate(date()); DPopupFrame* const popup = new DPopupFrame(this); DatePickerYearSelector* const picker = new DatePickerYearSelector(date(), popup); picker->resize(picker->sizeHint()); picker->setYear(thisDate.year()); picker->selectAll(); popup->setMainWidget(picker); connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int))); picker->setFocus(); if (popup->exec(d->selectYear->mapToGlobal(QPoint(0, d->selectMonth->height())))) { // We need to create a valid date in the year/month selected so we can find out how many // days are in the month. + QDate newDate(picker->year(), thisDate.month(), 1); // If we have succeeded in creating a date in the new month, then try to create the new // date, checking we don't set a day after the last day of the month + newDate = QDate(newDate.year(), newDate.month(), qMin(thisDate.day(), newDate.daysInMonth())); // Set the date, if it's invalid in any way then alert user and don't update - if (! setDate(newDate)) + + if (!setDate(newDate)) { QApplication::beep(); } } delete popup; d->selectYear->setChecked(false); } void DDatePicker::uncheckYearSelector() { d->selectYear->setChecked(false); d->selectYear->update(); } void DDatePicker::changeEvent(QEvent* e) { - if (e && e->type() == QEvent::EnabledChange) + if (e && (e->type() == QEvent::EnabledChange)) { if (isEnabled()) { d->table->setFocus(); } } } DDateTable *DDatePicker::dateTable() const { return d->table; } void DDatePicker::lineEnterPressed() { QString dateFormat = locale().dateFormat(QLocale::ShortFormat); if (!dateFormat.contains(QLatin1String("yyyy"))) { dateFormat.replace(QLatin1String("yy"), QLatin1String("yyyy")); } QDate newDate = QDate::fromString(d->line->text(), dateFormat); if (newDate.isValid()) { emit dateEntered(newDate); setDate(newDate); d->table->setFocus(); } else { QApplication::beep(); } } void DDatePicker::todayButtonClicked() { setDate(QDate::currentDate()); d->table->setFocus(); } QSize DDatePicker::sizeHint() const { return QWidget::sizeHint(); } void DDatePicker::setFontSize(int s) { QWidget* const buttons[] = { d->selectMonth, d->selectYear, }; const int NoOfButtons = sizeof(buttons) / sizeof(buttons[0]); int count; QFont font; QRect r; // ----- d->fontsize = s; for (count = 0; count < NoOfButtons; ++count) { font = buttons[count]->font(); font.setPointSize(s); buttons[count]->setFont(font); } d->table->setFontSize(s); QFontMetrics metrics(d->selectMonth->fontMetrics()); QString longestMonth; - for (int i = 1;; ++i) + for (int i = 1 ; ; ++i) { QString str = locale().standaloneMonthName(i, QLocale::LongFormat); if (str.isNull()) { break; } r = metrics.boundingRect(str); if (r.width() > d->maxMonthRect.width()) { d->maxMonthRect.setWidth(r.width()); longestMonth = str; } if (r.height() > d->maxMonthRect.height()) { d->maxMonthRect.setHeight(r.height()); } } QStyleOptionToolButton opt; opt.initFrom(d->selectMonth); opt.text = longestMonth; // stolen from QToolButton + QSize textSize = metrics.size(Qt::TextShowMnemonic, longestMonth); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + textSize.setWidth(textSize.width() + metrics.horizontalAdvance(QLatin1Char(' ')) * 2); + #else + textSize.setWidth(textSize.width() + metrics.width(QLatin1Char(' ')) * 2); + #endif int w = textSize.width(); int h = textSize.height(); - opt.rect.setHeight(h); // PM_MenuButtonIndicator depends on the height + opt.rect.setHeight(h); // PM_MenuButtonIndicator depends on the height QSize metricBound = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), d->selectMonth) - .expandedTo(QApplication::globalStrut()); + .expandedTo(QApplication::globalStrut()); d->selectMonth->setMinimumSize(metricBound); } int DDatePicker::fontSize() const { return d->fontsize; } void DDatePicker::setCloseButton(bool enable) { if (enable == (d->closeButton != nullptr)) { return; } if (enable) { d->closeButton = new QToolButton(this); d->closeButton->setAutoRaise(true); const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); d->navigationLayout->addSpacing(spacingHint); d->navigationLayout->addWidget(d->closeButton); d->closeButton->setToolTip(i18nc("@action:button", "Close")); d->closeButton->setIcon(QIcon::fromTheme(QLatin1String("window-close"))); connect(d->closeButton, &QAbstractButton::clicked, topLevelWidget(), &QWidget::close); } else { delete d->closeButton; d->closeButton = nullptr; } updateGeometry(); } bool DDatePicker::hasCloseButton() const { return (d->closeButton); } } // namespace Digikam diff --git a/core/app/date/ddatepicker.h b/core/app/date/ddatepicker.h index edf7e3eb7c..2c47d3c261 100644 --- a/core/app/date/ddatepicker.h +++ b/core/app/date/ddatepicker.h @@ -1,197 +1,197 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : A date selection widget. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DDATE_PICKER_H #define DIGIKAM_DDATE_PICKER_H // Qt includes #include #include // Local includes #include "digikam_export.h" class QLineEdit; namespace Digikam { class DDateTable; /** * Provides a widget for calendar date input. */ class DIGIKAM_GUI_EXPORT DDatePicker : public QFrame { Q_OBJECT Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged USER true) Q_PROPERTY(bool closeButton READ hasCloseButton WRITE setCloseButton) Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize) public: /** * The constructor. The current date will be displayed initially. */ explicit DDatePicker(QWidget* const parent = nullptr); /** * The constructor. The given date will be displayed initially. */ explicit DDatePicker(const QDate& dt, QWidget* const parent = nullptr); /** * The destructor. */ virtual ~DDatePicker(); /** * The size hint for date pickers. The size hint recommends the * minimum size of the widget so that all elements may be placed * without clipping. This sometimes looks ugly, so when using the * size hint, try adding 28 to each of the reported numbers of * pixels. */ - QSize sizeHint() const override; + QSize sizeHint() const override; /** * Sets the date. * * @returns @p false and does not change anything if the date given is invalid. */ bool setDate(const QDate& date); /** * @returns the selected date. */ - const QDate& date() const; + const QDate& date() const; /** * @returns the DDateTable widget child of this DDatePicker * widget. */ - DDateTable* dateTable() const; + DDateTable* dateTable() const; /** * Sets the font size of the widgets elements. */ void setFontSize(int); /** * Returns the font size of the widget elements. */ - int fontSize() const; + int fontSize() const; /** * By calling this method with @p enable = true, DDatePicker will show * a little close-button in the upper button-row. Clicking the * close-button will cause the DDatePicker's topLevelWidget()'s close() * method being called. This is mostly useful for toplevel datepickers * without a window manager decoration. * @see hasCloseButton */ void setCloseButton(bool enable); /** * @returns true if a DDatePicker shows a close-button. * @see setCloseButton */ - bool hasCloseButton() const; + bool hasCloseButton() const; protected: /// to catch move keyEvents when QLineEdit has keyFocus - bool eventFilter(QObject*, QEvent*) override; + bool eventFilter(QObject*, QEvent*) override; /// the resize event - void resizeEvent(QResizeEvent*) override; - void changeEvent(QEvent*) override; + void resizeEvent(QResizeEvent*) override; + void changeEvent(QEvent*) override; protected Q_SLOTS: void dateChangedSlot(const QDate& date); void tableClickedSlot(); void monthForwardClicked(); void monthBackwardClicked(); void yearForwardClicked(); void yearBackwardClicked(); void selectMonthClicked(); void selectYearClicked(); void uncheckYearSelector(); void lineEnterPressed(); void todayButtonClicked(); void weekSelected(int); Q_SIGNALS: /** * This signal is emitted each time the selected date is changed. * Usually, this does not mean that the date has been entered, * since the date also changes, for example, when another month is * selected. * @see dateSelected */ void dateChanged(const QDate& date); /** * This signal is emitted each time a day has been selected by * clicking on the table (hitting a day in the current month). It * has the same meaning as dateSelected() in older versions of * DDatePicker. */ void dateSelected(const QDate& date); /** * This signal is emitted when enter is pressed and a VALID date * has been entered before into the line edit. Connect to both * dateEntered() and dateSelected() to receive all events where the * user really enters a date. */ void dateEntered(const QDate& date); /** * This signal is emitted when the day has been selected by * clicking on it in the table. */ void tableClicked(); private: void initWidget(const QDate& date); private: class Private; Private *const d; friend class Private; }; } // namespace Digikam #endif // DIGIKAM_DDATE_PICKER_H diff --git a/core/app/date/ddatepicker_p.cpp b/core/app/date/ddatepicker_p.cpp index e5057deb2f..71b206f13b 100644 --- a/core/app/date/ddatepicker_p.cpp +++ b/core/app/date/ddatepicker_p.cpp @@ -1,222 +1,234 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : A date selection widget. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "ddatepicker_p.h" // Qt includes #include #include // KDE includes #include namespace Digikam { DatePickerValidator::DatePickerValidator(DDatePicker* const parent) : QValidator(parent), m_picker(parent) { } QValidator::State DatePickerValidator::validate(QString& text, int&) const { QLocale::FormatType formats[] = { QLocale::LongFormat, QLocale::ShortFormat, QLocale::NarrowFormat }; QLocale locale = m_picker->locale(); for (int i = 0 ; i < 3 ; ++i) { QDate tmp = locale.toDate(text, formats[i]); if (tmp.isValid()) { return Acceptable; } } return QValidator::Intermediate; } // ------------------------------------------------------------------------------ -// Week numbers are defined by ISO 8601 -// See https://en.wikipedia.org/wiki/Week#Week_numbering for details - +/** + * NOTE: Week numbers are defined by ISO 8601 + * See https://en.wikipedia.org/wiki/Week#Week_numbering for details + */ DatePickerYearSelector::DatePickerYearSelector(const QDate& currentDate, QWidget* const parent) : QLineEdit(parent), val(new QIntValidator(this)), result(0), oldDate(currentDate) { setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); setFrame(false); - // TODO: Find a way to get that from QLocale - //val->setRange( calendar->year( calendar->earliestValidDate() ), - // calendar->year( calendar->latestValidDate() ) ); +/* TODO: Find a way to get that from QLocale + val->setRange(calendar->year(calendar->earliestValidDate()), + calendar->year(calendar->latestValidDate())); +*/ setValidator(val); connect(this, &QLineEdit::returnPressed, this, &DatePickerYearSelector::yearEnteredSlot); } void DatePickerYearSelector::yearEnteredSlot() { bool ok; int newYear; // check if entered value is a number + newYear = text().toInt(&ok); if (!ok) { QApplication::beep(); return; } // check if new year will lead to a valid date + if (QDate(newYear, oldDate.month(), oldDate.day()).isValid()) { result = newYear; emit closeMe(1); } else { QApplication::beep(); } } int DatePickerYearSelector::year() const { return result; } void DatePickerYearSelector::setYear(int year) { setText(QString::number(year)); } // ------------------------------------------------------------------------------ DDatePicker::Private::Private(DDatePicker* const qq) - : q(qq) + : q(qq), + closeButton(nullptr), + selectWeek(nullptr), + todayButton(nullptr), + navigationLayout(nullptr), + yearForward(nullptr), + yearBackward(nullptr), + monthForward(nullptr), + monthBackward(nullptr), + selectMonth(nullptr), + selectYear(nullptr), + line(nullptr), + val(nullptr), + table(nullptr), + fontsize(0) { - closeButton = nullptr; - selectWeek = nullptr; - todayButton = nullptr; - navigationLayout = nullptr; - yearForward = nullptr; - yearBackward = nullptr; - monthForward = nullptr; - monthBackward = nullptr; - selectMonth = nullptr; - selectYear = nullptr; - line = nullptr; - val = nullptr; - table = nullptr; - fontsize = 0; } void DDatePicker::Private::fillWeeksCombo() { - // every year can have a different number of weeks - // it could be that we had 53,1..52 and now 1..53 which is the same number but different - // so always fill with new values - // We show all week numbers for all weeks between first day of year to last day of year - // This of course can be a list like 53,1,2..52 - + /** + * NOTE: every year can have a different number of weeks + * it could be that we had 53,1..52 and now 1..53 which is the same number but different + * so always fill with new values + * We show all week numbers for all weeks between first day of year to last day of year + * This of course can be a list like 53,1,2..52 + */ const QDate thisDate = q->date(); const int thisYear = thisDate.year(); QDate day(thisDate.year(), 1, 1); const QDate lastDayOfYear = QDate(thisDate.year() + 1, 1, 1).addDays(-1); selectWeek->clear(); // Starting from the first day in the year, loop through the year a week at a time // adding an entry to the week combo for each week in the year for (; day.isValid() && day <= lastDayOfYear; day = day.addDays(7)) { // Get the ISO week number for the current day and what year that week is in // e.g. 1st day of this year may fall in week 53 of previous year + int weekYear = thisYear; const int week = day.weekNumber(&weekYear); QString weekString = i18n("Week %1", week); // show that this is a week from a different year + if (weekYear != thisYear) { weekString += QLatin1Char('*'); } // when the week is selected, go to the same weekday as the one // that is currently selected in the date table + QDate targetDate = day.addDays(thisDate.dayOfWeek() - day.dayOfWeek()); selectWeek->addItem(weekString, targetDate); // make sure that the week of the lastDayOfYear is always inserted: in Chinese calendar // system, this is not always the case - if (day < lastDayOfYear && - day.daysTo(lastDayOfYear) < 7 && - lastDayOfYear.weekNumber() != day.weekNumber()) + + if ( + (day < lastDayOfYear) && + (day.daysTo(lastDayOfYear) < 7) && + (lastDayOfYear.weekNumber() != day.weekNumber()) + ) { day = lastDayOfYear.addDays(-7); } } } QDate DDatePicker::Private::validDateInYearMonth(int year, int month) { QDate newDate; // Try to create a valid date in this year and month // First try the first of the month, then try last of month - if (QDate(year, month, 1).isValid()) + + if (QDate(year, month, 1).isValid()) { newDate = QDate(year, month, 1); } else if (QDate(year, month + 1, 1).isValid()) { newDate = QDate(year, month + 1, 1).addDays(-1); } else { newDate = QDate::fromJulianDay(0); } return newDate; } } // namespace Digikam diff --git a/core/app/date/ddatepicker_p.h b/core/app/date/ddatepicker_p.h index 5758e2ca33..e359eb9b12 100644 --- a/core/app/date/ddatepicker_p.h +++ b/core/app/date/ddatepicker_p.h @@ -1,142 +1,151 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : A date selection widget. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DDATE_PICKER_PRIVATE_H #define DIGIKAM_DDATE_PICKER_PRIVATE_H // Qt includes #include #include #include #include #include #include #include #include // Local includes #include "ddatepicker.h" namespace Digikam { class Q_DECL_HIDDEN DatePickerValidator : public QValidator { public: explicit DatePickerValidator(DDatePicker* const parent); State validate(QString& text, int&) const override; private: DDatePicker* m_picker; }; // ------------------------------------------------------------------------------ class Q_DECL_HIDDEN DatePickerYearSelector : public QLineEdit { Q_OBJECT public: explicit DatePickerYearSelector(const QDate& currentDate, QWidget* const parent = nullptr); int year() const; void setYear(int year); public Q_SLOTS: void yearEnteredSlot(); Q_SIGNALS: void closeMe(int); protected: QIntValidator* val; int result; private: QDate oldDate; Q_DISABLE_COPY(DatePickerYearSelector) }; // ------------------------------------------------------------------------------ class Q_DECL_HIDDEN DDatePicker::Private { public: explicit Private(DDatePicker* const qq); void fillWeeksCombo(); QDate validDateInYearMonth(int year, int month); public: /// the date table DDatePicker* q; QToolButton* closeButton; QComboBox* selectWeek; QToolButton* todayButton; QBoxLayout* navigationLayout; /// the year forward button QToolButton* yearForward; + /// the year backward button QToolButton* yearBackward; + /// the month forward button QToolButton* monthForward; + /// the month backward button QToolButton* monthBackward; + /// the button for selecting the month directly QToolButton* selectMonth; + /// the button for selecting the year directly QToolButton* selectYear; + /// the line edit to enter the date directly QLineEdit* line; + /// the validator for the line edit: DatePickerValidator* val; + /// the date table DDateTable* table; + /// the widest month string in pixels: QSize maxMonthRect; /// the font size for the widget int fontsize; }; } // namespace Digikam #endif // DIGIKAM_DDATE_PICKER_PRIVATE_H diff --git a/core/app/date/ddatepickerpopup.cpp b/core/app/date/ddatepickerpopup.cpp index 36390b52fd..119dc487b4 100644 --- a/core/app/date/ddatepickerpopup.cpp +++ b/core/app/date/ddatepickerpopup.cpp @@ -1,249 +1,250 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2004-04-21 * Description : a menu widget to pick a date. * * Copyright (C) 2004 by Bram Schoenmakers * Copyright (C) 2006 by Mikolaj Machowski * Copyright (C) 2011-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "ddatepickerpopup.h" // Qt includes #include #include // KDE includes #include namespace Digikam { class Q_DECL_HIDDEN DDatePickerAction : public QWidgetAction { public: DDatePickerAction(DDatePicker* const widget, QObject* const parent) : QWidgetAction(parent), m_datePicker(widget), m_originalParent(widget->parentWidget()) { } protected: QWidget* createWidget(QWidget* const parent) { m_datePicker->setParent(parent); + return m_datePicker; } void deleteWidget(QWidget* const widget) { if (widget != m_datePicker) { return; } m_datePicker->setParent(m_originalParent); } private: DDatePicker* m_datePicker; QWidget* m_originalParent; }; // --------------------------------------------------------------------------------------- class Q_DECL_HIDDEN DDatePickerPopup::Private { public: explicit Private() : datePicker(nullptr) { } DDatePicker* datePicker; Items items; }; DDatePickerPopup::DDatePickerPopup(Items items, const QDate& date, QWidget* const parent) : QMenu(parent), d(new Private) { d->items = items; d->datePicker = new DDatePicker(this); d->datePicker->setCloseButton(false); connect(d->datePicker, &DDatePicker::dateEntered, this, &DDatePickerPopup::slotDateChanged); connect(d->datePicker, &DDatePicker::dateSelected, this, &DDatePickerPopup::slotDateChanged); d->datePicker->setDate(date); buildMenu(); } DDatePickerPopup::~DDatePickerPopup() { delete d; } void DDatePickerPopup::buildMenu() { if (isVisible()) { return; } clear(); if (d->items & DatePicker) { addAction(new DDatePickerAction(d->datePicker, this)); if ((d->items & NoDate ) || (d->items & Words)) { addSeparator(); } } if (d->items & Words) { addAction(i18n("&Today"), this, SLOT(slotToday())); addAction(i18n("To&morrow"), this, SLOT(slotTomorrow())); addAction(i18n("Next &Week"), this, SLOT(slotNextWeek())); addAction(i18n("Next M&onth"), this, SLOT(slotNextMonth())); addAction(i18n("Y&esterday"), this, SLOT(slotYesterday())); addAction(i18n("Last &Monday"), this, SLOT(slotPrevMonday())); addAction(i18n("Last &Friday"), this, SLOT(slotPrevFriday())); addAction(i18n("Last &Week"), this, SLOT(slotPrevWeek())); addAction(i18n("Last M&onth"), this, SLOT(slotPrevMonth())); if (d->items & NoDate) { addSeparator(); } } if (d->items & NoDate) { addAction(i18n("No Date"), this, SLOT(slotNoDate())); } } DDatePicker* DDatePickerPopup::datePicker() const { return d->datePicker; } void DDatePickerPopup::setDate(const QDate& date) { d->datePicker->setDate(date); } #if 0 void DDatePickerPopup::setItems(int items) { d->items = items; buildMenu(); } #endif int DDatePickerPopup::items() const { return d->items; } void DDatePickerPopup::slotDateChanged(const QDate& date) { emit dateChanged(date); hide(); } void DDatePickerPopup::slotToday() { emit dateChanged(QDate::currentDate()); } void DDatePickerPopup::slotTomorrow() { emit dateChanged(QDate::currentDate().addDays(1)); } void DDatePickerPopup::slotNoDate() { emit dateChanged(QDate()); } void DDatePickerPopup::slotNextWeek() { emit dateChanged(QDate::currentDate().addDays(7)); } void DDatePickerPopup::slotNextMonth() { emit dateChanged(QDate::currentDate().addMonths(1)); } void DDatePickerPopup::slotYesterday() { emit dateChanged(QDate::currentDate().addDays(-1)); } void DDatePickerPopup::slotPrevFriday() { QDate date = QDate::currentDate(); int day = date.dayOfWeek(); if (day < 6) { date = date.addDays(5 - 7 - day); } else { date = date.addDays(5 - day); } emit dateChanged(date); } void DDatePickerPopup::slotPrevMonday() { QDate date = QDate::currentDate(); emit dateChanged(date.addDays(1 - date.dayOfWeek())); } void DDatePickerPopup::slotPrevWeek() { emit dateChanged(QDate::currentDate().addDays(-7)); } void DDatePickerPopup::slotPrevMonth() { emit dateChanged(QDate::currentDate().addMonths(-1)); } } // namespace Digikam