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