switched to using wxPopupWindow
[wxWidgets.git] / src / generic / datectlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: generic/datectlg.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 #ifndef WX_PRECOMP
27 #include "wx/bmpbuttn.h"
28 #include "wx/dialog.h"
29 #include "wx/dcmemory.h"
30 #include "wx/panel.h"
31 #include "wx/textctrl.h"
32 #include "wx/valtext.h"
33 #endif
34
35 #define _WX_DEFINE_DATE_EVENTS_
36 #include "wx/dateevt.h"
37
38 #include "wx/datectrl.h"
39 #include "wx/calctrl.h"
40
41 // ----------------------------------------------------------------------------
42 // constants
43 // ----------------------------------------------------------------------------
44
45 enum
46 {
47 CTRLID_TXT = 101,
48 CTRLID_CAL,
49 CTRLID_BTN,
50 CTRLID_PAN
51 };
52
53 #ifndef DEFAULT_ITEM_WIDTH
54 #define DEFAULT_ITEM_WIDTH 100
55 #endif
56
57 // ============================================================================
58 // wxDatePickerCtrl implementation
59 // ============================================================================
60
61 BEGIN_EVENT_TABLE(wxDatePickerCtrl, wxDatePickerCtrlBase)
62 EVT_BUTTON(CTRLID_BTN, wxDatePickerCtrl::OnClick)
63 EVT_TEXT(CTRLID_TXT, wxDatePickerCtrl::OnText)
64 EVT_CHILD_FOCUS(wxDatePickerCtrl::OnChildSetFocus)
65 END_EVENT_TABLE()
66
67 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxDatePickerCtrlBase)
68
69 // ----------------------------------------------------------------------------
70 // creation
71 // ----------------------------------------------------------------------------
72
73 wxDatePickerCtrl::wxDatePickerCtrl(wxWindow *parent,
74 wxWindowID id,
75 const wxDateTime& date,
76 const wxPoint& pos,
77 const wxSize& size,
78 long style,
79 const wxString& name)
80 {
81 Init();
82 Create(parent, id, date, pos, size, style, name);
83 }
84
85
86 bool wxDatePickerCtrl::Create(wxWindow *parent,
87 wxWindowID id,
88 const wxDateTime& date,
89 const wxPoint& pos,
90 const wxSize& size,
91 long style,
92 const wxString& name)
93 {
94 wxASSERT_MSG( !(style & wxDP_SPIN),
95 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
96
97 if ( !wxControl::Create(parent, id, pos, size,
98 style | wxCLIP_CHILDREN | wxWANTS_CHARS,
99 wxDefaultValidator, name) )
100
101 {
102 return false;
103 }
104
105 SetWindowStyle(style | wxWANTS_CHARS);
106 InheritAttributes();
107
108 wxBitmap bmp(8, 4);
109 {
110 wxMemoryDC dc;
111
112 dc.SelectObject(bmp);
113 dc.SetBrush(wxBrush(GetBackgroundColour()));
114 dc.SetPen(wxPen(GetBackgroundColour()));
115 dc.DrawRectangle(0,0, 8,4);
116
117 dc.SetBrush(wxBrush(GetForegroundColour()));
118 dc.SetPen(wxPen(GetForegroundColour()));
119 wxPoint pt[3] = { wxPoint(0,0), wxPoint(6,0), wxPoint(3,3) };
120 dc.DrawPolygon(3, pt);
121 dc.SelectObject(wxNullBitmap);
122 }
123
124 m_txt=new wxTextCtrl(this, CTRLID_TXT);
125 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
126 (wxObjectEventFunction)&wxDatePickerCtrl::OnEditKey,
127 0, this);
128 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS,
129 (wxObjectEventFunction)&wxDatePickerCtrl::OnKillFocus,
130 0, this);
131
132 m_btn = new wxBitmapButton(this, CTRLID_BTN, bmp);
133
134 m_popup = new wxPopupWindow(this);
135 m_popup->SetFont(GetFont());
136
137 wxPanel *panel=new wxPanel(m_popup, CTRLID_PAN,
138 wxPoint(0, 0), wxDefaultSize,
139 wxSUNKEN_BORDER);
140 m_cal = new wxCalendarCtrl(panel, CTRLID_CAL, wxDefaultDateTime,
141 wxPoint(0,0), wxDefaultSize,
142 wxCAL_SHOW_HOLIDAYS | wxSUNKEN_BORDER);
143 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_SEL_CHANGED,
144 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
145 0, this);
146 m_cal->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
147 (wxObjectEventFunction)&wxDatePickerCtrl::OnCalKey,
148 0, this);
149 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DOUBLECLICKED,
150 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
151 0, this);
152 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DAY_CHANGED,
153 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
154 0, this);
155 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_MONTH_CHANGED,
156 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
157 0, this);
158 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_YEAR_CHANGED,
159 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
160 0, this);
161
162 wxWindow *yearControl = m_cal->GetYearControl();
163
164 Connect(wxID_ANY, wxID_ANY, wxEVT_SET_FOCUS,
165 (wxObjectEventFunction)&wxDatePickerCtrl::OnSetFocus);
166
167 wxClientDC dc(yearControl);
168 dc.SetFont(m_font);
169 wxCoord width, dummy;
170 dc.GetTextExtent(wxT("2000"), &width, &dummy);
171 width += ConvertDialogToPixels(wxSize(20,0)).x;
172
173 wxSize calSize = m_cal->GetBestSize();
174 wxSize yearSize = yearControl->GetSize();
175 yearSize.x = width;
176
177 wxPoint yearPosition = yearControl->GetPosition();
178
179 SetFormat(wxT("%x"));
180
181 if (date.IsValid())
182 m_txt->SetValue(date.Format(m_format));
183
184
185 #ifdef __WXMSW__
186 #define CALBORDER 0
187 #else
188 #define CALBORDER 4
189 #endif
190
191 width = yearPosition.x + yearSize.x+2+CALBORDER/2;
192 if (width < calSize.x-4)
193 width = calSize.x-4;
194
195 int calPos = (width-calSize.x)/2;
196 if (calPos == -1)
197 {
198 calPos = 0;
199 width += 2;
200 }
201 m_cal->SetSize(calPos, 0, calSize.x, calSize.y);
202 yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
203 yearSize.x, yearSize.y);
204 m_cal->GetMonthControl()->Move(0, 0);
205
206
207
208 panel->SetClientSize(width+CALBORDER/2, calSize.y-2+CALBORDER);
209 m_popup->SetClientSize(panel->GetSize());
210 m_popup->Hide();
211
212 return TRUE;
213 }
214
215
216 void wxDatePickerCtrl::Init()
217 {
218 m_popup = NULL;
219 m_txt = NULL;
220 m_cal = NULL;
221 m_btn = NULL;
222
223 m_dropped = false;
224 m_ignoreDrop = false;
225 }
226
227
228 bool wxDatePickerCtrl::Destroy()
229 {
230 if (m_cal)
231 m_cal->Destroy();
232 if (m_popup)
233 m_popup->Destroy();
234 if (m_txt)
235 m_txt->Destroy();
236 if (m_btn)
237 m_btn->Destroy();
238
239 m_popup = NULL;
240 m_txt = NULL;
241 m_cal = NULL;
242 m_btn = NULL;
243
244 return wxControl::Destroy();
245 }
246
247 // ----------------------------------------------------------------------------
248 // overridden base class methods
249 // ----------------------------------------------------------------------------
250
251 void wxDatePickerCtrl::DoMoveWindow(int x, int y, int w, int h)
252 {
253 wxControl::DoMoveWindow(x, y, w, h);
254 wxSize bs=m_btn->GetBestSize();
255 int eh=m_txt->GetBestSize().y;
256
257 m_txt->SetSize(0, 0, w-bs.x-1, h > eh ? eh : h);
258 m_btn->SetSize(w - bs.x, 0, bs.x, h > bs.y ? bs.y : h);
259
260 if (m_dropped)
261 DropDown();
262 }
263
264 wxSize wxDatePickerCtrl::DoGetBestSize() const
265 {
266 int bh=m_btn->GetBestSize().y;
267 int eh=m_txt->GetBestSize().y;
268 return wxSize(DEFAULT_ITEM_WIDTH, bh > eh ? bh : eh);
269 }
270
271
272 bool wxDatePickerCtrl::Show(bool show)
273 {
274 if ( !wxControl::Show(show) )
275 {
276 return FALSE;
277 }
278
279 if (!show)
280 {
281 if (m_popup)
282 {
283 m_popup->Hide();
284 m_dropped = false;
285 }
286 }
287
288 return TRUE;
289 }
290
291
292 bool wxDatePickerCtrl::Enable(bool enable)
293 {
294 if ( !wxControl::Enable(enable) )
295 {
296 return FALSE;
297 }
298
299 if (!enable)
300 {
301 if (m_cal)
302 m_cal->Hide();
303 }
304 if (m_btn)
305 m_btn->Enable(enable);
306 return TRUE;
307 }
308
309 // ----------------------------------------------------------------------------
310 // wxDatePickerCtrl API
311 // ----------------------------------------------------------------------------
312
313 bool
314 wxDatePickerCtrl::SetDateRange(const wxDateTime& lowerdate,
315 const wxDateTime& upperdate)
316 {
317 return m_cal->SetDateRange(lowerdate, upperdate);
318 }
319
320 bool wxDatePickerCtrl::SetFormat(const wxChar *fmt)
321 {
322 wxString currentText;
323 wxDateTime currentDate;
324 if (m_txt)
325 {
326 currentText = m_txt->GetValue();
327 if (!currentText.IsEmpty())
328 currentDate.ParseFormat(currentText, m_format);
329 }
330 wxDateTime dt;
331 dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
332 wxString str=dt.Format(fmt);
333 wxChar *p=(wxChar*)str.c_str();
334
335 m_format=wxEmptyString;
336
337 while (*p)
338 {
339 int n=wxAtoi(p);
340 if (n == dt.GetDay())
341 {
342 m_format.Append(wxT("%d"));
343 p += 2;
344 }
345 else if (n == (int)dt.GetMonth()+1)
346 {
347 m_format.Append(wxT("%m"));
348 p += 2;
349 }
350 else if (n == dt.GetYear())
351 {
352 m_format.Append(wxT("%Y"));
353 p += 4;
354 }
355 else if (n == (dt.GetYear() % 100))
356 {
357 m_format.Append(wxT("%y"));
358 p += 2;
359 }
360 else
361 m_format.Append(*p++);
362 }
363
364 if (m_txt)
365 {
366 wxStringList valList;
367 wxChar c;
368 for (c='0'; c <= '9'; c++)
369 valList.Add(wxString(c, 1));
370 wxChar *p=(wxChar*)m_format.c_str();
371 while (*p)
372 {
373 if (*p == '%')
374 p += 2;
375 else
376 valList.Add(wxString(*p++, 1));
377 }
378 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
379 tv.SetIncludeList(valList);
380
381 m_txt->SetValidator(tv);
382
383 if (!currentText.IsEmpty())
384 m_txt->SetValue(currentDate.Format(m_format));
385 }
386 return true;
387 }
388
389
390 wxDateTime wxDatePickerCtrl::GetValue() const
391 {
392 wxDateTime dt;
393 wxString txt=m_txt->GetValue();
394
395 if (!txt.IsEmpty())
396 dt.ParseFormat(txt, m_format);
397
398 return dt;
399 }
400
401
402 void wxDatePickerCtrl::SetValue(const wxDateTime& date)
403 {
404 if (m_cal)
405 {
406 if (date.IsValid())
407 m_txt->SetValue(date.Format(m_format));
408 else
409 m_txt->SetValue(wxEmptyString);
410 }
411 }
412
413
414 bool wxDatePickerCtrl::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
415 {
416 if (dt1)
417 *dt1 = m_cal->GetLowerDateLimit();
418 if (dt1)
419 *dt2 = m_cal->GetUpperDateLimit();
420 return true;
421 }
422
423
424 void wxDatePickerCtrl::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
425 {
426 m_cal->SetDateRange(dt1, dt2);
427 }
428
429 // ----------------------------------------------------------------------------
430 // event handlers
431 // ----------------------------------------------------------------------------
432
433 void wxDatePickerCtrl::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 wxDatePickerCtrl::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 wxDatePickerCtrl::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 wxDatePickerCtrl::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 wxDatePickerCtrl::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 wxDatePickerCtrl::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 wxDatePickerCtrl::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 wxDatePickerCtrl::OnEditKey(wxKeyEvent & ev)
567 {
568 if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers())
569 DropDown();
570 else
571 ev.Skip();
572 }
573
574
575 void wxDatePickerCtrl::OnCalKey(wxKeyEvent & ev)
576 {
577 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
578 DropDown(false);
579 else
580 ev.Skip();
581 }
582