QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 }
QDateTime dateTime() const
dateTime returns the date time which can eventually be a 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
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
bool allowNull() const
If the widget allows setting null date/time.
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 The current date and time is used by default.
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.
void mousePressEvent(QMouseEvent *event) override