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 
45 QT_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 
100 class QQuickMonthGridPrivate : public QQuickControlPrivate
101 {
102     Q_DECLARE_PUBLIC(QQuickMonthGrid)
103 
104 public:
QQuickMonthGridPrivate()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 
resizeItems()131 void 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 
cellAt(const QPointF & pos) const147 QQuickItem *QQuickMonthGridPrivate::cellAt(const QPointF &pos) const
148 {
149     Q_Q(const QQuickMonthGrid);
150     if (contentItem) {
151         QPointF mapped = q->mapToItem(contentItem, pos);
152         return contentItem->childAt(mapped.x(), mapped.y());
153     }
154     return nullptr;
155 }
156 
dateOf(QQuickItem * cell) const157 QDate QQuickMonthGridPrivate::dateOf(QQuickItem *cell) const
158 {
159     if (contentItem)
160         return model->dateAt(contentItem->childItems().indexOf(cell));
161     return QDate();
162 }
163 
updatePress(const QPointF & pos)164 void QQuickMonthGridPrivate::updatePress(const QPointF &pos)
165 {
166     Q_Q(QQuickMonthGrid);
167     clearPress(false);
168     pressedItem = cellAt(pos);
169     setContextProperty(pressedItem, QStringLiteral("pressed"), true);
170     pressedDate = dateOf(pressedItem);
171     if (pressedDate.isValid())
172         emit q->pressed(pressedDate);
173 }
174 
clearPress(bool clicked)175 void QQuickMonthGridPrivate::clearPress(bool clicked)
176 {
177     Q_Q(QQuickMonthGrid);
178     setContextProperty(pressedItem, QStringLiteral("pressed"), false);
179     if (pressedDate.isValid()) {
180         emit q->released(pressedDate);
181         if (clicked)
182             emit q->clicked(pressedDate);
183     }
184     pressedDate = QDate();
185     pressedItem = nullptr;
186 }
187 
handlePress(const QPointF & point)188 void QQuickMonthGridPrivate::handlePress(const QPointF &point)
189 {
190     Q_Q(QQuickMonthGrid);
191     QQuickControlPrivate::handlePress(point);
192     updatePress(point);
193     if (pressedDate.isValid())
194         pressTimer = q->startTimer(qGuiApp->styleHints()->mousePressAndHoldInterval());
195 }
196 
handleMove(const QPointF & point)197 void QQuickMonthGridPrivate::handleMove(const QPointF &point)
198 {
199     QQuickControlPrivate::handleMove(point);
200     updatePress(point);
201 }
202 
handleRelease(const QPointF & point)203 void QQuickMonthGridPrivate::handleRelease(const QPointF &point)
204 {
205     QQuickControlPrivate::handleRelease(point);
206     clearPress(true);
207 }
208 
handleUngrab()209 void QQuickMonthGridPrivate::handleUngrab()
210 {
211     QQuickControlPrivate::handleUngrab();
212     clearPress(false);
213 }
214 
setContextProperty(QQuickItem * item,const QString & name,const QVariant & value)215 void 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 
QQuickMonthGrid(QQuickItem * parent)225 QQuickMonthGrid::QQuickMonthGrid(QQuickItem *parent) :
226     QQuickControl(*(new QQuickMonthGridPrivate), parent)
227 {
228     Q_D(QQuickMonthGrid);
229     setFlag(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(d->model);
238     connect(d->model, &QQuickMonthModel::monthChanged, this, &QQuickMonthGrid::monthChanged);
239     connect(d->model, &QQuickMonthModel::yearChanged, this, &QQuickMonthGrid::yearChanged);
240     connect(d->model, &QQuickMonthModel::titleChanged, this, &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 */
month() const270 int QQuickMonthGrid::month() const
271 {
272     Q_D(const QQuickMonthGrid);
273     return d->model->month() - 1;
274 }
275 
setMonth(int month)276 void QQuickMonthGrid::setMonth(int month)
277 {
278     Q_D(QQuickMonthGrid);
279     if (month < 0 || month > 11) {
280         qmlWarning(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 */
year() const294 int QQuickMonthGrid::year() const
295 {
296     Q_D(const QQuickMonthGrid);
297     return d->model->year();
298 }
299 
setYear(int year)300 void QQuickMonthGrid::setYear(int year)
301 {
302     Q_D(QQuickMonthGrid);
303     if (year < -271820 || year > 275759) {
304         qmlWarning(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 */
source() const317 QVariant QQuickMonthGrid::source() const
318 {
319     Q_D(const QQuickMonthGrid);
320     return d->source;
321 }
322 
setSource(const QVariant & source)323 void 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 */
title() const341 QString 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 
setTitle(const QString & title)349 void 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 */
delegate() const380 QQmlComponent *QQuickMonthGrid::delegate() const
381 {
382     Q_D(const QQuickMonthGrid);
383     return d->delegate;
384 }
385 
setDelegate(QQmlComponent * delegate)386 void QQuickMonthGrid::setDelegate(QQmlComponent *delegate)
387 {
388     Q_D(QQuickMonthGrid);
389     if (d->delegate != delegate) {
390         d->delegate = delegate;
391         emit delegateChanged();
392     }
393 }
394 
componentComplete()395 void 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(child)->isTransparentForPositioner())
403                 d->setContextProperty(child, QStringLiteral("pressed"), false);
404         }
405     }
406     d->resizeItems();
407 }
408 
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)409 void 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 
localeChange(const QLocale & newLocale,const QLocale & oldLocale)417 void 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 
paddingChange(const QMarginsF & newPadding,const QMarginsF & oldPadding)424 void 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 
updatePolish()432 void QQuickMonthGrid::updatePolish()
433 {
434     Q_D(QQuickMonthGrid);
435     QQuickControl::updatePolish();
436     d->resizeItems();
437 }
438 
timerEvent(QTimerEvent * event)439 void QQuickMonthGrid::timerEvent(QTimerEvent *event)
440 {
441     Q_D(QQuickMonthGrid);
442     if (event->timerId() == d->pressTimer) {
443         if (d->pressedDate.isValid())
444             emit pressAndHold(d->pressedDate);
445         killTimer(d->pressTimer);
446     }
447 }
448 
449 QT_END_NAMESPACE
450