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