]> git.saurik.com Git - wxWidgets.git/blame - src/generic/datectlg.cpp
work around probable bug in GTK+ 2.18 when calling WriteText on a new, empty control...
[wxWidgets.git] / src / generic / datectlg.cpp
CommitLineData
39df3acd 1/////////////////////////////////////////////////////////////////////////////
68f2155b 2// Name: src/generic/datectlg.cpp
7ae712f5 3// Purpose: generic wxDatePickerCtrlGeneric implementation
39df3acd
VZ
4// Author: Andreas Pflug
5// Modified by:
6// Created: 2005-01-19
7// RCS-ID: $Id$
8// Copyright: (c) 2005 Andreas Pflug <pgadmin@pse-consulting.de>
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
7ae712f5
VZ
26#if wxUSE_DATEPICKCTRL
27
a962d4e0 28#ifndef WX_PRECOMP
a962d4e0
KH
29 #include "wx/dialog.h"
30 #include "wx/dcmemory.h"
31 #include "wx/panel.h"
32 #include "wx/textctrl.h"
33 #include "wx/valtext.h"
34#endif
35
c245a012
VZ
36#include "wx/datectrl.h"
37#include "wx/generic/datectrl.h"
91edf16c 38
39df3acd
VZ
39
40// ----------------------------------------------------------------------------
41// constants
42// ----------------------------------------------------------------------------
43
930fb29e 44
aa74ad5b
VZ
45// ----------------------------------------------------------------------------
46// global variables
47// ----------------------------------------------------------------------------
48
930fb29e 49
1721a8c0
VZ
50// ----------------------------------------------------------------------------
51// local classes
52// ----------------------------------------------------------------------------
53
c245a012 54class wxCalendarComboPopup : public wxCalendarCtrl,
85fa9d60 55 public wxComboPopup
38511687
VZ
56{
57public:
38511687 58
c245a012 59 wxCalendarComboPopup() : wxCalendarCtrl(),
85fa9d60 60 wxComboPopup()
caad7637 61 {
caad7637
JS
62 }
63
85fa9d60
VZ
64 virtual void Init()
65 {
66 }
caad7637 67
85fa9d60
VZ
68 // NB: Don't create lazily since it didn't work that way before
69 // wxComboCtrl was used, and changing behaviour would almost
70 // certainly introduce new bugs.
71 virtual bool Create(wxWindow* parent)
72 {
c245a012 73 if ( !wxCalendarCtrl::Create(parent, wxID_ANY, wxDefaultDateTime,
85fa9d60 74 wxPoint(0, 0), wxDefaultSize,
6c70323f 75 wxCAL_SEQUENTIAL_MONTH_SELECTION
c245a012 76 | wxCAL_SHOW_HOLIDAYS | wxBORDER_SUNKEN) )
85fa9d60 77 return false;
caad7637 78
6c70323f 79 SetFormat(GetLocaleDateFormat());
caad7637 80
c245a012 81 m_useSize = wxCalendarCtrl::GetBestSize();
caad7637 82
85fa9d60
VZ
83 wxWindow* tx = m_combo->GetTextCtrl();
84 if ( !tx )
85 tx = m_combo;
caad7637 86
85fa9d60
VZ
87 tx->Connect(wxEVT_KILL_FOCUS,
88 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus),
89 NULL, this);
caad7637 90
85fa9d60
VZ
91 return true;
92 }
38511687 93
85fa9d60
VZ
94 virtual wxSize GetAdjustedSize(int WXUNUSED(minWidth),
95 int WXUNUSED(prefHeight),
96 int WXUNUSED(maxHeight))
97 {
98 return m_useSize;
99 }
38511687 100
85fa9d60 101 virtual wxWindow *GetControl() { return this; }
930fb29e 102
85fa9d60
VZ
103 void SetDateValue(const wxDateTime& date)
104 {
105 if ( date.IsValid() )
106 {
107 m_combo->SetText(date.Format(m_format));
d6781628 108 SetDate(date);
85fa9d60
VZ
109 }
110 else // invalid date
111 {
112 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE),
9a83f860 113 wxT("this control must have a valid date") );
caad7637 114
85fa9d60
VZ
115 m_combo->SetText(wxEmptyString);
116 }
85fa9d60 117 }
94ab4d92 118
2ccc6650
VZ
119 bool IsTextEmpty() const
120 {
121 return m_combo->GetTextCtrl()->IsEmpty();
122 }
123
85fa9d60
VZ
124 bool ParseDateTime(const wxString& s, wxDateTime* pDt)
125 {
126 wxASSERT(pDt);
38511687 127
85fa9d60
VZ
128 if ( !s.empty() )
129 {
e0a050e3 130 pDt->ParseFormat(s.c_str(), m_format);
85fa9d60
VZ
131 if ( !pDt->IsValid() )
132 return false;
133 }
38511687 134
85fa9d60
VZ
135 return true;
136 }
38511687 137
85fa9d60
VZ
138 void SendDateEvent(const wxDateTime& dt)
139 {
85fa9d60
VZ
140 // Sends both wxCalendarEvent and wxDateEvent
141 wxWindow* datePicker = m_combo->GetParent();
38511687 142
628e155d 143 wxCalendarEvent cev(datePicker, dt, wxEVT_CALENDAR_SEL_CHANGED);
d1b736b7 144 datePicker->GetEventHandler()->ProcessEvent(cev);
38511687 145
85fa9d60 146 wxDateEvent event(datePicker, dt, wxEVT_DATE_CHANGED);
d1b736b7 147 datePicker->GetEventHandler()->ProcessEvent(event);
85fa9d60 148 }
caad7637 149
85fa9d60 150private:
caad7637 151
85fa9d60 152 void OnCalKey(wxKeyEvent & ev)
caad7637 153 {
85fa9d60
VZ
154 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
155 Dismiss();
156 else
157 ev.Skip();
caad7637 158 }
caad7637 159
85fa9d60
VZ
160 void OnSelChange(wxCalendarEvent &ev)
161 {
2fda1fe5 162 m_combo->SetText(GetDate().Format(m_format));
caad7637 163
85fa9d60
VZ
164 if ( ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED )
165 {
166 Dismiss();
167 }
caad7637 168
2fda1fe5 169 SendDateEvent(GetDate());
38511687
VZ
170 }
171
85fa9d60
VZ
172 void OnKillTextFocus(wxFocusEvent &ev)
173 {
174 ev.Skip();
caad7637 175
2fda1fe5
VZ
176 const wxDateTime& dtOld = GetDate();
177
85fa9d60
VZ
178 wxDateTime dt;
179 wxString value = m_combo->GetValue();
180 if ( !ParseDateTime(value, &dt) )
181 {
182 if ( !HasDPFlag(wxDP_ALLOWNONE) )
2fda1fe5 183 dt = dtOld;
85fa9d60 184 }
caad7637 185
2fda1fe5 186 m_combo->SetText(GetStringValueFor(dt));
38511687 187
d6781628
RD
188 if ( !dt.IsValid() && HasDPFlag(wxDP_ALLOWNONE) )
189 return;
6c70323f 190
85fa9d60 191 // notify that we had to change the date after validation
2fda1fe5
VZ
192 if ( (dt.IsValid() && (!dtOld.IsValid() || dt != dtOld)) ||
193 (!dt.IsValid() && dtOld.IsValid()) )
85fa9d60 194 {
2fda1fe5 195 SetDate(dt);
85fa9d60
VZ
196 SendDateEvent(dt);
197 }
198 }
38511687 199
6c70323f 200 bool HasDPFlag(int flag) const
caad7637 201 {
85fa9d60 202 return m_combo->GetParent()->HasFlag(flag);
caad7637 203 }
caad7637 204
6c70323f
VZ
205 // it expands "%x" format and changes %y to %Y if wxDP_SHOWCENTURY flag
206 // is given. If the locale format can't be easily analyzed (e.g. when
207 // the month is given as a name, not number), "%x" is returned
208 wxString GetLocaleDateFormat() const
85fa9d60 209 {
6c70323f
VZ
210 wxString x_format(wxT("%x"));
211 wxString fmt;
212 int year_cnt = 0, month_cnt = 0, day_cnt = 0;
caad7637 213
85fa9d60 214 wxDateTime dt;
6c70323f
VZ
215 dt.ParseFormat(wxT("2003-10-17"), wxT("%Y-%m-%d"));
216 wxString str(dt.Format(x_format));
1721a8c0 217
85fa9d60
VZ
218 const wxChar *p = str.c_str();
219 while ( *p )
220 {
6c70323f 221 if (wxIsdigit(*p))
85fa9d60 222 {
6c70323f
VZ
223 int n=wxAtoi(p);
224 if (n == dt.GetDay())
225 {
226 fmt.Append(wxT("%d"));
227 day_cnt++;
228 p += 2;
229 }
230 else if (n == (int)dt.GetMonth()+1)
231 {
232 fmt.Append(wxT("%m"));
233 month_cnt++;
234 p += 2;
235 }
236 else if (n == dt.GetYear())
237 {
238 fmt.Append(wxT("%Y"));
239 year_cnt++;
240 p += 4;
241 }
242 else if (n == (dt.GetYear() % 100))
243 {
244 if ( HasDPFlag(wxDP_SHOWCENTURY) )
245 fmt.Append(wxT("%Y"));
246 else
247 fmt.Append(wxT("%y"));
248 year_cnt++;
249 p += 2;
250 }
85fa9d60 251 else
6c70323f
VZ
252 // this shouldn't happen
253 return x_format;
254 }
255 else {
256 fmt.Append(*p);
257 p++;
85fa9d60 258 }
85fa9d60 259 }
1721a8c0 260
6c70323f
VZ
261 if (year_cnt == 1 && month_cnt == 1 && day_cnt == 1)
262 return fmt;
8f2a8de6
VZ
263
264 return x_format;
6c70323f
VZ
265 }
266
267 bool SetFormat(const wxString& fmt)
268 {
269 m_format = fmt;
270
85fa9d60
VZ
271 if ( m_combo )
272 {
273 wxArrayString allowedChars;
9a83f860 274 for ( wxChar c = wxT('0'); c <= wxT('9'); c++ )
85fa9d60
VZ
275 allowedChars.Add(wxString(c, 1));
276
277 const wxChar *p2 = m_format.c_str();
278 while ( *p2 )
279 {
280 if ( *p2 == '%')
281 p2 += 2;
282 else
283 allowedChars.Add(wxString(*p2++, 1));
284 }
285
286 #if wxUSE_VALIDATORS
287 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
288 tv.SetIncludes(allowedChars);
289 m_combo->SetValidator(tv);
290 #endif
291
2fda1fe5
VZ
292 if ( GetDate().IsValid() )
293 m_combo->SetText(GetDate().Format(m_format));
85fa9d60 294 }
1721a8c0 295
85fa9d60 296 return true;
1721a8c0
VZ
297 }
298
85fa9d60 299 virtual void SetStringValue(const wxString& s)
1721a8c0 300 {
85fa9d60 301 wxDateTime dt;
d6781628 302 if ( !s.empty() && ParseDateTime(s, &dt) )
2fda1fe5 303 SetDate(dt);
d6781628 304 //else: keep the old value
1721a8c0 305 }
1721a8c0 306
85fa9d60 307 virtual wxString GetStringValue() const
1721a8c0 308 {
2fda1fe5 309 return GetStringValueFor(GetDate());
1721a8c0
VZ
310 }
311
85fa9d60 312private:
2fda1fe5
VZ
313 // returns either the given date representation using the current format or
314 // an empty string if it's invalid
315 wxString GetStringValueFor(const wxDateTime& dt) const
316 {
317 wxString val;
318 if ( dt.IsValid() )
319 val = dt.Format(m_format);
320
321 return val;
322 }
85fa9d60
VZ
323
324 wxSize m_useSize;
325 wxString m_format;
85fa9d60
VZ
326
327 DECLARE_EVENT_TABLE()
1721a8c0
VZ
328};
329
85fa9d60 330
c245a012 331BEGIN_EVENT_TABLE(wxCalendarComboPopup, wxCalendarCtrl)
85fa9d60
VZ
332 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey)
333 EVT_CALENDAR_SEL_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
628e155d 334 EVT_CALENDAR_PAGE_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
85fa9d60
VZ
335 EVT_CALENDAR(wxID_ANY, wxCalendarComboPopup::OnSelChange)
336END_EVENT_TABLE()
337
1721a8c0 338
39df3acd 339// ============================================================================
7ae712f5 340// wxDatePickerCtrlGeneric implementation
39df3acd
VZ
341// ============================================================================
342
7ae712f5 343BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
85fa9d60 344 EVT_TEXT(wxID_ANY, wxDatePickerCtrlGeneric::OnText)
caad7637 345 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize)
4427c0a3 346 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus)
39df3acd
VZ
347END_EVENT_TABLE()
348
b6292ff0 349#ifndef wxHAS_NATIVE_DATEPICKCTRL
94ab4d92 350 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
b6292ff0 351#endif
39df3acd
VZ
352
353// ----------------------------------------------------------------------------
354// creation
355// ----------------------------------------------------------------------------
356
7ae712f5
VZ
357bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
358 wxWindowID id,
359 const wxDateTime& date,
360 const wxPoint& pos,
361 const wxSize& size,
362 long style,
807f5038 363 const wxValidator& validator,
7ae712f5 364 const wxString& name)
39df3acd 365{
29c86948 366 wxASSERT_MSG( !(style & wxDP_SPIN),
9a83f860 367 wxT("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
29c86948 368
39df3acd 369 if ( !wxControl::Create(parent, id, pos, size,
85fa9d60 370 style | wxCLIP_CHILDREN | wxWANTS_CHARS | wxBORDER_NONE,
807f5038 371 validator, name) )
39df3acd
VZ
372 {
373 return false;
374 }
375
39df3acd
VZ
376 InheritAttributes();
377
85fa9d60
VZ
378 m_combo = new wxComboCtrl(this, -1, wxEmptyString,
379 wxDefaultPosition, wxDefaultSize);
39df3acd 380
4427c0a3
RR
381 m_combo->SetCtrlMainWnd(this);
382
85fa9d60 383 m_popup = new wxCalendarComboPopup();
39df3acd 384
06077aaf
VZ
385#if defined(__WXMSW__)
386 // without this keyboard navigation in month control doesn't work
387 m_combo->UseAltPopupWindow();
388#endif
85fa9d60 389 m_combo->SetPopupControl(m_popup);
39df3acd 390
85fa9d60 391 m_popup->SetDateValue(date.IsValid() ? date : wxDateTime::Today());
1721a8c0 392
170acdc9 393 SetInitialSize(size);
930fb29e 394
3a0c6181 395 return true;
39df3acd
VZ
396}
397
398
7ae712f5 399void wxDatePickerCtrlGeneric::Init()
39df3acd 400{
85fa9d60 401 m_combo = NULL;
85fa9d60 402 m_popup = NULL;
39df3acd
VZ
403}
404
a9d13e15
JS
405wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
406{
a9d13e15 407}
39df3acd 408
7ae712f5 409bool wxDatePickerCtrlGeneric::Destroy()
39df3acd 410{
85fa9d60
VZ
411 if ( m_combo )
412 m_combo->Destroy();
39df3acd 413
85fa9d60 414 m_combo = NULL;
85fa9d60 415 m_popup = NULL;
39df3acd
VZ
416
417 return wxControl::Destroy();
418}
419
420// ----------------------------------------------------------------------------
421// overridden base class methods
422// ----------------------------------------------------------------------------
423
7ae712f5 424wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
39df3acd 425{
85fa9d60 426 return m_combo->GetBestSize();
39df3acd
VZ
427}
428
429// ----------------------------------------------------------------------------
7ae712f5 430// wxDatePickerCtrlGeneric API
39df3acd
VZ
431// ----------------------------------------------------------------------------
432
4b134bb2 433bool
7ae712f5
VZ
434wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
435 const wxDateTime& upperdate)
4b134bb2 436{
2fda1fe5 437 return m_popup->SetDateRange(lowerdate, upperdate);
4b134bb2
VZ
438}
439
39df3acd 440
7ae712f5 441wxDateTime wxDatePickerCtrlGeneric::GetValue() const
39df3acd 442{
2ccc6650
VZ
443 if ( HasFlag(wxDP_ALLOWNONE) && m_popup->IsTextEmpty() )
444 return wxInvalidDateTime;
2fda1fe5 445 return m_popup->GetDate();
39df3acd
VZ
446}
447
448
7ae712f5 449void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
39df3acd 450{
85fa9d60 451 m_popup->SetDateValue(date);
39df3acd
VZ
452}
453
454
7ae712f5 455bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
39df3acd 456{
c245a012 457 return m_popup->GetDateRange(dt1, dt2);
39df3acd
VZ
458}
459
460
7ae712f5
VZ
461void
462wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
39df3acd 463{
2fda1fe5
VZ
464 m_popup->SetDateRange(dt1, dt2);
465}
466
c245a012 467wxCalendarCtrl *wxDatePickerCtrlGeneric::GetCalendar() const
2fda1fe5
VZ
468{
469 return m_popup;
39df3acd
VZ
470}
471
472// ----------------------------------------------------------------------------
473// event handlers
474// ----------------------------------------------------------------------------
475
39df3acd 476
caad7637
JS
477void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent& event)
478{
85fa9d60
VZ
479 if ( m_combo )
480 m_combo->SetSize(GetClientSize());
caad7637
JS
481
482 event.Skip();
483}
484
485
7ae712f5 486void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
39df3acd
VZ
487{
488 ev.SetEventObject(this);
489 ev.SetId(GetId());
eafd76b0 490 GetParent()->GetEventHandler()->ProcessEvent(ev);
39df3acd
VZ
491
492 // We'll create an additional event if the date is valid.
1721a8c0 493 // If the date isn't valid, the user's probably in the middle of typing
39df3acd 494 wxDateTime dt;
b0fb790b 495 if ( !m_popup || !m_popup->ParseDateTime(m_combo->GetValue(), &dt) )
85fa9d60 496 return;
39df3acd 497
85fa9d60 498 m_popup->SendDateEvent(dt);
39df3acd
VZ
499}
500
501
4427c0a3
RR
502void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent& WXUNUSED(event))
503{
504 m_combo->SetFocus();
505}
506
507
7ae712f5 508#endif // wxUSE_DATEPICKCTRL
85fa9d60 509