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