File indexing completed on 2024-05-12 05:09:47

0001 /***************************************************************************
0002     Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 // this class borrows heavily from kdateedit.h in the kdepim module
0026 // which is Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
0027 // and published under the LGPL
0028 
0029 #include "datewidget.h"
0030 #include "spinbox.h"
0031 
0032 #include <KComboBox>
0033 #include <KDatePicker>
0034 
0035 #include <QPushButton>
0036 #include <QHBoxLayout>
0037 #include <QFrame>
0038 #include <QDate>
0039 #include <QEvent>
0040 #include <QMenu>
0041 #include <QWidgetAction>
0042 #include <QApplication>
0043 #include <QDesktopWidget>
0044 
0045 using Tellico::GUI::DateWidget;
0046 
0047 class DateWidget::DatePickerAction : public QWidgetAction
0048 {
0049   Q_OBJECT
0050 
0051   public:
0052     DatePickerAction( KDatePicker *widget, QObject *parent )
0053       : QWidgetAction( parent ),
0054         mDatePicker( widget ), mOriginalParent( widget->parentWidget() )
0055     {
0056     }
0057 
0058   protected:
0059     QWidget *createWidget( QWidget *parent ) Q_DECL_OVERRIDE
0060     {
0061       mDatePicker->setParent( parent );
0062       return mDatePicker;
0063     }
0064 
0065     void deleteWidget( QWidget *widget ) Q_DECL_OVERRIDE
0066     {
0067       if ( widget != mDatePicker ) {
0068         return;
0069       }
0070 
0071       mDatePicker->setParent( mOriginalParent );
0072     }
0073 
0074   private:
0075     KDatePicker *mDatePicker;
0076     QWidget *mOriginalParent;
0077 };
0078 
0079 DateWidget::DateWidget(QWidget* parent_) : QWidget(parent_) {
0080   QBoxLayout* l = new QHBoxLayout(this);
0081   l->setContentsMargins(0, 0, 0, 0);
0082 
0083   // 0 allows empty value
0084   m_daySpin = new SpinBox(0, 31, this);
0085   l->addWidget(m_daySpin, 1);
0086   l->setStretchFactor(m_daySpin, 1);
0087 
0088   m_monthCombo = new KComboBox(false, this);
0089   l->addWidget(m_monthCombo, 1);
0090   l->setStretchFactor(m_monthCombo, 1);
0091   // allow empty item
0092   m_monthCombo->addItem(QString());
0093   for(int i = 1; ; ++i) {
0094     QString str = QLocale().standaloneMonthName(i, QLocale::LongFormat);
0095     if(str.isEmpty()) {
0096       break;
0097     }
0098     m_monthCombo->addItem(str);
0099   }
0100 
0101   // the min year is the value when the year is empty
0102   const int minYear = QDate::fromJulianDay(0).year() - 1;
0103   m_yearSpin = new SpinBox(minYear, 9999, this);
0104   m_yearSpin->setValue(minYear);
0105   l->addWidget(m_yearSpin, 1);
0106   l->setStretchFactor(m_yearSpin, 1);
0107 
0108   void (SpinBox::* valueChangedInt)(int) = &SpinBox::valueChanged;
0109   void (KComboBox::* indexChanged)(int) = &KComboBox::currentIndexChanged;
0110   connect(m_daySpin, valueChangedInt, this, &DateWidget::slotDateChanged);
0111   connect(m_monthCombo, indexChanged, this, &DateWidget::slotDateChanged);
0112   connect(m_yearSpin, valueChangedInt, this, &DateWidget::slotDateChanged);
0113 
0114   m_dateButton = new QPushButton(this);
0115   m_dateButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-calendar")));
0116   connect(m_dateButton, &QAbstractButton::clicked, this, &DateWidget::slotShowPicker);
0117   l->addWidget(m_dateButton, 0);
0118 
0119   m_menu = new QMenu(this);
0120   m_menu->hide();
0121 
0122   m_picker = new KDatePicker(m_menu);
0123   m_picker->setCloseButton(false);
0124   connect(m_picker, &KDatePicker::dateEntered, this, &DateWidget::slotDateEntered);
0125   connect(m_picker, &KDatePicker::dateSelected, this, &DateWidget::slotDateSelected);
0126 
0127   m_menu->addAction(new DatePickerAction(m_picker, m_menu));
0128 }
0129 
0130 DateWidget::~DateWidget() {
0131 }
0132 
0133 void DateWidget::slotDateChanged() {
0134   int day = m_daySpin->value();
0135   day = qMin(qMax(day, m_daySpin->minimum()), m_daySpin->maximum());
0136 
0137   int m = m_monthCombo->currentIndex();
0138   m = qMin(qMax(m, 0), m_monthCombo->count()-1);
0139 
0140   int y = m_yearSpin->value();
0141   y = qMin(qMax(y, m_yearSpin->minimum()), m_yearSpin->maximum());
0142 
0143   // if all are valid, set this date
0144   if(day > m_daySpin->minimum() && m > 0 && y > m_yearSpin->minimum()) {
0145     QDate d(y, m, day);
0146     setDate(d);
0147   }
0148   emit signalModified();
0149 }
0150 
0151 QDate DateWidget::date() const {
0152   // possible for either day, month, or year to be empty
0153   // in which case a null date is returned
0154   int day = m_daySpin->value();
0155   // min value is the empty one
0156   if(day == m_daySpin->minimum()) {
0157     return QDate();
0158   }
0159   int month = m_monthCombo->currentIndex();
0160   if(month == 0) {
0161     return QDate();
0162   }
0163   int year = m_yearSpin->value();
0164   if(year == m_yearSpin->minimum()) {
0165     return QDate();
0166   }
0167   return QDate(year, month, day);
0168 }
0169 
0170 QString DateWidget::text() const {
0171   // possible for either day, month, or year to be empty
0172   // but not all three
0173   bool empty = true;
0174   // format is "year-month-day"
0175   QString s;
0176   if(m_yearSpin->value() > m_yearSpin->minimum()) {
0177     s += QString::number(m_yearSpin->value());
0178     empty = false;
0179   }
0180   s += QLatin1Char('-');
0181   // first item is empty
0182   if(m_monthCombo->currentIndex() > 0) {
0183     // zero-pad to two digits
0184     if(m_monthCombo->currentIndex() < 10) {
0185       s += QLatin1Char('0');
0186     }
0187     s += QString::number(m_monthCombo->currentIndex());
0188     empty = false;
0189   }
0190   s += QLatin1Char('-');
0191   if(m_daySpin->value() > m_daySpin->minimum()) {
0192     // zero-pad to two digits
0193     if(m_daySpin->value() < 10) {
0194       s += QLatin1Char('0');
0195     }
0196     s += QString::number(m_daySpin->value());
0197     empty = false;
0198   }
0199   return empty ? QString() : s;
0200 }
0201 
0202 void DateWidget::setDate(const QDate& date_) {
0203   const QDate oldDate = date();
0204 
0205   m_daySpin->blockSignals(true);
0206   m_monthCombo->blockSignals(true);
0207   m_yearSpin->blockSignals(true);
0208 
0209   m_daySpin->setMaximum(date_.daysInMonth());
0210   m_daySpin->setValue(date_.day());
0211   m_monthCombo->setCurrentIndex(date_.month()); // don't subtract 1 since there's the blank first item
0212   m_yearSpin->setValue(date_.year());
0213 
0214   m_daySpin->blockSignals(false);
0215   m_monthCombo->blockSignals(false);
0216   m_yearSpin->blockSignals(false);
0217 
0218   if(oldDate != date_) {
0219     emit signalModified();
0220   }
0221 }
0222 
0223 void DateWidget::setDate(const QString& date_) {
0224   m_daySpin->blockSignals(true);
0225   m_monthCombo->blockSignals(true);
0226   m_yearSpin->blockSignals(true);
0227 
0228   QStringList s = date_.split(QLatin1Char('-'));
0229   bool ok = true;
0230   int y = s.count() > 0 ? s[0].toInt(&ok) : m_yearSpin->minimum();
0231   if(!ok) {
0232     y = m_yearSpin->minimum();
0233     ok = true;
0234   }
0235   y = qMin(qMax(y, m_yearSpin->minimum()), m_yearSpin->maximum());
0236   m_yearSpin->setValue(y);
0237 
0238   int m = s.count() > 1 ? s[1].toInt(&ok) : 0;
0239   if(!ok) {
0240     m = 0;
0241     ok = true;
0242   }
0243   m = qMin(qMax(m, 0), m_monthCombo->count()-1);
0244   m_monthCombo->setCurrentIndex(m);
0245 
0246   // need to update number of days in month
0247   // for now set date to 1
0248   QDate date(y, (m == 0 ? 1 : m), 1);
0249   m_daySpin->setMaximum(date.daysInMonth());
0250 
0251   int day = s.count() > 2 ? s[2].toInt(&ok) : m_daySpin->minimum();
0252   if(!ok) {
0253     day = m_daySpin->minimum();
0254   }
0255   day = qMin(qMax(day, m_daySpin->minimum()), m_daySpin->maximum());
0256   m_daySpin->setValue(day);
0257 
0258   m_daySpin->blockSignals(false);
0259   m_monthCombo->blockSignals(false);
0260   m_yearSpin->blockSignals(false);
0261 
0262   // if all are valid, set this date
0263   if(day > m_daySpin->minimum() && m > 0 && y > m_yearSpin->minimum()) {
0264     QDate d(y, m, day);
0265     m_picker->blockSignals(true);
0266     m_picker->setDate(d);
0267     m_picker->blockSignals(false);
0268   }
0269 }
0270 
0271 void DateWidget::clear() {
0272   m_daySpin->blockSignals(true);
0273   m_monthCombo->blockSignals(true);
0274   m_yearSpin->blockSignals(true);
0275   m_picker->blockSignals(true);
0276 
0277   m_daySpin->setValue(m_daySpin->minimum());
0278   m_monthCombo->setCurrentIndex(0);
0279   m_yearSpin->setValue(m_yearSpin->minimum());
0280   m_picker->setDate(QDate::currentDate());
0281 
0282   m_daySpin->blockSignals(false);
0283   m_monthCombo->blockSignals(false);
0284   m_yearSpin->blockSignals(false);
0285   m_picker->blockSignals(false);
0286 }
0287 
0288 void DateWidget::slotShowPicker() {
0289   QRect desk = QApplication::desktop()->screenGeometry(this);
0290   QPoint popupPoint = mapToGlobal(QPoint(0, 0));
0291 
0292   int dateFrameHeight = m_menu->sizeHint().height();
0293   if(popupPoint.y() + height() + dateFrameHeight > desk.bottom()) {
0294     popupPoint.setY(popupPoint.y() - dateFrameHeight);
0295   } else {
0296     popupPoint.setY(popupPoint.y() + height());
0297   }
0298   int dateFrameWidth = m_menu->sizeHint().width();
0299   if(popupPoint.x() + width() > desk.right()) {
0300     popupPoint.setX(desk.right() - dateFrameWidth);
0301   } else {
0302     popupPoint.setX(popupPoint.x() + width() - dateFrameWidth);
0303   }
0304 
0305   if(popupPoint.x() < desk.left()) {
0306     popupPoint.setX( desk.left());
0307   }
0308   if(popupPoint.y() < desk.top()) {
0309     popupPoint.setY(desk.top());
0310   }
0311 
0312   QDate d = date();
0313   if(d.isValid()) {
0314     m_picker->setDate(d);
0315   }
0316 
0317   m_menu->popup(popupPoint);
0318 }
0319 
0320 void DateWidget::slotDateSelected(QDate date_) {
0321   if(date_.isValid()) {
0322     setDate(date_);
0323     m_menu->hide();
0324   }
0325 }
0326 
0327 void DateWidget::slotDateEntered(QDate date_) {
0328   if(date_.isValid()) {
0329     setDate(date_);
0330   }
0331 }
0332 
0333 #include "datewidget.moc"