Do not emit a wxEVT_COMMAND_TEXT_UPDATED when the tab key is lifted (otherwise each...
[wxWidgets.git] / src / generic / datectlg.cpp
CommitLineData
39df3acd 1/////////////////////////////////////////////////////////////////////////////
d5ae99f5 2// Name: 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
15c86b39 32#if wxUSE_DATEPICKCTRL_GENERIC || !defined(wxHAS_NATIVE_DATEPICKCTRL)
7ae712f5 33
a962d4e0
KH
34#ifndef WX_PRECOMP
35 #include "wx/bmpbuttn.h"
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
7ae712f5 43// otherwise it's defined in the native version implementation
15c86b39 44#ifndef wxHAS_NATIVE_DATEPICKCTRL
7ae712f5
VZ
45 #define _WX_DEFINE_DATE_EVENTS_
46#endif
47
91edf16c 48#include "wx/dateevt.h"
b6292ff0 49#include "wx/generic/datectrl.h"
91edf16c 50
4b134bb2 51#include "wx/calctrl.h"
209afcf7 52#include "wx/popupwin.h"
4221a0e5 53#include "wx/renderer.h"
39df3acd
VZ
54
55// ----------------------------------------------------------------------------
56// constants
57// ----------------------------------------------------------------------------
58
59enum
60{
61 CTRLID_TXT = 101,
4b134bb2
VZ
62 CTRLID_CAL,
63 CTRLID_BTN,
39df3acd
VZ
64 CTRLID_PAN
65};
66
67#ifndef DEFAULT_ITEM_WIDTH
68 #define DEFAULT_ITEM_WIDTH 100
69#endif
70
71// ============================================================================
7ae712f5 72// wxDatePickerCtrlGeneric implementation
39df3acd
VZ
73// ============================================================================
74
7ae712f5
VZ
75BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
76 EVT_BUTTON(CTRLID_BTN, wxDatePickerCtrlGeneric::OnClick)
77 EVT_TEXT(CTRLID_TXT, wxDatePickerCtrlGeneric::OnText)
78 EVT_CHILD_FOCUS(wxDatePickerCtrlGeneric::OnChildSetFocus)
39df3acd
VZ
79END_EVENT_TABLE()
80
b6292ff0
VZ
81#ifndef wxHAS_NATIVE_DATEPICKCTRL
82 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxDatePickerCtrlBase)
83#endif
39df3acd
VZ
84
85// ----------------------------------------------------------------------------
86// creation
87// ----------------------------------------------------------------------------
88
7ae712f5
VZ
89bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
90 wxWindowID id,
91 const wxDateTime& date,
92 const wxPoint& pos,
93 const wxSize& size,
94 long style,
95 const wxString& name)
39df3acd 96{
29c86948
VZ
97 wxASSERT_MSG( !(style & wxDP_SPIN),
98 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
99
39df3acd
VZ
100 if ( !wxControl::Create(parent, id, pos, size,
101 style | wxCLIP_CHILDREN | wxWANTS_CHARS,
102 wxDefaultValidator, name) )
103
104 {
105 return false;
106 }
107
39df3acd
VZ
108 InheritAttributes();
109
4221a0e5 110 m_txt = new wxTextCtrl(this, CTRLID_TXT);
4b134bb2 111 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
7ae712f5 112 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnEditKey,
4b134bb2
VZ
113 0, this);
114 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS,
7ae712f5 115 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnKillFocus,
4b134bb2 116 0, this);
39df3acd 117
4221a0e5
VZ
118 const int height = m_txt->GetBestSize().y - 4; // FIXME: fudge
119 wxBitmap bmp(height, height);
120 {
121 wxMemoryDC dc;
122 dc.SelectObject(bmp);
123 wxRendererNative::Get().DrawComboBoxDropButton
124 (
125 this,
126 dc,
127 wxRect(0, 0, height, height)
128 );
129 }
130
131 wxBitmapButton *btn = new wxBitmapButton(this, CTRLID_BTN, bmp);
132 btn->SetMargins(0, 0);
133 m_btn = btn;
39df3acd 134
d5ae99f5
VZ
135 m_popup = new wxPopupWindow(this);
136 m_popup->SetFont(GetFont());
39df3acd 137
d5ae99f5 138 wxPanel *panel=new wxPanel(m_popup, CTRLID_PAN,
4b134bb2
VZ
139 wxPoint(0, 0), wxDefaultSize,
140 wxSUNKEN_BORDER);
141 m_cal = new wxCalendarCtrl(panel, CTRLID_CAL, wxDefaultDateTime,
142 wxPoint(0,0), wxDefaultSize,
143 wxCAL_SHOW_HOLIDAYS | wxSUNKEN_BORDER);
144 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_SEL_CHANGED,
7ae712f5 145 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
4b134bb2
VZ
146 0, this);
147 m_cal->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
7ae712f5 148 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnCalKey,
4b134bb2
VZ
149 0, this);
150 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DOUBLECLICKED,
7ae712f5 151 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
4b134bb2
VZ
152 0, this);
153 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DAY_CHANGED,
7ae712f5 154 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
4b134bb2
VZ
155 0, this);
156 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_MONTH_CHANGED,
7ae712f5 157 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
4b134bb2
VZ
158 0, this);
159 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_YEAR_CHANGED,
7ae712f5 160 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
4b134bb2 161 0, this);
39df3acd
VZ
162
163 wxWindow *yearControl = m_cal->GetYearControl();
164
4b134bb2 165 Connect(wxID_ANY, wxID_ANY, wxEVT_SET_FOCUS,
7ae712f5 166 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSetFocus);
39df3acd
VZ
167
168 wxClientDC dc(yearControl);
169 dc.SetFont(m_font);
170 wxCoord width, dummy;
171 dc.GetTextExtent(wxT("2000"), &width, &dummy);
172 width += ConvertDialogToPixels(wxSize(20,0)).x;
173
174 wxSize calSize = m_cal->GetBestSize();
175 wxSize yearSize = yearControl->GetSize();
176 yearSize.x = width;
177
178 wxPoint yearPosition = yearControl->GetPosition();
179
d5ae99f5
VZ
180 SetFormat(wxT("%x"));
181
182 if (date.IsValid())
183 m_txt->SetValue(date.Format(m_format));
184
185
39df3acd
VZ
186#ifdef __WXMSW__
187#define CALBORDER 0
188#else
189#define CALBORDER 4
190#endif
191
192 width = yearPosition.x + yearSize.x+2+CALBORDER/2;
193 if (width < calSize.x-4)
194 width = calSize.x-4;
195
196 int calPos = (width-calSize.x)/2;
197 if (calPos == -1)
198 {
199 calPos = 0;
200 width += 2;
201 }
202 m_cal->SetSize(calPos, 0, calSize.x, calSize.y);
4b134bb2
VZ
203 yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
204 yearSize.x, yearSize.y);
39df3acd
VZ
205 m_cal->GetMonthControl()->Move(0, 0);
206
207
208
209 panel->SetClientSize(width+CALBORDER/2, calSize.y-2+CALBORDER);
d5ae99f5
VZ
210 m_popup->SetClientSize(panel->GetSize());
211 m_popup->Hide();
39df3acd
VZ
212
213 return TRUE;
214}
215
216
7ae712f5 217void wxDatePickerCtrlGeneric::Init()
39df3acd 218{
d5ae99f5 219 m_popup = NULL;
39df3acd
VZ
220 m_txt = NULL;
221 m_cal = NULL;
222 m_btn = NULL;
223
224 m_dropped = false;
225 m_ignoreDrop = false;
226}
227
228
7ae712f5 229bool wxDatePickerCtrlGeneric::Destroy()
39df3acd
VZ
230{
231 if (m_cal)
232 m_cal->Destroy();
d5ae99f5
VZ
233 if (m_popup)
234 m_popup->Destroy();
39df3acd
VZ
235 if (m_txt)
236 m_txt->Destroy();
237 if (m_btn)
238 m_btn->Destroy();
239
d5ae99f5 240 m_popup = NULL;
39df3acd
VZ
241 m_txt = NULL;
242 m_cal = NULL;
243 m_btn = NULL;
244
245 return wxControl::Destroy();
246}
247
248// ----------------------------------------------------------------------------
249// overridden base class methods
250// ----------------------------------------------------------------------------
251
7ae712f5 252void wxDatePickerCtrlGeneric::DoMoveWindow(int x, int y, int w, int h)
39df3acd
VZ
253{
254 wxControl::DoMoveWindow(x, y, w, h);
255 wxSize bs=m_btn->GetBestSize();
256 int eh=m_txt->GetBestSize().y;
257
258 m_txt->SetSize(0, 0, w-bs.x-1, h > eh ? eh : h);
259 m_btn->SetSize(w - bs.x, 0, bs.x, h > bs.y ? bs.y : h);
260
261 if (m_dropped)
262 DropDown();
263}
264
7ae712f5 265wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
39df3acd
VZ
266{
267 int bh=m_btn->GetBestSize().y;
268 int eh=m_txt->GetBestSize().y;
269 return wxSize(DEFAULT_ITEM_WIDTH, bh > eh ? bh : eh);
270}
271
272
7ae712f5 273bool wxDatePickerCtrlGeneric::Show(bool show)
39df3acd
VZ
274{
275 if ( !wxControl::Show(show) )
276 {
277 return FALSE;
278 }
279
280 if (!show)
281 {
d5ae99f5 282 if (m_popup)
39df3acd 283 {
d5ae99f5 284 m_popup->Hide();
39df3acd
VZ
285 m_dropped = false;
286 }
287 }
288
289 return TRUE;
290}
291
292
7ae712f5 293bool wxDatePickerCtrlGeneric::Enable(bool enable)
39df3acd
VZ
294{
295 if ( !wxControl::Enable(enable) )
296 {
297 return FALSE;
298 }
299
300 if (!enable)
301 {
302 if (m_cal)
303 m_cal->Hide();
304 }
305 if (m_btn)
306 m_btn->Enable(enable);
307 return TRUE;
308}
309
310// ----------------------------------------------------------------------------
7ae712f5 311// wxDatePickerCtrlGeneric API
39df3acd
VZ
312// ----------------------------------------------------------------------------
313
4b134bb2 314bool
7ae712f5
VZ
315wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
316 const wxDateTime& upperdate)
4b134bb2
VZ
317{
318 return m_cal->SetDateRange(lowerdate, upperdate);
319}
320
7ae712f5 321bool wxDatePickerCtrlGeneric::SetFormat(const wxChar *fmt)
39df3acd 322{
d5ae99f5
VZ
323 wxString currentText;
324 wxDateTime currentDate;
325 if (m_txt)
326 {
327 currentText = m_txt->GetValue();
328 if (!currentText.IsEmpty())
329 currentDate.ParseFormat(currentText, m_format);
330 }
39df3acd
VZ
331 wxDateTime dt;
332 dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
333 wxString str=dt.Format(fmt);
334 wxChar *p=(wxChar*)str.c_str();
335
336 m_format=wxEmptyString;
337
338 while (*p)
339 {
340 int n=wxAtoi(p);
341 if (n == dt.GetDay())
342 {
343 m_format.Append(wxT("%d"));
344 p += 2;
345 }
346 else if (n == (int)dt.GetMonth()+1)
347 {
348 m_format.Append(wxT("%m"));
349 p += 2;
350 }
351 else if (n == dt.GetYear())
352 {
353 m_format.Append(wxT("%Y"));
354 p += 4;
355 }
d5ae99f5
VZ
356 else if (n == (dt.GetYear() % 100))
357 {
358 m_format.Append(wxT("%y"));
359 p += 2;
360 }
39df3acd
VZ
361 else
362 m_format.Append(*p++);
363 }
364
365 if (m_txt)
366 {
367 wxStringList valList;
368 wxChar c;
369 for (c='0'; c <= '9'; c++)
370 valList.Add(wxString(c, 1));
371 wxChar *p=(wxChar*)m_format.c_str();
372 while (*p)
373 {
374 if (*p == '%')
375 p += 2;
376 else
377 valList.Add(wxString(*p++, 1));
378 }
379 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
380 tv.SetIncludeList(valList);
381
382 m_txt->SetValidator(tv);
d5ae99f5
VZ
383
384 if (!currentText.IsEmpty())
385 m_txt->SetValue(currentDate.Format(m_format));
39df3acd
VZ
386 }
387 return true;
388}
389
390
7ae712f5 391wxDateTime wxDatePickerCtrlGeneric::GetValue() const
39df3acd
VZ
392{
393 wxDateTime dt;
394 wxString txt=m_txt->GetValue();
395
396 if (!txt.IsEmpty())
397 dt.ParseFormat(txt, m_format);
398
399 return dt;
400}
401
402
7ae712f5 403void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
39df3acd
VZ
404{
405 if (m_cal)
406 {
407 if (date.IsValid())
d5ae99f5 408 m_txt->SetValue(date.Format(m_format));
39df3acd
VZ
409 else
410 m_txt->SetValue(wxEmptyString);
411 }
412}
413
414
7ae712f5 415bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
39df3acd
VZ
416{
417 if (dt1)
4b134bb2 418 *dt1 = m_cal->GetLowerDateLimit();
39df3acd 419 if (dt1)
4b134bb2 420 *dt2 = m_cal->GetUpperDateLimit();
39df3acd
VZ
421 return true;
422}
423
424
7ae712f5
VZ
425void
426wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
39df3acd 427{
4b134bb2 428 m_cal->SetDateRange(dt1, dt2);
39df3acd
VZ
429}
430
431// ----------------------------------------------------------------------------
432// event handlers
433// ----------------------------------------------------------------------------
434
7ae712f5 435void wxDatePickerCtrlGeneric::DropDown(bool down)
39df3acd 436{
d5ae99f5 437 if (m_popup)
39df3acd
VZ
438 {
439 if (down)
440 {
d5ae99f5
VZ
441 wxDateTime dt;
442 if (!m_txt->GetValue().IsEmpty())
39df3acd 443 dt.ParseFormat(m_txt->GetValue(), m_format);
d5ae99f5
VZ
444
445 if (dt.IsValid())
39df3acd 446 m_cal->SetDate(dt);
d5ae99f5
VZ
447 else
448 m_cal->SetDate(wxDateTime::Today());
39df3acd 449
d5ae99f5
VZ
450 wxPoint pos=GetParent()->ClientToScreen(GetPosition());
451 m_popup->Move(pos.x, pos.y + GetSize().y);
452 m_popup->Show();
39df3acd
VZ
453 m_dropped = true;
454 }
455 else
456 {
457 if (m_dropped)
d5ae99f5 458 m_popup->Hide();
39df3acd
VZ
459 m_dropped = false;
460 }
461 }
462}
463
464
7ae712f5 465void wxDatePickerCtrlGeneric::OnChildSetFocus(wxChildFocusEvent &ev)
39df3acd
VZ
466{
467 ev.Skip();
468 m_ignoreDrop = false;
469
470 wxWindow *w=(wxWindow*)ev.GetEventObject();
471 while (w)
472 {
d5ae99f5 473 if (w == m_popup)
39df3acd
VZ
474 return;
475 w = w->GetParent();
476 }
477
478 if (m_dropped)
479 {
480 DropDown(false);
481 if (ev.GetEventObject() == m_btn)
482 m_ignoreDrop = true;
483 }
484}
485
486
b6292ff0 487void wxDatePickerCtrlGeneric::OnClick(wxCommandEvent& WXUNUSED(event))
39df3acd
VZ
488{
489 if (m_ignoreDrop)
490 {
491 m_ignoreDrop = false;
492 m_txt->SetFocus();
493 }
494 else
495 {
496 DropDown();
497 m_cal->SetFocus();
498 }
499}
500
501
b6292ff0 502void wxDatePickerCtrlGeneric::OnSetFocus(wxFocusEvent& WXUNUSED(ev))
39df3acd
VZ
503{
504 if (m_txt)
505 {
506 m_txt->SetFocus();
507 m_txt->SetSelection(0, 100);
508 }
509}
510
511
7ae712f5 512void wxDatePickerCtrlGeneric::OnKillFocus(wxFocusEvent &ev)
39df3acd
VZ
513{
514 ev.Skip();
515
516 wxDateTime dt;
517 dt.ParseFormat(m_txt->GetValue(), m_format);
518 if (!dt.IsValid())
519 m_txt->SetValue(wxEmptyString);
520 else
521 m_txt->SetValue(dt.Format(m_format));
522}
523
524
7ae712f5 525void wxDatePickerCtrlGeneric::OnSelChange(wxCalendarEvent &ev)
39df3acd
VZ
526{
527 if (m_cal)
528 {
d5ae99f5 529 m_txt->SetValue(m_cal->GetDate().Format(m_format));
39df3acd
VZ
530 if (ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED)
531 {
532 DropDown(false);
533 m_txt->SetFocus();
534 }
535 }
536 ev.SetEventObject(this);
537 ev.SetId(GetId());
538 GetParent()->ProcessEvent(ev);
539}
540
541
7ae712f5 542void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
39df3acd
VZ
543{
544 ev.SetEventObject(this);
545 ev.SetId(GetId());
546 GetParent()->ProcessEvent(ev);
547
548 // We'll create an additional event if the date is valid.
549 // If the date isn't valid, the user's probable in the middle of typing
550 wxString txt=m_txt->GetValue();
551 wxDateTime dt;
552 if (!txt.IsEmpty())
553 {
554 dt.ParseFormat(txt, m_format);
555 if (!dt.IsValid())
556 return;
557 }
558
559 wxCalendarEvent cev(m_cal, wxEVT_CALENDAR_SEL_CHANGED);
560 cev.SetEventObject(this);
561 cev.SetId(GetId());
562 cev.SetDate(dt);
563
564 GetParent()->ProcessEvent(cev);
565}
566
567
7ae712f5 568void wxDatePickerCtrlGeneric::OnEditKey(wxKeyEvent & ev)
39df3acd
VZ
569{
570 if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers())
571 DropDown();
572 else
573 ev.Skip();
574}
575
576
7ae712f5 577void wxDatePickerCtrlGeneric::OnCalKey(wxKeyEvent & ev)
39df3acd
VZ
578{
579 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
580 DropDown(false);
581 else
582 ev.Skip();
583}
584
7ae712f5
VZ
585#endif // wxUSE_DATEPICKCTRL_GENERIC
586
587#endif // wxUSE_DATEPICKCTRL
588