From eda11b8755aed36c89ce86ab6da0b293b0933ac0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 3 Nov 2020 02:37:02 +0100 Subject: [PATCH 1/6] Use specified format in wxGridCellDateRenderer if possible For the tables that store dates as strings, we must use the specified format for interpreting the table data, as otherwise we could confuse the user by showing it incorrectly: for example, if "%d.%m.%y" format is used, "01.03.02" should really mean 2002-03-01, but it was parsed as 0002-01-03 by ParseDate() which had no choice but to guess the order of data components. --- src/generic/gridctrl.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index a82d8ea24f..38744e2ff3 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -132,6 +132,14 @@ wxString wxGridCellDateRenderer::GetString(const wxGrid& grid, int row, int col) bool wxGridCellDateRenderer::Parse(const wxString& text, wxDateTime& result) { wxString::const_iterator end; + + // Try parsing using the same format we use for output first. + if ( result.ParseFormat(text, m_oformat, &end) && end == text.end() ) + return true; + + // But fall back to free-form parsing, which notably allows us to parse + // strings such as "today" or "tomorrow" which would be never accepted by + // ParseFormat(). return result.ParseDate(text, &end) && end == text.end(); } From 56ec476a3a54e59ae1e0407dc77e1279481254b2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 3 Nov 2020 02:50:31 +0100 Subject: [PATCH 2/6] Refactor wxGridCellDateRenderer::Parse() to make it reusable No real changes, just create TryParseDate() helper in order to allow reusing it from wxGridCellDateEditor too in the upcoming commit. --- include/wx/generic/private/grid.h | 15 +++++++++++++++ src/generic/gridctrl.cpp | 28 ++++++++++++++++++---------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index 1330bb1044..19df9d4c76 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -1084,5 +1084,20 @@ wxGetContentRect(wxSize contentSize, int hAlign, int vAlign); +namespace wxGridPrivate +{ + +#if wxUSE_DATETIME + +// Helper function trying to parse the given string using the specified date +// format and then using ParseDate() as a fallback if it failed. If this still +// fails, returns false. +bool +TryParseDate(wxDateTime& result, const wxString& text, const wxString& format); + +#endif // wxUSE_DATETIME + +} // namespace wxGridPrivate + #endif // wxUSE_GRID #endif // _WX_GENERIC_GRID_PRIVATE_H_ diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index 38744e2ff3..c4b37fb421 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -76,6 +76,23 @@ void wxGridCellRenderer::Draw(wxGrid& grid, #if wxUSE_DATETIME +bool +wxGridPrivate::TryParseDate(wxDateTime& result, + const wxString& text, + const wxString& format) +{ + wxString::const_iterator end; + + // Try parsing using the same format we use for output first. + if ( result.ParseFormat(text, format, &end) && end == text.end() ) + return true; + + // But fall back to free-form parsing, which notably allows us to parse + // strings such as "today" or "tomorrow" which would be never accepted by + // ParseFormat(). + return result.ParseDate(text, &end) && end == text.end(); +} + // Enables a grid cell to display a formatted date wxGridCellDateRenderer::wxGridCellDateRenderer(const wxString& outformat) @@ -131,16 +148,7 @@ wxString wxGridCellDateRenderer::GetString(const wxGrid& grid, int row, int col) bool wxGridCellDateRenderer::Parse(const wxString& text, wxDateTime& result) { - wxString::const_iterator end; - - // Try parsing using the same format we use for output first. - if ( result.ParseFormat(text, m_oformat, &end) && end == text.end() ) - return true; - - // But fall back to free-form parsing, which notably allows us to parse - // strings such as "today" or "tomorrow" which would be never accepted by - // ParseFormat(). - return result.ParseDate(text, &end) && end == text.end(); + return wxGridPrivate::TryParseDate(result, text, m_oformat); } void wxGridCellDateRenderer::Draw(wxGrid& grid, From 705006615f0a8f1389f54add9e3028ad96847355 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 3 Nov 2020 02:57:22 +0100 Subject: [PATCH 3/6] Add support for date format to wxGridCellDateEditor too This is needed to allow editing the cells using wxGridCellDateRenderer with a custom format, otherwise the editor might parse the contents of the cell differently from what is actually shown. In particular, this ensures that using "date:%d.%m.%y" (or any other custom format) as "cell type" works correctly, which wasn't the case before. --- include/wx/generic/grideditors.h | 5 ++++- interface/wx/grid.h | 8 +++++++- src/generic/grideditors.cpp | 17 +++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/wx/generic/grideditors.h b/include/wx/generic/grideditors.h index 4510d31944..025c9faff7 100644 --- a/include/wx/generic/grideditors.h +++ b/include/wx/generic/grideditors.h @@ -390,7 +390,9 @@ public: class WXDLLIMPEXP_ADV wxGridCellDateEditor : public wxGridCellEditor { public: - wxGridCellDateEditor() { } + explicit wxGridCellDateEditor(const wxString& format = wxString()); + + virtual void SetParameters(const wxString& params) wxOVERRIDE; virtual void Create(wxWindow* parent, wxWindowID id, @@ -414,6 +416,7 @@ protected: private: wxDateTime m_value; + wxString m_format; wxDECLARE_NO_COPY_CLASS(wxGridCellDateEditor); }; diff --git a/interface/wx/grid.h b/interface/wx/grid.h index 9e33e41e4d..4ef30b65ce 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -1092,8 +1092,14 @@ class wxGridCellDateEditor : public wxGridCellEditor public: /** Date editor constructor. + + @param format Optional format for the date displayed in the associated + cell. By default, the locale-specific date format ("%x") is assumed. + You would typically want to specify the same format as the one + used with the cell renderer, if a non-default one is used. + Note that this parameter is only available since wxWidgets 3.1.5. */ - wxGridCellDateEditor(); + explicit wxGridCellDateEditor(const wxString& format = wxString()); }; diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 0c7df337e4..3183f628fb 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1861,6 +1861,19 @@ struct wxGridCellDateEditorKeyHandler }; #endif // __WXGTK__ +wxGridCellDateEditor::wxGridCellDateEditor(const wxString& format) +{ + SetParameters(format); +} + +void wxGridCellDateEditor::SetParameters(const wxString& params) +{ + if ( params.empty() ) + m_format = "%x"; + else + m_format = params; +} + void wxGridCellDateEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler) { @@ -1908,7 +1921,7 @@ void wxGridCellDateEditor::BeginEdit(int row, int col, wxGrid* grid) wxASSERT_MSG(m_control, "The wxGridCellDateEditor must be created first!"); const wxString dateStr = grid->GetTable()->GetValue(row, col); - if ( !m_value.ParseDate(dateStr) ) + if ( !wxGridPrivate::TryParseDate(m_value, dateStr, m_format) ) { // Invalidate m_value, so that it always compares different // to any value returned from DatePicker()->GetValue(). @@ -1960,7 +1973,7 @@ void wxGridCellDateEditor::Reset() wxGridCellEditor *wxGridCellDateEditor::Clone() const { - return new wxGridCellDateEditor(); + return new wxGridCellDateEditor(m_format); } wxString wxGridCellDateEditor::GetValue() const From cced297b26bbf739223eabf77b880b948bde75c5 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 3 Nov 2020 03:09:17 +0100 Subject: [PATCH 4/6] Remove unused wxGridCellDateTimeRenderer::m_dateDef This has apparently never been really used ever since it was added in d10f4bf950 (applied tons of wxGrid patches, 2001-05-27). --- include/wx/generic/gridctrl.h | 4 +--- src/generic/gridctrl.cpp | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/include/wx/generic/gridctrl.h b/include/wx/generic/gridctrl.h index d55edddfd3..ac78ebe999 100644 --- a/include/wx/generic/gridctrl.h +++ b/include/wx/generic/gridctrl.h @@ -222,8 +222,7 @@ public: wxGridCellDateTimeRenderer(const wxGridCellDateTimeRenderer& other) : wxGridCellDateRenderer(other), - m_iformat(other.m_iformat), - m_dateDef(other.m_dateDef) + m_iformat(other.m_iformat) { } @@ -233,7 +232,6 @@ protected: virtual bool Parse(const wxString& text, wxDateTime& result) wxOVERRIDE; wxString m_iformat; - wxDateTime m_dateDef; }; #endif // wxUSE_DATETIME diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index c4b37fb421..8485e60ff3 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -208,7 +208,6 @@ void wxGridCellDateRenderer::SetParameters(const wxString& params) wxGridCellDateTimeRenderer::wxGridCellDateTimeRenderer(const wxString& outformat, const wxString& informat) : wxGridCellDateRenderer(outformat) , m_iformat(informat) - , m_dateDef(wxDefaultDateTime) { } @@ -219,7 +218,7 @@ wxGridCellRenderer *wxGridCellDateTimeRenderer::Clone() const bool wxGridCellDateTimeRenderer::Parse(const wxString& text, wxDateTime& result) { - const char * const end = result.ParseFormat(text, m_iformat, m_dateDef); + const char * const end = result.ParseFormat(text, m_iformat); return end && !*end; } From 9311dc8ffb25d97fe312b8405fd5dd1234e7b4d8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 3 Nov 2020 03:14:43 +0100 Subject: [PATCH 5/6] Remove redundant wxGridCellDateTimeRenderer::SetParameters() docs This function is inherited from wxGridCellDateRenderer since the changes of 659ab78c6d (Add support for editing dates (without time) to wxGrid, 2018-09-06), so there is no need to document it separately. --- interface/wx/grid.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/interface/wx/grid.h b/interface/wx/grid.h index 4ef30b65ce..3a6129a3f6 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -248,16 +248,6 @@ public: */ wxGridCellDateTimeRenderer(const wxString& outformat = wxDefaultDateTimeFormat, const wxString& informat = wxDefaultDateTimeFormat); - - - /** - Sets the strptime()-like format string which will be used to parse - the date/time. - - @param params - strptime()-like format string used to parse the date/time. - */ - virtual void SetParameters(const wxString& params); }; /** From ce0f10c3777c1c5e667505a0a8ac52af444bce0e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 3 Nov 2020 17:30:54 +0100 Subject: [PATCH 6/6] Get dates directly from table in wxGridCellDateEditor if possible Unlike wxGridCellDateRenderer, which already did it, the editor class always got the cell value from the table as a string, even if the table supported returning the dates directly. Fix this by using the same code in the editor as in the renderer, which required a further refactoring in order to make it reusable: the helper TryParseDate() was replaced with TryGetValueAsDate() and DateParseParams was added to allow overriding the arguments passed to it in the overridden wxGridCellDateTimeRenderer::GetDateParseParams(). --- include/wx/generic/gridctrl.h | 11 ++++- include/wx/generic/private/grid.h | 48 +++++++++++++++++-- src/generic/gridctrl.cpp | 79 +++++++++++++++++-------------- src/generic/grideditors.cpp | 6 ++- 4 files changed, 100 insertions(+), 44 deletions(-) diff --git a/include/wx/generic/gridctrl.h b/include/wx/generic/gridctrl.h index ac78ebe999..1bdb812751 100644 --- a/include/wx/generic/gridctrl.h +++ b/include/wx/generic/gridctrl.h @@ -171,6 +171,8 @@ public: #include "wx/datetime.h" +namespace wxGridPrivate { class DateParseParams; } + // renderer for the cells containing dates only, without time component class WXDLLIMPEXP_ADV wxGridCellDateRenderer : public wxGridCellStringRenderer { @@ -207,7 +209,11 @@ public: protected: wxString GetString(const wxGrid& grid, int row, int col); - virtual bool Parse(const wxString& text, wxDateTime& result); + + // This is overridden in wxGridCellDateTimeRenderer which uses a separate + // input format and forbids fallback to ParseDate(). + virtual void + GetDateParseParams(wxGridPrivate::DateParseParams& params) const; wxString m_oformat; wxDateTime::TimeZone m_tz; @@ -229,7 +235,8 @@ public: virtual wxGridCellRenderer *Clone() const wxOVERRIDE; protected: - virtual bool Parse(const wxString& text, wxDateTime& result) wxOVERRIDE; + virtual void + GetDateParseParams(wxGridPrivate::DateParseParams& params) const wxOVERRIDE; wxString m_iformat; }; diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index 19df9d4c76..07c432f588 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -1089,11 +1089,51 @@ namespace wxGridPrivate #if wxUSE_DATETIME -// Helper function trying to parse the given string using the specified date -// format and then using ParseDate() as a fallback if it failed. If this still -// fails, returns false. +// This is used as TryGetValueAsDate() parameter. +class DateParseParams +{ +public: + // Unfortunately we have to provide the default ctor (and also make the + // members non-const) because we use these objects as out-parameters as + // they are not fully declared in the public headers. The factory functions + // below must be used to create a really usable object. + DateParseParams() : fallbackParseDate(false) { } + + // Use these functions to really initialize the object. + static DateParseParams WithFallback(const wxString& format) + { + return DateParseParams(format, true); + } + + static DateParseParams WithoutFallback(const wxString& format) + { + return DateParseParams(format, false); + } + + // The usual format, e.g. "%x" or "%Y-%m-%d". + wxString format; + + // Whether fall back to ParseDate() is allowed. + bool fallbackParseDate; + +private: + DateParseParams(const wxString& format_, bool fallbackParseDate_) + : format(format_), + fallbackParseDate(fallbackParseDate_) + { + } +}; + +// Helper function trying to get a date from the given cell: if possible, get +// the date value from the table directly, otherwise get the string value for +// this cell and try to parse it using the specified date format and, if this +// doesn't work and fallbackParseDate is true, try using ParseDate() as a +// fallback. If this still fails, returns false. bool -TryParseDate(wxDateTime& result, const wxString& text, const wxString& format); +TryGetValueAsDate(wxDateTime& result, + const DateParseParams& params, + const wxGrid& grid, + int row, int col); #endif // wxUSE_DATETIME diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index 8485e60ff3..f1e83880d8 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -77,22 +77,46 @@ void wxGridCellRenderer::Draw(wxGrid& grid, #if wxUSE_DATETIME bool -wxGridPrivate::TryParseDate(wxDateTime& result, - const wxString& text, - const wxString& format) +wxGridPrivate::TryGetValueAsDate(wxDateTime& result, + const DateParseParams& params, + const wxGrid& grid, + int row, int col) { + wxGridTableBase *table = grid.GetTable(); + + if ( table->CanGetValueAs(row, col, wxGRID_VALUE_DATETIME) ) + { + void * tempval = table->GetValueAsCustom(row, col,wxGRID_VALUE_DATETIME); + + if (tempval) + { + result = *((wxDateTime *)tempval); + delete (wxDateTime *)tempval; + + return true; + } + + } + + const wxString text = table->GetValue(row, col); + wxString::const_iterator end; - // Try parsing using the same format we use for output first. - if ( result.ParseFormat(text, format, &end) && end == text.end() ) + if ( result.ParseFormat(text, params.format, &end) && end == text.end() ) return true; - // But fall back to free-form parsing, which notably allows us to parse - // strings such as "today" or "tomorrow" which would be never accepted by - // ParseFormat(). - return result.ParseDate(text, &end) && end == text.end(); + // Check if we can fall back to free-form parsing, which notably allows us + // to parse strings such as "today" or "tomorrow" which would be never + // accepted by ParseFormat(). + if ( params.fallbackParseDate && + result.ParseDate(text, &end) && end == text.end() ) + return true; + + return false; } +using namespace wxGridPrivate; + // Enables a grid cell to display a formatted date wxGridCellDateRenderer::wxGridCellDateRenderer(const wxString& outformat) @@ -115,40 +139,23 @@ wxGridCellRenderer *wxGridCellDateRenderer::Clone() const wxString wxGridCellDateRenderer::GetString(const wxGrid& grid, int row, int col) { - wxGridTableBase *table = grid.GetTable(); - - bool hasDatetime = false; - wxDateTime val; wxString text; - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_DATETIME) ) - { - void * tempval = table->GetValueAsCustom(row, col,wxGRID_VALUE_DATETIME); - if (tempval) - { - val = *((wxDateTime *)tempval); - hasDatetime = true; - delete (wxDateTime *)tempval; - } + DateParseParams params; + GetDateParseParams(params); - } - - if (!hasDatetime ) - { - text = table->GetValue(row, col); - hasDatetime = Parse(text, val); - } - - if ( hasDatetime ) + wxDateTime val; + if ( TryGetValueAsDate(val, params, grid, row, col) ) text = val.Format(m_oformat, m_tz ); // If we failed to parse string just show what we where given? return text; } -bool wxGridCellDateRenderer::Parse(const wxString& text, wxDateTime& result) +void +wxGridCellDateRenderer::GetDateParseParams(DateParseParams& params) const { - return wxGridPrivate::TryParseDate(result, text, m_oformat); + params = DateParseParams::WithFallback(m_oformat); } void wxGridCellDateRenderer::Draw(wxGrid& grid, @@ -216,10 +223,10 @@ wxGridCellRenderer *wxGridCellDateTimeRenderer::Clone() const return new wxGridCellDateTimeRenderer(*this); } -bool wxGridCellDateTimeRenderer::Parse(const wxString& text, wxDateTime& result) +void +wxGridCellDateTimeRenderer::GetDateParseParams(DateParseParams& params) const { - const char * const end = result.ParseFormat(text, m_iformat); - return end && !*end; + params = DateParseParams::WithoutFallback(m_iformat); } #endif // wxUSE_DATETIME diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 3183f628fb..5f0793bcd5 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -1920,8 +1920,10 @@ void wxGridCellDateEditor::BeginEdit(int row, int col, wxGrid* grid) { wxASSERT_MSG(m_control, "The wxGridCellDateEditor must be created first!"); - const wxString dateStr = grid->GetTable()->GetValue(row, col); - if ( !wxGridPrivate::TryParseDate(m_value, dateStr, m_format) ) + using namespace wxGridPrivate; + + if ( !TryGetValueAsDate(m_value, DateParseParams::WithFallback(m_format), + *grid, row, col) ) { // Invalidate m_value, so that it always compares different // to any value returned from DatePicker()->GetValue().