QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  displayNull();
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  // otherwise, NULL is not displayed in the line edit
81  // this might not be the right way to do it
82  clearFocus();
83  }
84 }
85 
87 {
88  mClearAction->setVisible( mAllowNull );
89  mIsEmpty = true;
90 }
91 
92 void QgsDateTimeEdit::mousePressEvent( QMouseEvent *event )
93 {
94  // catch mouse press on the button (when the current value is null)
95  // in non-calendar mode: modifiy the date so it leads to showing current date (don't bother about time)
96  // in calendar mode: be sure NULL is displayed when needed and show page of current date in calendar widget
97 
98  bool updateCalendar = false;
99 
100  if ( mIsNull )
101  {
102  QStyle::SubControl control;
103  if ( calendarPopup() )
104  {
105  QStyleOptionComboBox optCombo;
106  optCombo.init( this );
107  optCombo.editable = true;
108  optCombo.subControls = QStyle::SC_All;
109  control = style()->hitTestComplexControl( QStyle::CC_ComboBox, &optCombo, event->pos(), this );
110 
111  if ( control == QStyle::SC_ComboBoxArrow && calendarWidget() )
112  {
113  mCurrentPressEvent = true;
114  // ensure the line edit still displays NULL
115  updateCalendar = true;
116  displayNull( updateCalendar );
117  mCurrentPressEvent = false;
118  }
119  }
120  else
121  {
122  QStyleOptionSpinBox opt;
123  this->initStyleOption( &opt );
124  control = style()->hitTestComplexControl( QStyle::CC_SpinBox, &opt, event->pos(), this );
125 
126  if ( control == QStyle::SC_SpinBoxDown || control == QStyle::SC_SpinBoxUp )
127  {
128  mCurrentPressEvent = true;
129  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
130  resetBeforeChange( control == QStyle::SC_SpinBoxDown ? -1 : 1 );
131  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
132  mCurrentPressEvent = false;
133  }
134  }
135  }
136 
137  QDateTimeEdit::mousePressEvent( event );
138 
139  if ( updateCalendar )
140  {
141  // set calendar page to current date to avoid going to minimal date page when value is null
142  calendarWidget()->setCurrentPage( QDate::currentDate().year(), QDate::currentDate().month() );
143  }
144 }
145 
146 void QgsDateTimeEdit::focusOutEvent( QFocusEvent *event )
147 {
148  if ( mAllowNull && mIsNull && !mCurrentPressEvent )
149  {
150  QAbstractSpinBox::focusOutEvent( event );
151  if ( lineEdit()->text() != QgsApplication::nullRepresentation() )
152  {
153  displayNull();
154  }
155  emit editingFinished();
156  }
157  else
158  {
159  QDateTimeEdit::focusOutEvent( event );
160  }
161 }
162 
163 void QgsDateTimeEdit::wheelEvent( QWheelEvent *event )
164 {
165  // dateTime might have been set to minimum in calendar mode
166  if ( mAllowNull && mIsNull )
167  {
168  resetBeforeChange( -event->delta() );
169  }
170  QDateTimeEdit::wheelEvent( event );
171 }
172 
173 void QgsDateTimeEdit::showEvent( QShowEvent *event )
174 {
175  QDateTimeEdit::showEvent( event );
176  if ( mAllowNull && mIsNull &&
177  lineEdit()->text() != QgsApplication::nullRepresentation() )
178  {
179  displayNull();
180  }
181 }
182 
183 void QgsDateTimeEdit::changed( const QDateTime &dateTime )
184 {
185  mIsEmpty = false;
186  bool isNull = dateTime.isNull();
187  if ( isNull != mIsNull )
188  {
189  mIsNull = isNull;
190  if ( mIsNull )
191  {
192  if ( mOriginalStyleSheet.isNull() )
193  {
194  mOriginalStyleSheet = lineEdit()->styleSheet();
195  }
196  lineEdit()->setStyleSheet( QStringLiteral( "QLineEdit { font-style: italic; color: grey; }" ) );
197  }
198  else
199  {
200  lineEdit()->setStyleSheet( mOriginalStyleSheet );
201  }
202  }
203 
204  mClearAction->setVisible( mAllowNull && !mIsNull );
205 
206  emit QgsDateTimeEdit::valueChanged( dateTime );
207 }
208 
209 void QgsDateTimeEdit::displayNull( bool updateCalendar )
210 {
211  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
212  if ( updateCalendar )
213  {
214  // set current time to minimum date time to avoid having
215  // a date selected in calendar widget
216  QDateTimeEdit::setDateTime( minimumDateTime() );
217  }
218  lineEdit()->setText( QgsApplication::nullRepresentation() );
219  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
220 }
221 
222 void QgsDateTimeEdit::resetBeforeChange( int delta )
223 {
224  QDateTime dt = QDateTime::currentDateTime();
225  switch ( currentSection() )
226  {
227  case QDateTimeEdit::DaySection:
228  dt = dt.addDays( delta );
229  break;
230  case QDateTimeEdit::MonthSection:
231  dt = dt.addMonths( delta );
232  break;
233  case QDateTimeEdit::YearSection:
234  dt = dt.addYears( delta );
235  break;
236  default:
237  break;
238  }
239  if ( dt < minimumDateTime() )
240  {
241  dt = minimumDateTime();
242  }
243  else if ( dt > maximumDateTime() )
244  {
245  dt = maximumDateTime();
246  }
247  QDateTimeEdit::setDateTime( dt );
248 }
249 
250 void QgsDateTimeEdit::setDateTime( const QDateTime &dateTime )
251 {
252  mIsEmpty = false;
253 
254  // set an undefined date
255  if ( !dateTime.isValid() || dateTime.isNull() )
256  {
257  clear();
258  }
259  // Check if it's really changed or crash, see GH #29937
260  else if ( dateTime != QgsDateTimeEdit::dateTime() )
261  {
262  QDateTimeEdit::setDateTime( dateTime );
263  changed( dateTime );
264  }
265 }
266 
267 QDateTime QgsDateTimeEdit::dateTime() const
268 {
269  if ( mAllowNull && mIsNull )
270  {
271  return QDateTime();
272  }
273  else
274  {
275  return QDateTimeEdit::dateTime();
276  }
277 }
bool allowNull() const
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
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)
setDateTime set the date time in the widget and handles null date times.
void clear() override
Set the current date as NULL.
QDateTime dateTime() const
dateTime returns the date time which can eventually be a null date/time
void mousePressEvent(QMouseEvent *event) override