File indexing completed on 2024-05-12 05:14:39
0001 /* 0002 * datepicker.cpp - date chooser widget 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2021-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "datepicker.h" 0010 0011 #include "daymatrix.h" 0012 #include "functions.h" 0013 #include "preferences.h" 0014 #include "lib/locale.h" 0015 #include "lib/synchtimer.h" 0016 #include "kalarmcalendar/kadatetime.h" 0017 0018 #include <KLocalizedString> 0019 0020 #include <QLabel> 0021 #include <QToolButton> 0022 #include <QGridLayout> 0023 #include <QHBoxLayout> 0024 #include <QVBoxLayout> 0025 #include <QLocale> 0026 #include <QWheelEvent> 0027 #include <QApplication> 0028 0029 class DPToolButton : public QToolButton 0030 { 0031 Q_OBJECT 0032 public: 0033 DPToolButton(QWidget* parent) : QToolButton(parent) {} 0034 QSize sizeHint() const override 0035 { 0036 QSize s = QToolButton::sizeHint(); 0037 return QSize(s.width() * 4 / 5, s.height()); 0038 } 0039 QSize minimumSizeHint() const override 0040 { 0041 QSize s = QToolButton::minimumSizeHint(); 0042 return QSize(s.width() * 4 / 5, s.height()); 0043 } 0044 }; 0045 0046 0047 DatePicker::DatePicker(QWidget* parent) 0048 : QWidget(parent) 0049 { 0050 const QString whatsThis = i18nc("@info:whatsthis", "Select dates to show in the alarm list. Only alarms due on these dates will be shown."); 0051 0052 QVBoxLayout* topLayout = new QVBoxLayout(this); 0053 const int spacing = topLayout->spacing(); 0054 topLayout->setSpacing(0); 0055 0056 QLabel* label = new QLabel(i18nc("@title:group", "Alarm Date Selector"), this); 0057 label->setAlignment(Qt::AlignCenter); 0058 label->setWordWrap(true); 0059 label->setWhatsThis(whatsThis); 0060 topLayout->addWidget(label, 0, Qt::AlignHCenter); 0061 topLayout->addSpacing(spacing); 0062 0063 // Set up the month/year navigation buttons at the top. 0064 QHBoxLayout* hlayout = new QHBoxLayout; 0065 hlayout->setContentsMargins(0, 0, 0, 0); 0066 topLayout->addLayout(hlayout); 0067 0068 // Use Breeze icon names if possible, otherwise default to Oxygen names. 0069 const bool useArrows = !QIcon::hasThemeIcon(QStringLiteral("go-next-skip")); 0070 DPToolButton* leftYear = createArrowButton(useArrows, QStringLiteral("go-previous-skip"), QStringLiteral("arrow-left-double")); 0071 DPToolButton* leftMonth = createArrowButton(useArrows, QStringLiteral("go-previous"), QStringLiteral("arrow-left")); 0072 DPToolButton* rightMonth = createArrowButton(useArrows, QStringLiteral("go-next"), QStringLiteral("arrow-right")); 0073 DPToolButton* rightYear = createArrowButton(useArrows, QStringLiteral("go-next-skip"), QStringLiteral("arrow-right-double")); 0074 DPToolButton* today = createArrowButton(QStringLiteral("go-jump-today")); 0075 0076 mPrevYear = leftYear; 0077 mPrevMonth = leftMonth; 0078 mNextYear = rightYear; 0079 mNextMonth = rightMonth; 0080 mToday = today; 0081 if (QApplication::isRightToLeft()) 0082 { 0083 mPrevYear = rightYear; 0084 mPrevMonth = rightMonth; 0085 mNextYear = leftYear; 0086 mNextMonth = leftMonth; 0087 } 0088 mPrevYear->setToolTip(i18nc("@info:tooltip", "Show the previous year")); 0089 mPrevMonth->setToolTip(i18nc("@info:tooltip", "Show the previous month")); 0090 mNextYear->setToolTip(i18nc("@info:tooltip", "Show the next year")); 0091 mNextMonth->setToolTip(i18nc("@info:tooltip", "Show the next month")); 0092 mToday->setToolTip(i18nc("@info:tooltip", "Show today")); 0093 connect(mPrevYear, &QToolButton::clicked, this, &DatePicker::prevYearClicked); 0094 connect(mPrevMonth, &QToolButton::clicked, this, &DatePicker::prevMonthClicked); 0095 connect(mNextYear, &QToolButton::clicked, this, &DatePicker::nextYearClicked); 0096 connect(mNextMonth, &QToolButton::clicked, this, &DatePicker::nextMonthClicked); 0097 connect(mToday, &QToolButton::clicked, this, &DatePicker::todayClicked); 0098 0099 const QDate currentDate = KADateTime::currentDateTime(Preferences::timeSpec()).date(); 0100 mMonthYear = new QLabel(this); 0101 mMonthYear->setAlignment(Qt::AlignCenter); 0102 QLocale locale; 0103 QDate d(currentDate.year(), 1, 1); 0104 int maxWidth = 0; 0105 for (int i = 1; i <= 12; ++i) 0106 { 0107 mMonthYear->setText(locale.toString(d, QStringLiteral("MMM yyyy"))); 0108 maxWidth = std::max(maxWidth, mMonthYear->minimumSizeHint().width()); 0109 d = d.addMonths(1); 0110 } 0111 mMonthYear->setMinimumWidth(maxWidth); 0112 0113 if (QApplication::isRightToLeft()) 0114 hlayout->addWidget(mToday); 0115 hlayout->addWidget(mPrevYear); 0116 hlayout->addWidget(mPrevMonth); 0117 hlayout->addStretch(); 0118 hlayout->addWidget(mMonthYear); 0119 hlayout->addStretch(); 0120 hlayout->addWidget(mNextMonth); 0121 hlayout->addWidget(mNextYear); 0122 if (!QApplication::isRightToLeft()) 0123 hlayout->addWidget(mToday); 0124 0125 // Set up the day name headings. 0126 // These start at the user's start day of the week. 0127 QWidget* widget = new QWidget(this); // this is to control the QWhatsThis text display area 0128 widget->setWhatsThis(whatsThis); 0129 topLayout->addWidget(widget); 0130 QVBoxLayout* vlayout = new QVBoxLayout(widget); 0131 vlayout->setContentsMargins(0, 0, 0, 0); 0132 QGridLayout* grid = new QGridLayout; 0133 grid->setSpacing(0); 0134 grid->setContentsMargins(0, 0, 0, 0); 0135 vlayout->addLayout(grid); 0136 mDayNames = new QLabel[7]; 0137 maxWidth = 0; 0138 for (int i = 0; i < 7; ++i) 0139 { 0140 const int day = Locale::localeDayInWeek_to_weekDay(i); 0141 mDayNames[i].setText(locale.dayName(day, QLocale::ShortFormat)); 0142 mDayNames[i].setAlignment(Qt::AlignCenter); 0143 maxWidth = std::max(maxWidth, mDayNames[i].minimumSizeHint().width()); 0144 grid->addWidget(&mDayNames[i], 0, i, 1, 1, Qt::AlignCenter); 0145 } 0146 for (int i = 0; i < 7; ++i) 0147 mDayNames[i].setMinimumWidth(maxWidth); 0148 0149 mDayMatrix = new DayMatrix(widget); 0150 mDayMatrix->setWhatsThis(whatsThis); 0151 vlayout->addWidget(mDayMatrix); 0152 mDayMatrix->installEventFilter(this); 0153 connect(mDayMatrix, &DayMatrix::selected, this, &DatePicker::datesSelected); 0154 connect(mDayMatrix, &DayMatrix::newAlarm, this, &DatePicker::slotNewAlarm); 0155 connect(mDayMatrix, &DayMatrix::newAlarmFromTemplate, this, &DatePicker::slotNewAlarmFromTemplate); 0156 0157 // Initialise the display. 0158 mMonthShown.setDate(currentDate.year(), currentDate.month(), 1); 0159 newMonthShown(); 0160 updateDisplay(); 0161 0162 MidnightTimer::connect(this, SLOT(updateToday())); 0163 } 0164 0165 DatePicker::~DatePicker() 0166 { 0167 delete[] mDayNames; 0168 } 0169 0170 QList<QDate> DatePicker::selectedDates() const 0171 { 0172 return mDayMatrix->selectedDates(); 0173 } 0174 0175 void DatePicker::clearSelection() 0176 { 0177 mDayMatrix->clearSelection(); 0178 } 0179 0180 /****************************************************************************** 0181 * Called when the widget is shown. Set the row height for the day matrix. 0182 */ 0183 void DatePicker::showEvent(QShowEvent* e) 0184 { 0185 mDayMatrix->setRowHeight(mDayNames[0].height()); 0186 QWidget::showEvent(e); 0187 } 0188 0189 /****************************************************************************** 0190 * Receives events destined for the day matrix. 0191 */ 0192 bool DatePicker::eventFilter(QObject* obj, QEvent* e) 0193 { 0194 if (obj == mDayMatrix) 0195 { 0196 if (e->type() == QEvent::Wheel) 0197 { 0198 auto* we = (QWheelEvent*)e; 0199 if (we->angleDelta().y() > 0) 0200 prevMonthClicked(); 0201 else 0202 nextMonthClicked(); 0203 return true; 0204 } 0205 return false; 0206 } 0207 return QWidget::eventFilter(obj, e); 0208 } 0209 0210 /****************************************************************************** 0211 * Called when the previous year arrow button has been clicked. 0212 */ 0213 void DatePicker::prevYearClicked() 0214 { 0215 newMonthShown(); 0216 if (mPrevYear->isEnabled()) 0217 { 0218 mMonthShown = mMonthShown.addYears(-1); 0219 newMonthShown(); 0220 updateDisplay(); 0221 } 0222 } 0223 0224 /****************************************************************************** 0225 * Called when the previous month arrow button has been clicked. 0226 */ 0227 void DatePicker::prevMonthClicked() 0228 { 0229 newMonthShown(); 0230 if (mPrevMonth->isEnabled()) 0231 { 0232 mMonthShown = mMonthShown.addMonths(-1); 0233 newMonthShown(); 0234 updateDisplay(); 0235 } 0236 } 0237 0238 /****************************************************************************** 0239 * Called when the next year arrow button has been clicked. 0240 */ 0241 void DatePicker::nextYearClicked() 0242 { 0243 mMonthShown = mMonthShown.addYears(1); 0244 newMonthShown(); 0245 updateDisplay(); 0246 } 0247 0248 /****************************************************************************** 0249 * Called when the next month arrow button has been clicked. 0250 */ 0251 void DatePicker::nextMonthClicked() 0252 { 0253 mMonthShown = mMonthShown.addMonths(1); 0254 newMonthShown(); 0255 updateDisplay(); 0256 } 0257 0258 /****************************************************************************** 0259 * Called when the today button has been clicked. 0260 */ 0261 void DatePicker::todayClicked() 0262 { 0263 const QDate currentDate = KADateTime::currentDateTime(Preferences::timeSpec()).date(); 0264 const QDate monthToShow(currentDate.year(), currentDate.month(), 1); 0265 if (monthToShow != mMonthShown) 0266 { 0267 mMonthShown = monthToShow; 0268 newMonthShown(); 0269 updateDisplay(); 0270 } 0271 } 0272 0273 /****************************************************************************** 0274 * Called at midnight. If the month has changed, update the view. 0275 */ 0276 void DatePicker::updateToday() 0277 { 0278 const QDate currentDate = KADateTime::currentDateTime(Preferences::timeSpec()).date(); 0279 const QDate monthToShow(currentDate.year(), currentDate.month(), 1); 0280 if (monthToShow > mMonthShown) 0281 { 0282 mMonthShown = monthToShow; 0283 newMonthShown(); 0284 updateDisplay(); 0285 } 0286 else 0287 mDayMatrix->updateToday(currentDate); 0288 } 0289 0290 /****************************************************************************** 0291 * Called when a new month is shown, to enable/disable 'previous' arrow buttons. 0292 */ 0293 void DatePicker::newMonthShown() 0294 { 0295 QLocale locale; 0296 mMonthYear->setText(locale.toString(mMonthShown, QStringLiteral("MMM yyyy"))); 0297 0298 const QDate currentDate = KADateTime::currentDateTime(Preferences::timeSpec()).date(); 0299 mPrevMonth->setEnabled(mMonthShown > currentDate); 0300 mPrevYear->setEnabled(mMonthShown.addMonths(-11) > currentDate); 0301 } 0302 0303 /****************************************************************************** 0304 * Called when the "New Alarm" menu item is selected to edit a new alarm. 0305 */ 0306 void DatePicker::slotNewAlarm(EditAlarmDlg::Type type) 0307 { 0308 const QList<QDate> selectedDates = mDayMatrix->selectedDates(); 0309 const QDate startDate = selectedDates.isEmpty() ? QDate() : selectedDates[0]; 0310 KAlarm::editNewAlarm(type, startDate); 0311 } 0312 0313 /****************************************************************************** 0314 * Called when the "New Alarm" menu item is selected to edit a new alarm from a 0315 * template. 0316 */ 0317 void DatePicker::slotNewAlarmFromTemplate(const KAEvent& event) 0318 { 0319 const QList<QDate> selectedDates = mDayMatrix->selectedDates(); 0320 const QDate startDate = selectedDates.isEmpty() ? QDate() : selectedDates[0]; 0321 KAlarm::editNewAlarm(event, startDate); 0322 } 0323 0324 /****************************************************************************** 0325 * Update the days shown. 0326 */ 0327 void DatePicker::updateDisplay() 0328 { 0329 const int firstDay = Locale::weekDay_to_localeDayInWeek(mMonthShown.dayOfWeek()); 0330 mStartDate = mMonthShown.addDays(-firstDay); 0331 mDayMatrix->setStartDate(mStartDate); 0332 mDayMatrix->update(); 0333 mDayMatrix->repaint(); 0334 } 0335 0336 /****************************************************************************** 0337 * Create an arrow button for moving backwards or forwards. 0338 */ 0339 DPToolButton* DatePicker::createArrowButton(const QString& iconId) 0340 { 0341 return createArrowButton(false, iconId); 0342 } 0343 DPToolButton* DatePicker::createArrowButton(bool useArrows, const QString& iconId, const QString& arrowIconId) 0344 { 0345 DPToolButton* button = new DPToolButton(this); 0346 button->setIcon(QIcon::fromTheme(useArrows ? arrowIconId : iconId)); 0347 button->setToolButtonStyle(Qt::ToolButtonIconOnly); 0348 button->setAutoRaise(true); 0349 return button; 0350 } 0351 0352 #include "datepicker.moc" 0353 #include "moc_datepicker.cpp" 0354 0355 // vim: et sw=4: