1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 1997-04-21
7 * Description : A date selection widget.
8 *
9 * Copyright (C) 2011-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 * Copyright (C) 1997 by Tim D. Gilman <tdgilman at best dot org>
11 * Copyright (C) 1998-2001 by Mirko Boehm <mirko at kde dot org>
12 * Copyright (C) 2007 by John Layt <john at layt dot net>
13 *
14 * This program is free software; you can redistribute it
15 * and/or modify it under the terms of the GNU General
16 * Public License as published by the Free Software Foundation;
17 * either version 2, or (at your option)
18 * any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * ============================================================ */
26
27 #include "ddatepicker_p.h"
28
29 // Qt includes
30
31 #include <QFontDatabase>
32 #include <QApplication>
33
34 // KDE includes
35
36 #include <klocalizedstring.h>
37
38 namespace Digikam
39 {
40
DatePickerValidator(DDatePicker * const parent)41 DatePickerValidator::DatePickerValidator(DDatePicker* const parent)
42 : QValidator(parent),
43 m_picker (parent)
44 {
45 }
46
validate(QString & text,int &) const47 QValidator::State DatePickerValidator::validate(QString& text, int&) const
48 {
49 QLocale::FormatType formats[] =
50 {
51 QLocale::LongFormat,
52 QLocale::ShortFormat,
53 QLocale::NarrowFormat
54 };
55
56 QLocale locale = m_picker->locale();
57
58 for (int i = 0 ; i < 3 ; ++i)
59 {
60 QDate tmp = locale.toDate(text, formats[i]);
61
62 if (tmp.isValid())
63 {
64 return Acceptable;
65 }
66 }
67
68 return QValidator::Intermediate;
69 }
70
71 // ------------------------------------------------------------------------------
72
73 /**
74 * NOTE: Week numbers are defined by ISO 8601
75 * See https://en.wikipedia.org/wiki/Week#Week_numbering for details
76 */
DatePickerYearSelector(const QDate & currentDate,QWidget * const parent)77 DatePickerYearSelector::DatePickerYearSelector(const QDate& currentDate, QWidget* const parent)
78 : QLineEdit(parent),
79 val (new QIntValidator(this)),
80 result (0),
81 oldDate (currentDate)
82 {
83 setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
84
85 setFrame(false);
86
87 /*
88 TODO: Find a way to get that from QLocale
89 val->setRange(calendar->year(calendar->earliestValidDate()),
90 calendar->year(calendar->latestValidDate()));
91 */
92 setValidator(val);
93
94 connect(this, &QLineEdit::returnPressed,
95 this, &DatePickerYearSelector::yearEnteredSlot);
96 }
97
yearEnteredSlot()98 void DatePickerYearSelector::yearEnteredSlot()
99 {
100 bool ok;
101 int newYear;
102
103 // check if entered value is a number
104
105 newYear = text().toInt(&ok);
106
107 if (!ok)
108 {
109 QApplication::beep();
110 return;
111 }
112
113 // check if new year will lead to a valid date
114
115 if (QDate(newYear, oldDate.month(), oldDate.day()).isValid())
116 {
117 result = newYear;
118 emit closeMe(1);
119 }
120 else
121 {
122 QApplication::beep();
123 }
124 }
125
year() const126 int DatePickerYearSelector::year() const
127 {
128 return result;
129 }
130
setYear(int year)131 void DatePickerYearSelector::setYear(int year)
132 {
133 setText(QString::number(year));
134 }
135
136 // ------------------------------------------------------------------------------
137
Private(DDatePicker * const qq)138 DDatePicker::Private::Private(DDatePicker* const qq)
139 : q (qq),
140 closeButton (nullptr),
141 selectWeek (nullptr),
142 todayButton (nullptr),
143 navigationLayout (nullptr),
144 yearForward (nullptr),
145 yearBackward (nullptr),
146 monthForward (nullptr),
147 monthBackward (nullptr),
148 selectMonth (nullptr),
149 selectYear (nullptr),
150 line (nullptr),
151 val (nullptr),
152 table (nullptr),
153 fontsize (0)
154 {
155 }
156
fillWeeksCombo()157 void DDatePicker::Private::fillWeeksCombo()
158 {
159 /**
160 * NOTE: every year can have a different number of weeks
161 * it could be that we had 53,1..52 and now 1..53 which is the same number but different
162 * so always fill with new values
163 * We show all week numbers for all weeks between first day of year to last day of year
164 * This of course can be a list like 53,1,2..52
165 */
166 const QDate thisDate = q->date();
167 const int thisYear = thisDate.year();
168 QDate day(thisDate.year(), 1, 1);
169 const QDate lastDayOfYear = QDate(thisDate.year() + 1, 1, 1).addDays(-1);
170
171 selectWeek->clear();
172
173 // Starting from the first day in the year, loop through the year a week at a time
174 // adding an entry to the week combo for each week in the year
175
176 for ( ; day.isValid() && (day <= lastDayOfYear) ; (day = day.addDays(7)))
177 {
178 // Get the ISO week number for the current day and what year that week is in
179 // e.g. 1st day of this year may fall in week 53 of previous year
180
181 int weekYear = thisYear;
182 const int week = day.weekNumber(&weekYear);
183 QString weekString = i18n("Week %1", week);
184
185 // show that this is a week from a different year
186
187 if (weekYear != thisYear)
188 {
189 weekString += QLatin1Char('*');
190 }
191
192 // when the week is selected, go to the same weekday as the one
193 // that is currently selected in the date table
194
195 QDate targetDate = day.addDays(thisDate.dayOfWeek() - day.dayOfWeek());
196 selectWeek->addItem(weekString, targetDate);
197
198 // make sure that the week of the lastDayOfYear is always inserted: in Chinese calendar
199 // system, this is not always the case
200
201 if (
202 (day < lastDayOfYear) &&
203 (day.daysTo(lastDayOfYear) < 7) &&
204 (lastDayOfYear.weekNumber() != day.weekNumber())
205 )
206 {
207 day = lastDayOfYear.addDays(-7);
208 }
209 }
210 }
211
validDateInYearMonth(int year,int month)212 QDate DDatePicker::Private::validDateInYearMonth(int year, int month)
213 {
214 QDate newDate;
215
216 // Try to create a valid date in this year and month
217 // First try the first of the month, then try last of month
218
219 if (QDate(year, month, 1).isValid())
220 {
221 newDate = QDate(year, month, 1);
222 }
223 else if (QDate(year, month + 1, 1).isValid())
224 {
225 newDate = QDate(year, month + 1, 1).addDays(-1);
226 }
227 else
228 {
229 newDate = QDate::fromJulianDay(0);
230 }
231
232 return newDate;
233 }
234
235 } // namespace Digikam
236