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