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