QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsdatetimeedit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdatetimeedit.cpp
3  --------------------------------------
4  Date : 08.2014
5  Copyright : (C) 2014 Denis Rouzaud
6  Email : [email protected]
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <QAction>
17 #include <QCalendarWidget>
18 #include <QLineEdit>
19 #include <QMouseEvent>
20 #include <QStyle>
21 #include <QStyleOptionSpinBox>
22 
23 
24 #include "qgsdatetimeedit.h"
25 
26 #include "qgsapplication.h"
27 #include "qgslogger.h"
28 
29 
30 
32  : QDateTimeEdit( parent )
33 {
34  QIcon clearIcon = QgsApplication::getThemeIcon( "/mIconClearText.svg" );
35  mClearAction = new QAction( clearIcon, tr( "clear" ), this );
36  mClearAction->setCheckable( false );
37  lineEdit()->addAction( mClearAction, QLineEdit::TrailingPosition );
38  mClearAction->setVisible( mAllowNull );
39  connect( mClearAction, &QAction::triggered, this, &QgsDateTimeEdit::clear );
40 
41  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
42 
43  // enable calendar widget by default so it's already created
44  setCalendarPopup( true );
45 
46  setMinimumEditDateTime();
47 
48  // init with current time so mIsNull is properly initialized
49  QDateTimeEdit::setDateTime( QDateTime::currentDateTime() );
50 }
51 
53 {
54  mAllowNull = allowNull;
55  mClearAction->setVisible( mAllowNull && ( !mIsNull || mIsEmpty ) );
56 }
57 
58 
60 {
61  if ( mAllowNull )
62  {
63  displayCurrentDate();
64 
65  // Check if it's really changed or crash, see GH #29937
66  if ( ! dateTime().isNull() )
67  {
68  changed( QDateTime() );
69  }
70 
71  // emit signal of QDateTime::dateTimeChanged with an invalid date
72  // anyway, using parent's signal should be avoided
73  // If you consequently connect parent's dateTimeChanged signal
74  // and call dateTime() afterwards there is no warranty to
75  // have a proper NULL value handling
76  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
77  emit dateTimeChanged( QDateTime() );
78  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
79  }
80 }
81 
83 {
84  mClearAction->setVisible( mAllowNull );
85  mIsEmpty = true;
86 }
87 
88 void QgsDateTimeEdit::mousePressEvent( QMouseEvent *event )
89 {
90  // catch mouse press on the button (when the current value is null)
91  // in non-calendar mode: modifiy the date so it leads to showing current date (don't bother about time)
92  // in calendar mode: be sure NULL is displayed when needed and show page of current date in calendar widget
93 
94  bool updateCalendar = false;
95 
96  if ( mIsNull )
97  {
98  QStyle::SubControl control;
99  if ( calendarPopup() )
100  {
101  QStyleOptionComboBox optCombo;
102  optCombo.init( this );
103  optCombo.editable = true;
104  optCombo.subControls = QStyle::SC_All;
105  control = style()->hitTestComplexControl( QStyle::CC_ComboBox, &optCombo, event->pos(), this );
106 
107  if ( control == QStyle::SC_ComboBoxArrow && calendarWidget() )
108  {
109  mCurrentPressEvent = true;
110  // ensure the line edit still displays NULL
111  updateCalendar = true;
112  displayNull( updateCalendar );
113  mCurrentPressEvent = false;
114  }
115  }
116  else
117  {
118  QStyleOptionSpinBox opt;
119  this->initStyleOption( &opt );
120  control = style()->hitTestComplexControl( QStyle::CC_SpinBox, &opt, event->pos(), this );
121 
122  if ( control == QStyle::SC_SpinBoxDown || control == QStyle::SC_SpinBoxUp )
123  {
124  mCurrentPressEvent = true;
125  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
126  resetBeforeChange( control == QStyle::SC_SpinBoxDown ? -1 : 1 );
127  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
128  mCurrentPressEvent = false;
129  }
130  }
131  }
132 
133  QDateTimeEdit::mousePressEvent( event );
134 
135  if ( updateCalendar )
136  {
137  // set calendar page to current date to avoid going to minimal date page when value is null
138  calendarWidget()->setCurrentPage( QDate::currentDate().year(), QDate::currentDate().month() );
139  }
140 }
141 
142 void QgsDateTimeEdit::focusOutEvent( QFocusEvent *event )
143 {
144  if ( mAllowNull && mIsNull && !mCurrentPressEvent )
145  {
146  QAbstractSpinBox::focusOutEvent( event );
147  if ( lineEdit()->text() != QgsApplication::nullRepresentation() )
148  {
149  displayNull();
150  }
151  emit editingFinished();
152  }
153  else
154  {
155  QDateTimeEdit::focusOutEvent( event );
156  }
157 }
158 
159 void QgsDateTimeEdit::focusInEvent( QFocusEvent *event )
160 {
161  if ( mAllowNull && mIsNull && !mCurrentPressEvent )
162  {
163  QAbstractSpinBox::focusInEvent( event );
164 
165  displayCurrentDate();
166  }
167  else
168  {
169  QDateTimeEdit::focusInEvent( event );
170  }
171 }
172 
173 void QgsDateTimeEdit::wheelEvent( QWheelEvent *event )
174 {
175  // dateTime might have been set to minimum in calendar mode
176  if ( mAllowNull && mIsNull )
177  {
178  resetBeforeChange( -event->delta() );
179  }
180  QDateTimeEdit::wheelEvent( event );
181 }
182 
183 void QgsDateTimeEdit::showEvent( QShowEvent *event )
184 {
185  QDateTimeEdit::showEvent( event );
186  if ( mAllowNull && mIsNull &&
187  lineEdit()->text() != QgsApplication::nullRepresentation() )
188  {
189  displayNull();
190  }
191 }
192 
193 void QgsDateTimeEdit::changed( const QDateTime &dateTime )
194 {
195  mIsEmpty = false;
196  bool isNull = dateTime.isNull();
197  if ( isNull != mIsNull )
198  {
199  mIsNull = isNull;
200  if ( mIsNull )
201  {
202  if ( mOriginalStyleSheet.isNull() )
203  {
204  mOriginalStyleSheet = lineEdit()->styleSheet();
205  }
206  lineEdit()->setStyleSheet( QStringLiteral( "QLineEdit { font-style: italic; color: grey; }" ) );
207  }
208  else
209  {
210  lineEdit()->setStyleSheet( mOriginalStyleSheet );
211  }
212  }
213 
214  mClearAction->setVisible( mAllowNull && !mIsNull );
215 
216  emit QgsDateTimeEdit::valueChanged( dateTime );
217 }
218 
219 void QgsDateTimeEdit::displayNull( bool updateCalendar )
220 {
221  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
222  if ( updateCalendar )
223  {
224  // set current time to minimum date time to avoid having
225  // a date selected in calendar widget
226  QDateTimeEdit::setDateTime( minimumDateTime() );
227  }
228  lineEdit()->setCursorPosition( lineEdit()->text().length() );
229  lineEdit()->setText( QgsApplication::nullRepresentation() );
230  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
231 }
232 
233 void QgsDateTimeEdit::displayCurrentDate()
234 {
235  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
236  QDateTimeEdit::setDateTime( QDateTime::currentDateTime() );
237  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
238 }
239 
240 void QgsDateTimeEdit::resetBeforeChange( int delta )
241 {
242  QDateTime dt = QDateTime::currentDateTime();
243  switch ( currentSection() )
244  {
245  case QDateTimeEdit::DaySection:
246  dt = dt.addDays( delta );
247  break;
248  case QDateTimeEdit::MonthSection:
249  dt = dt.addMonths( delta );
250  break;
251  case QDateTimeEdit::YearSection:
252  dt = dt.addYears( delta );
253  break;
254  default:
255  break;
256  }
257  if ( dt < minimumDateTime() )
258  {
259  dt = minimumDateTime();
260  }
261  else if ( dt > maximumDateTime() )
262  {
263  dt = maximumDateTime();
264  }
265  QDateTimeEdit::setDateTime( dt );
266 }
267 
268 void QgsDateTimeEdit::setDateTime( const QDateTime &dateTime )
269 {
270  mIsEmpty = false;
271 
272  // set an undefined date
273  if ( !dateTime.isValid() || dateTime.isNull() )
274  {
275  clear();
276  displayNull();
277  }
278  // Check if it's really changed or crash, see GH #29937
279  else if ( dateTime != QgsDateTimeEdit::dateTime() )
280  {
281  QDateTimeEdit::setDateTime( dateTime );
282  changed( dateTime );
283  }
284 }
285 
286 QDateTime QgsDateTimeEdit::dateTime() const
287 {
288  if ( mAllowNull && mIsNull )
289  {
290  return QDateTime();
291  }
292  else
293  {
294  return QDateTimeEdit::dateTime();
295  }
296 }
297 
299 {
300  if ( mAllowNull && mIsNull )
301  {
302  return QTime();
303  }
304  else
305  {
306  return QDateTimeEdit::time();
307  }
308 }
309 
311 {
312  if ( mAllowNull && mIsNull )
313  {
314  return QDate();
315  }
316  else
317  {
318  return QDateTimeEdit::date();
319  }
320 }
QTime time() const
Returns the time which can be a null time.
bool allowNull() const
If the widget allows setting null date/time.
void showEvent(QShowEvent *event) override
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void focusOutEvent(QFocusEvent *event) override
void wheelEvent(QWheelEvent *event) override
void focusInEvent(QFocusEvent *event) override
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
void valueChanged(const QDateTime &date)
Signal emitted whenever the value changes.
void setEmpty()
Resets the widget to show no value (ie, an "unknown" state).
void setAllowNull(bool allowNull)
Determines if the widget allows setting null date/time.
QgsDateTimeEdit(QWidget *parent=nullptr)
Constructor for QgsDateTimeEdit.
void setDateTime(const QDateTime &dateTime)
Set the date time in the widget and handles null date times.
QDate date() const
Returns the date which can be a null date.
void clear() override
Set the current date as NULL.
QDateTime dateTime() const
Returns the date time which can be a null date/time.
void mousePressEvent(QMouseEvent *event) override