1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Labs Calendar module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qquickmonthgrid_p.h"
38#include "qquickmonthmodel_p.h"
39
40#include <QtGui/qstylehints.h>
41#include <QtGui/qguiapplication.h>
42#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
43#include <QtQml/qqmlinfo.h>
44
45QT_BEGIN_NAMESPACE
46
47/*!
48 \qmltype MonthGrid
49 \inherits Control
50//! \instantiates QQuickMonthGrid
51 \inqmlmodule Qt.labs.calendar
52 \brief A grid of days for a calendar month.
53
54 MonthGrid presents a calendar month in a grid. The contents are
55 calculated for a given \l month and \l year, using the specified
56 \l {Control::locale}{locale}.
57
58 \image qtlabscalendar-monthgrid.png
59 \snippet qtlabscalendar-monthgrid.qml 1
60
61 MonthGrid can be used as a standalone control, but it is most often
62 used in conjunction with DayOfWeekRow and WeekNumberColumn. Regardless
63 of the use case, positioning of the grid is left to the user.
64
65 \image qtlabscalendar-monthgrid-layout.png
66 \snippet qtlabscalendar-monthgrid-layout.qml 1
67
68 The visual appearance of MonthGrid can be changed by
69 implementing a \l {delegate}{custom delegate}.
70
71 \labs
72
73 \sa DayOfWeekRow, WeekNumberColumn, CalendarModel
74*/
75
76/*!
77 \qmlsignal Qt.labs.calendar::MonthGrid::pressed(date date)
78
79 This signal is emitted when \a date is pressed.
80*/
81
82/*!
83 \qmlsignal Qt.labs.calendar::MonthGrid::released(date date)
84
85 This signal is emitted when \a date is released.
86*/
87
88/*!
89 \qmlsignal Qt.labs.calendar::MonthGrid::clicked(date date)
90
91 This signal is emitted when \a date is clicked.
92*/
93
94/*!
95 \qmlsignal Qt.labs.calendar::MonthGrid::pressAndHold(date date)
96
97 This signal is emitted when \a date is pressed and held down.
98*/
99
100class QQuickMonthGridPrivate : public QQuickControlPrivate
101{
102 Q_DECLARE_PUBLIC(QQuickMonthGrid)
103
104public:
105 QQuickMonthGridPrivate() : pressTimer(0), pressedItem(nullptr), model(nullptr), delegate(nullptr) { }
106
107 void resizeItems();
108
109 QQuickItem *cellAt(const QPointF &pos) const;
110 QDate dateOf(QQuickItem *cell) const;
111
112 void updatePress(const QPointF &pos);
113 void clearPress(bool clicked);
114
115 void handlePress(const QPointF &point) override;
116 void handleMove(const QPointF &point) override;
117 void handleRelease(const QPointF &point) override;
118 void handleUngrab() override;
119
120 static void setContextProperty(QQuickItem *item, const QString &name, const QVariant &value);
121
122 QString title;
123 QVariant source;
124 QDate pressedDate;
125 int pressTimer;
126 QQuickItem *pressedItem;
127 QQuickMonthModel *model;
128 QQmlComponent *delegate;
129};
130
131void QQuickMonthGridPrivate::resizeItems()
132{
133 if (!contentItem)
134 return;
135
136 QSizeF itemSize;
137 itemSize.setWidth((contentItem->width() - 6 * spacing) / 7);
138 itemSize.setHeight((contentItem->height() - 5 * spacing) / 6);
139
140 const auto childItems = contentItem->childItems();
141 for (QQuickItem *item : childItems) {
142 if (!QQuickItemPrivate::get(item)->isTransparentForPositioner())
143 item->setSize(itemSize);
144 }
145}
146
147QQuickItem *QQuickMonthGridPrivate::cellAt(const QPointF &pos) const
148{
149 Q_Q(const QQuickMonthGrid);
150 if (contentItem) {
151 QPointF mapped = q->mapToItem(item: contentItem, point: pos);
152 return contentItem->childAt(x: mapped.x(), y: mapped.y());
153 }
154 return nullptr;
155}
156
157QDate QQuickMonthGridPrivate::dateOf(QQuickItem *cell) const
158{
159 if (contentItem)
160 return model->dateAt(index: contentItem->childItems().indexOf(t: cell));
161 return QDate();
162}
163
164void QQuickMonthGridPrivate::updatePress(const QPointF &pos)
165{
166 Q_Q(QQuickMonthGrid);
167 clearPress(clicked: false);
168 pressedItem = cellAt(pos);
169 setContextProperty(item: pressedItem, QStringLiteral("pressed"), value: true);
170 pressedDate = dateOf(cell: pressedItem);
171 if (pressedDate.isValid())
172 emit q->pressed(date: pressedDate);
173}
174
175void QQuickMonthGridPrivate::clearPress(bool clicked)
176{
177 Q_Q(QQuickMonthGrid);
178 setContextProperty(item: pressedItem, QStringLiteral("pressed"), value: false);
179 if (pressedDate.isValid()) {
180 emit q->released(date: pressedDate);
181 if (clicked)
182 emit q->clicked(date: pressedDate);
183 }
184 pressedDate = QDate();
185 pressedItem = nullptr;
186}
187
188void QQuickMonthGridPrivate::handlePress(const QPointF &point)
189{
190 Q_Q(QQuickMonthGrid);
191 QQuickControlPrivate::handlePress(point);
192 updatePress(pos: point);
193 if (pressedDate.isValid())
194 pressTimer = q->startTimer(qGuiApp->styleHints()->mousePressAndHoldInterval());
195}
196
197void QQuickMonthGridPrivate::handleMove(const QPointF &point)
198{
199 QQuickControlPrivate::handleMove(point);
200 updatePress(pos: point);
201}
202
203void QQuickMonthGridPrivate::handleRelease(const QPointF &point)
204{
205 QQuickControlPrivate::handleRelease(point);
206 clearPress(clicked: true);
207}
208
209void QQuickMonthGridPrivate::handleUngrab()
210{
211 QQuickControlPrivate::handleUngrab();
212 clearPress(clicked: false);
213}
214
215void QQuickMonthGridPrivate::setContextProperty(QQuickItem *item, const QString &name, const QVariant &value)
216{
217 QQmlContext *context = qmlContext(item);
218 if (context && context->isValid()) {
219 context = context->parentContext();
220 if (context && context->isValid())
221 context->setContextProperty(name, value);
222 }
223}
224
225QQuickMonthGrid::QQuickMonthGrid(QQuickItem *parent) :
226 QQuickControl(*(new QQuickMonthGridPrivate), parent)
227{
228 Q_D(QQuickMonthGrid);
229 setFlag(flag: ItemIsFocusScope);
230 setActiveFocusOnTab(true);
231 setAcceptedMouseButtons(Qt::LeftButton);
232#if QT_CONFIG(cursor)
233 setCursor(Qt::ArrowCursor);
234#endif
235
236 d->model = new QQuickMonthModel(this);
237 d->source = QVariant::fromValue(value: d->model);
238 connect(sender: d->model, signal: &QQuickMonthModel::monthChanged, receiver: this, slot: &QQuickMonthGrid::monthChanged);
239 connect(sender: d->model, signal: &QQuickMonthModel::yearChanged, receiver: this, slot: &QQuickMonthGrid::yearChanged);
240 connect(sender: d->model, signal: &QQuickMonthModel::titleChanged, receiver: this, slot: &QQuickMonthGrid::titleChanged);
241}
242
243/*!
244 \qmlproperty int Qt.labs.calendar::MonthGrid::month
245
246 This property holds the number of the month. The default value is the
247 current month.
248
249 The Qt Labs Calendar module uses 0-based month numbers to be consistent
250 with the JavaScript Date type, that is used by the QML language. This
251 means that \c Date::getMonth() can be assigned to this property as is.
252 When dealing with dealing with month numbers directly, it is highly
253 recommended to use the following enumeration values to avoid confusion.
254
255 \value Calendar.January January (0)
256 \value Calendar.February February (1)
257 \value Calendar.March March (2)
258 \value Calendar.April April (3)
259 \value Calendar.May May (4)
260 \value Calendar.June June (5)
261 \value Calendar.July July (6)
262 \value Calendar.August August (7)
263 \value Calendar.September September (8)
264 \value Calendar.October October (9)
265 \value Calendar.November November (10)
266 \value Calendar.December December (11)
267
268 \sa Calendar
269*/
270int QQuickMonthGrid::month() const
271{
272 Q_D(const QQuickMonthGrid);
273 return d->model->month() - 1;
274}
275
276void QQuickMonthGrid::setMonth(int month)
277{
278 Q_D(QQuickMonthGrid);
279 if (month < 0 || month > 11) {
280 qmlWarning(me: this) << "month " << month << " is out of range [0...11]";
281 return;
282 }
283 d->model->setMonth(month + 1);
284}
285
286/*!
287 \qmlproperty int Qt.labs.calendar::MonthGrid::year
288
289 This property holds the number of the year.
290
291 The value must be in the range from \c -271820 to \c 275759. The default
292 value is the current year.
293*/
294int QQuickMonthGrid::year() const
295{
296 Q_D(const QQuickMonthGrid);
297 return d->model->year();
298}
299
300void QQuickMonthGrid::setYear(int year)
301{
302 Q_D(QQuickMonthGrid);
303 if (year < -271820 || year > 275759) {
304 qmlWarning(me: this) << "year " << year << " is out of range [-271820...275759]";
305 return;
306 }
307 d->model->setYear(year);
308}
309
310/*!
311 \internal
312 \qmlproperty model Qt.labs.calendar::MonthGrid::source
313
314 This property holds the source model that is used as a data model
315 for the internal content column.
316*/
317QVariant QQuickMonthGrid::source() const
318{
319 Q_D(const QQuickMonthGrid);
320 return d->source;
321}
322
323void QQuickMonthGrid::setSource(const QVariant &source)
324{
325 Q_D(QQuickMonthGrid);
326 if (d->source != source) {
327 d->source = source;
328 emit sourceChanged();
329 }
330}
331
332/*!
333 \qmlproperty string Qt.labs.calendar::MonthGrid::title
334
335 This property holds a title for the calendar.
336
337 This property is provided for convenience. MonthGrid itself does
338 not visualize the title. The default value consists of the month name,
339 formatted using \l {Control::locale}{locale}, and the year number.
340*/
341QString QQuickMonthGrid::title() const
342{
343 Q_D(const QQuickMonthGrid);
344 if (d->title.isNull())
345 return d->model->title();
346 return d->title;
347}
348
349void QQuickMonthGrid::setTitle(const QString &title)
350{
351 Q_D(QQuickMonthGrid);
352 if (d->title != title) {
353 d->title = title;
354 emit titleChanged();
355 }
356}
357
358/*!
359 \qmlproperty Component Qt.labs.calendar::MonthGrid::delegate
360
361 This property holds the item delegate that visualizes each day.
362
363 In addition to the \c index property, a list of model data roles
364 are available in the context of each delegate:
365 \table
366 \row \li \b model.date : date \li The date of the cell
367 \row \li \b model.day : int \li The number of the day
368 \row \li \b model.today : bool \li Whether the delegate represents today
369 \row \li \b model.weekNumber : int \li The week number
370 \row \li \b model.month : int \li The number of the month
371 \row \li \b model.year : int \li The number of the year
372 \endtable
373
374 The following snippet presents the default implementation of the item
375 delegate. It can be used as a starting point for implementing custom
376 delegates.
377
378 \snippet MonthGrid.qml delegate
379*/
380QQmlComponent *QQuickMonthGrid::delegate() const
381{
382 Q_D(const QQuickMonthGrid);
383 return d->delegate;
384}
385
386void QQuickMonthGrid::setDelegate(QQmlComponent *delegate)
387{
388 Q_D(QQuickMonthGrid);
389 if (d->delegate != delegate) {
390 d->delegate = delegate;
391 emit delegateChanged();
392 }
393}
394
395void QQuickMonthGrid::componentComplete()
396{
397 Q_D(QQuickMonthGrid);
398 QQuickControl::componentComplete();
399 if (d->contentItem) {
400 const auto childItems = d->contentItem->childItems();
401 for (QQuickItem *child : childItems) {
402 if (!QQuickItemPrivate::get(item: child)->isTransparentForPositioner())
403 d->setContextProperty(item: child, QStringLiteral("pressed"), value: false);
404 }
405 }
406 d->resizeItems();
407}
408
409void QQuickMonthGrid::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
410{
411 Q_D(QQuickMonthGrid);
412 QQuickControl::geometryChanged(newGeometry, oldGeometry);
413 if (isComponentComplete())
414 d->resizeItems();
415}
416
417void QQuickMonthGrid::localeChange(const QLocale &newLocale, const QLocale &oldLocale)
418{
419 Q_D(QQuickMonthGrid);
420 QQuickControl::localeChange(newLocale, oldLocale);
421 d->model->setLocale(newLocale);
422}
423
424void QQuickMonthGrid::paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding)
425{
426 Q_D(QQuickMonthGrid);
427 QQuickControl::paddingChange(newPadding, oldPadding);
428 if (isComponentComplete())
429 d->resizeItems();
430}
431
432void QQuickMonthGrid::updatePolish()
433{
434 Q_D(QQuickMonthGrid);
435 QQuickControl::updatePolish();
436 d->resizeItems();
437}
438
439void QQuickMonthGrid::timerEvent(QTimerEvent *event)
440{
441 Q_D(QQuickMonthGrid);
442 if (event->timerId() == d->pressTimer) {
443 if (d->pressedDate.isValid())
444 emit pressAndHold(date: d->pressedDate);
445 killTimer(id: d->pressTimer);
446 }
447}
448
449QT_END_NAMESPACE
450

source code of qtquickcontrols2/src/imports/calendar/qquickmonthgrid.cpp