diff --git a/include/wx/datetimectrl.h b/include/wx/datetimectrl.h index 30f23dfffe..29cff3c7c9 100644 --- a/include/wx/datetimectrl.h +++ b/include/wx/datetimectrl.h @@ -32,6 +32,13 @@ public: // Set/get the date or time (in the latter case, time part is ignored). virtual void SetValue(const wxDateTime& dt) = 0; virtual wxDateTime GetValue() const = 0; + + // For the controls with wxDP_ALLOWNONE style, set the string displayed + // when the control doesn't have any valid value. Currently this is only + // actually used under MSW, where it can be used to override the previous + // value which is still displayed by the control in this case, and ignored + // elsewhere. + virtual void SetNullText(const wxString& WXUNUSED(text)) { } }; #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) diff --git a/include/wx/msw/datetimectrl.h b/include/wx/msw/datetimectrl.h index cc6eab89b7..a56c87d580 100644 --- a/include/wx/msw/datetimectrl.h +++ b/include/wx/msw/datetimectrl.h @@ -26,6 +26,8 @@ public: virtual void SetValue(const wxDateTime& dt) wxOVERRIDE; virtual wxDateTime GetValue() const wxOVERRIDE; + virtual void SetNullText(const wxString& text) wxOVERRIDE; + // returns true if the platform should explicitly apply a theme border virtual bool CanApplyThemeBorder() const wxOVERRIDE { return false; } @@ -61,6 +63,19 @@ protected: // the date currently shown by the control, may be invalid wxDateTime m_date; + +private: + // Helper setting the appropriate format depending on the passed in state. + void MSWUpdateFormat(bool valid); + + // Same thing, but only doing if the validity differs from the date + // validity, i.e. avoiding useless work if nothing needs to be done. + void MSWUpdateFormatIfNeeded(bool valid); + + + // shown when there is no valid value (so only used with wxDP_ALLOWNONE), + // always non-empty if SetNullText() was called, see the comments there + wxString m_nullText; }; #endif // _WX_MSW_DATETIMECTRL_H_ diff --git a/interface/wx/datectrl.h b/interface/wx/datectrl.h index 72199583e7..59d1eda289 100644 --- a/interface/wx/datectrl.h +++ b/interface/wx/datectrl.h @@ -168,6 +168,22 @@ public: */ virtual wxDateTime GetValue() const; + /** + Set the text to show when there is no valid value. + + For the controls with @c wxDP_ALLOWNONE style, set the string displayed + when the control doesn't have any valid value. Currently this is only + actually used under MSW, where it can be used to override the previous + value which is still displayed by the control in this case, and ignored + elsewhere. + + Notably, @a text can be empty to completely hide the date if no valid + date is specified. + + @since 3.1.5 + */ + void SetNullText(const wxString& text); + /** Sets the valid range for the date selection. If @a dt1 is valid, it becomes the earliest date (inclusive) accepted by the control. If diff --git a/samples/widgets/datepick.cpp b/samples/widgets/datepick.cpp index 3bc561b472..f0374900da 100644 --- a/samples/widgets/datepick.cpp +++ b/samples/widgets/datepick.cpp @@ -54,6 +54,7 @@ enum DatePickerPage_Reset = wxID_HIGHEST, DatePickerPage_Set, DatePickerPage_SetRange, + DatePickerPage_SetNullText, DatePickerPage_Picker }; @@ -78,6 +79,7 @@ protected: void OnButtonSet(wxCommandEvent& event); void OnButtonSetRange(wxCommandEvent& event); + void OnButtonSetNullText(wxCommandEvent& event); void OnButtonReset(wxCommandEvent& event); // reset the date picker parameters @@ -96,6 +98,7 @@ protected: wxTextCtrl *m_textCur; wxTextCtrl *m_textMin; wxTextCtrl *m_textMax; + wxTextCtrl *m_textNull; wxRadioBox* m_radioKind; wxCheckBox* m_chkStyleCentury; @@ -117,6 +120,7 @@ wxBEGIN_EVENT_TABLE(DatePickerWidgetsPage, WidgetsPage) EVT_BUTTON(DatePickerPage_Reset, DatePickerWidgetsPage::OnButtonReset) EVT_BUTTON(DatePickerPage_Set, DatePickerWidgetsPage::OnButtonSet) EVT_BUTTON(DatePickerPage_SetRange, DatePickerWidgetsPage::OnButtonSetRange) + EVT_BUTTON(DatePickerPage_SetNullText, DatePickerWidgetsPage::OnButtonSetNullText) EVT_DATE_CHANGED(wxID_ANY, DatePickerWidgetsPage::OnDateChanged) wxEND_EVENT_TABLE() @@ -197,6 +201,20 @@ void DatePickerWidgetsPage::CreateContent() sizerMiddle->Add(new wxButton(this, DatePickerPage_SetRange, "Set &range"), wxSizerFlags().Centre().Border()); + sizerMiddle->AddSpacer(10); + + sizerMiddle->Add(CreateSizerWithTextAndLabel + ( + "&Null text", + wxID_ANY, + &m_textNull + ), + wxSizerFlags().Expand().Border()); + + sizerMiddle->Add(new wxButton(this, DatePickerPage_SetNullText, + "Set &null text"), + wxSizerFlags().Centre().Border()); + // right pane: control itself wxSizer *sizerRight = new wxBoxSizer(wxHORIZONTAL); @@ -327,6 +345,11 @@ void DatePickerWidgetsPage::OnButtonSetRange(wxCommandEvent& WXUNUSED(event)) } } +void DatePickerWidgetsPage::OnButtonSetNullText(wxCommandEvent& WXUNUSED(event)) +{ + m_datePicker->SetNullText(m_textNull->GetValue()); +} + // Helper function which has to be used here because the controls with // wxDP_ALLOWNONE style can have invalid date, both in the control itself and // in the event it generates. diff --git a/src/msw/datetimectrl.cpp b/src/msw/datetimectrl.cpp index df023a5231..41059fa360 100644 --- a/src/msw/datetimectrl.cpp +++ b/src/msw/datetimectrl.cpp @@ -99,6 +99,8 @@ void wxDateTimePickerCtrl::SetValue(const wxDateTime& dt) return; } + MSWUpdateFormatIfNeeded(dt.IsValid()); + m_date = dt; } @@ -107,6 +109,57 @@ wxDateTime wxDateTimePickerCtrl::GetValue() const return m_date; } +void wxDateTimePickerCtrl::MSWUpdateFormatIfNeeded(bool valid) +{ + if ( MSWAllowsNone() && !m_nullText.empty() && valid != m_date.IsValid() ) + MSWUpdateFormat(valid); +} + +void wxDateTimePickerCtrl::MSWUpdateFormat(bool valid) +{ + // We just use NULL to reset to the default format when the date is valid, + // as the control seems to remember whichever format was used when it was + // created, i.e. this works both with and without wxDP_SHOWCENTURY. + + // Note: due to a bug in MinGW headers, with missing parentheses around the + // macro argument (corrected in or before 8.2, but still existing in 5.3), + // we have to use a temporary variable here. + const TCHAR* const format = valid ? NULL : m_nullText.t_str(); + DateTime_SetFormat(GetHwnd(), format); +} + +void wxDateTimePickerCtrl::SetNullText(const wxString& text) +{ + m_nullText = text; + if ( m_nullText.empty() ) + { + // Using empty format doesn't work with the native control, it just + // uses the default short date format in this case, so set the format + // to the single space which is more or less guaranteed to work as it's + // the semi-official way to clear the control contents when it doesn't + // have any valid value, according to Microsoft's old KB document + // Q238077, which can still be found online by searching for its + // number, even if it's not available on Microsoft web site any more. + // + // Coincidentally, it's also convenient for us, as we can just check if + // null text is empty to see if we need to use it elsewhere in the code. + m_nullText = wxS(" "); + } + else + { + // We need to quote the text, as otherwise format specifiers (e.g. + // "d", "m" etc) would be interpreted specially by the control. To make + // things simple, we just quote it entirely and always. + m_nullText.Replace("'", "''"); + m_nullText.insert(0, "'"); + m_nullText.append("'"); + } + + // Apply it immediately if we don't have any value right now. + if ( !m_date.IsValid() ) + MSWUpdateFormat(false); +} + wxSize wxDateTimePickerCtrl::DoGetBestSize() const { wxClientDC dc(const_cast(this)); @@ -168,7 +221,12 @@ wxDateTimePickerCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) switch ( hdr->code ) { case DTN_DATETIMECHANGE: - if ( MSWOnDateTimeChange(*(NMDATETIMECHANGE*)(hdr)) ) + const NMDATETIMECHANGE& dtch = *(NMDATETIMECHANGE*)(hdr); + + // Update the format before showing the new date if necessary. + MSWUpdateFormatIfNeeded(dtch.dwFlags == GDT_VALID); + + if ( MSWOnDateTimeChange(dtch) ) { *result = 0; return true;