don't include wx/popupwin.h in the header, use forward declarations
[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 #include "wx/popupwin.h"
41
42 // ----------------------------------------------------------------------------
43 // constants
44 // ----------------------------------------------------------------------------
45
46 enum
47 {
48 CTRLID_TXT = 101,
49 CTRLID_CAL,
50 CTRLID_BTN,
51 CTRLID_PAN
52 };
53
54 #ifndef DEFAULT_ITEM_WIDTH
55 #define DEFAULT_ITEM_WIDTH 100
56 #endif
57
58 // ============================================================================
59 // wxDatePickerCtrl implementation
60 // ============================================================================
61
62 BEGIN_EVENT_TABLE(wxDatePickerCtrl, wxDatePickerCtrlBase)
63 EVT_BUTTON(CTRLID_BTN, wxDatePickerCtrl::OnClick)
64 EVT_TEXT(CTRLID_TXT, wxDatePickerCtrl::OnText)
65 EVT_CHILD_FOCUS(wxDatePickerCtrl::OnChildSetFocus)
66 END_EVENT_TABLE()
67
68 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxDatePickerCtrlBase)
69
70 // ----------------------------------------------------------------------------
71 // creation
72 // ----------------------------------------------------------------------------
73
74 wxDatePickerCtrl::wxDatePickerCtrl(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 Init();
83 Create(parent, id, date, pos, size, style, name);
84 }
85
86
87 bool wxDatePickerCtrl::Create(wxWindow *parent,
88 wxWindowID id,
89 const wxDateTime& date,
90 const wxPoint& pos,
91 const wxSize& size,
92 long style,
93 const wxString& name)
94 {
95 wxASSERT_MSG( !(style & wxDP_SPIN),
96 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
97
98 if ( !wxControl::Create(parent, id, pos, size,
99 style | wxCLIP_CHILDREN | wxWANTS_CHARS,
100 wxDefaultValidator, name) )
101
102 {
103 return false;
104 }
105
106 SetWindowStyle(style | wxWANTS_CHARS);
107 InheritAttributes();
108
109 wxBitmap bmp(8, 4);
110 {
111 wxMemoryDC dc;
112
113 dc.SelectObject(bmp);
114 dc.SetBrush(wxBrush(GetBackgroundColour()));
115 dc.SetPen(wxPen(GetBackgroundColour()));
116 dc.DrawRectangle(0,0, 8,4);
117
118 dc.SetBrush(wxBrush(GetForegroundColour()));
119 dc.SetPen(wxPen(GetForegroundColour()));
120 wxPoint pt[3] = { wxPoint(0,0), wxPoint(6,0), wxPoint(3,3) };
121 dc.DrawPolygon(3, pt);
122 dc.SelectObject(wxNullBitmap);
123 }
124
125 m_txt=new wxTextCtrl(this, CTRLID_TXT);
126 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
127 (wxObjectEventFunction)&wxDatePickerCtrl::OnEditKey,
128 0, this);
129 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS,
130 (wxObjectEventFunction)&wxDatePickerCtrl::OnKillFocus,
131 0, this);
132
133 m_btn = new wxBitmapButton(this, CTRLID_BTN, bmp);
134
135 m_popup = new wxPopupWindow(this);
136 m_popup->SetFont(GetFont());
137
138 wxPanel *panel=new wxPanel(m_popup, CTRLID_PAN,
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,
145 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
146 0, this);
147 m_cal->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
148 (wxObjectEventFunction)&wxDatePickerCtrl::OnCalKey,
149 0, this);
150 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DOUBLECLICKED,
151 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
152 0, this);
153 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DAY_CHANGED,
154 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
155 0, this);
156 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_MONTH_CHANGED,
157 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
158 0, this);
159 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_YEAR_CHANGED,
160 (wxObjectEventFunction)&wxDatePickerCtrl::OnSelChange,
161 0, this);
162
163 wxWindow *yearControl = m_cal->GetYearControl();
164
165 Connect(wxID_ANY, wxID_ANY, wxEVT_SET_FOCUS,
166 (wxObjectEventFunction)&wxDatePickerCtrl::OnSetFocus);
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
180 SetFormat(wxT("%x"));
181
182 if (date.IsValid())
183 m_txt->SetValue(date.Format(m_format));
184
185
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);
203 yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
204 yearSize.x, yearSize.y);
205 m_cal->GetMonthControl()->Move(0, 0);
206
207
208
209 panel->SetClientSize(width+CALBORDER/2, calSize.y-2+CALBORDER);
210 m_popup->SetClientSize(panel->GetSize());
211 m_popup->Hide();
212
213 return TRUE;
214 }
215
216
217 void wxDatePickerCtrl::Init()
218 {
219 m_popup = NULL;
220 m_txt = NULL;
221 m_cal = NULL;
222 m_btn = NULL;
223
224 m_dropped = false;
225 m_ignoreDrop = false;
226 }
227
228
229 bool wxDatePickerCtrl::Destroy()
230 {
231 if (m_cal)
232 m_cal->Destroy();
233 if (m_popup)
234 m_popup->Destroy();
235 if (m_txt)
236 m_txt->Destroy();
237 if (m_btn)
238 m_btn->Destroy();
239
240 m_popup = NULL;
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
252 void wxDatePickerCtrl::DoMoveWindow(int x, int y, int w, int h)
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
265 wxSize wxDatePickerCtrl::DoGetBestSize() const
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
273 bool wxDatePickerCtrl::Show(bool show)
274 {
275 if ( !wxControl::Show(show) )
276 {
277 return FALSE;
278 }
279
280 if (!show)
281 {
282 if (m_popup)
283 {
284 m_popup->Hide();
285 m_dropped = false;
286 }
287 }
288
289 return TRUE;
290 }
291
292
293 bool wxDatePickerCtrl::Enable(bool enable)
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 // ----------------------------------------------------------------------------
311 // wxDatePickerCtrl API
312 // ----------------------------------------------------------------------------
313
314 bool
315 wxDatePickerCtrl::SetDateRange(const wxDateTime& lowerdate,
316 const wxDateTime& upperdate)
317 {
318 return m_cal->SetDateRange(lowerdate, upperdate);
319 }
320
321 bool wxDatePickerCtrl::SetFormat(const wxChar *fmt)
322 {
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 }
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 }
356 else if (n == (dt.GetYear() % 100))
357 {
358 m_format.Append(wxT("%y"));
359 p += 2;
360 }
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);
383
384 if (!currentText.IsEmpty())
385 m_txt->SetValue(currentDate.Format(m_format));
386 }
387 return true;
388 }
389
390
391 wxDateTime wxDatePickerCtrl::GetValue() const
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
403 void wxDatePickerCtrl::SetValue(const wxDateTime& date)
404 {
405 if (m_cal)
406 {
407 if (date.IsValid())
408 m_txt->SetValue(date.Format(m_format));
409 else
410 m_txt->SetValue(wxEmptyString);
411 }
412 }
413
414
415 bool wxDatePickerCtrl::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
416 {
417 if (dt1)
418 *dt1 = m_cal->GetLowerDateLimit();
419 if (dt1)
420 *dt2 = m_cal->GetUpperDateLimit();
421 return true;
422 }
423
424
425 void wxDatePickerCtrl::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
426 {
427 m_cal->SetDateRange(dt1, dt2);
428 }
429
430 // ----------------------------------------------------------------------------
431 // event handlers
432 // ----------------------------------------------------------------------------
433
434 void wxDatePickerCtrl::DropDown(bool down)
435 {
436 if (m_popup)
437 {
438 if (down)
439 {
440 wxDateTime dt;
441 if (!m_txt->GetValue().IsEmpty())
442 dt.ParseFormat(m_txt->GetValue(), m_format);
443
444 if (dt.IsValid())
445 m_cal->SetDate(dt);
446 else
447 m_cal->SetDate(wxDateTime::Today());
448
449 wxPoint pos=GetParent()->ClientToScreen(GetPosition());
450 m_popup->Move(pos.x, pos.y + GetSize().y);
451 m_popup->Show();
452 m_dropped = true;
453 }
454 else
455 {
456 if (m_dropped)
457 m_popup->Hide();
458 m_dropped = false;
459 }
460 }
461 }
462
463
464 void wxDatePickerCtrl::OnChildSetFocus(wxChildFocusEvent &ev)
465 {
466 ev.Skip();
467 m_ignoreDrop = false;
468
469 wxWindow *w=(wxWindow*)ev.GetEventObject();
470 while (w)
471 {
472 if (w == m_popup)
473 return;
474 w = w->GetParent();
475 }
476
477 if (m_dropped)
478 {
479 DropDown(false);
480 if (ev.GetEventObject() == m_btn)
481 m_ignoreDrop = true;
482 }
483 }
484
485
486 void wxDatePickerCtrl::OnClick(wxCommandEvent& event)
487 {
488 if (m_ignoreDrop)
489 {
490 m_ignoreDrop = false;
491 m_txt->SetFocus();
492 }
493 else
494 {
495 DropDown();
496 m_cal->SetFocus();
497 }
498 }
499
500
501 void wxDatePickerCtrl::OnSetFocus(wxFocusEvent &ev)
502 {
503 if (m_txt)
504 {
505 m_txt->SetFocus();
506 m_txt->SetSelection(0, 100);
507 }
508 }
509
510
511 void wxDatePickerCtrl::OnKillFocus(wxFocusEvent &ev)
512 {
513 ev.Skip();
514
515 wxDateTime dt;
516 dt.ParseFormat(m_txt->GetValue(), m_format);
517 if (!dt.IsValid())
518 m_txt->SetValue(wxEmptyString);
519 else
520 m_txt->SetValue(dt.Format(m_format));
521 }
522
523
524 void wxDatePickerCtrl::OnSelChange(wxCalendarEvent &ev)
525 {
526 if (m_cal)
527 {
528 m_txt->SetValue(m_cal->GetDate().Format(m_format));
529 if (ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED)
530 {
531 DropDown(false);
532 m_txt->SetFocus();
533 }
534 }
535 ev.SetEventObject(this);
536 ev.SetId(GetId());
537 GetParent()->ProcessEvent(ev);
538 }
539
540
541 void wxDatePickerCtrl::OnText(wxCommandEvent &ev)
542 {
543 ev.SetEventObject(this);
544 ev.SetId(GetId());
545 GetParent()->ProcessEvent(ev);
546
547 // We'll create an additional event if the date is valid.
548 // If the date isn't valid, the user's probable in the middle of typing
549 wxString txt=m_txt->GetValue();
550 wxDateTime dt;
551 if (!txt.IsEmpty())
552 {
553 dt.ParseFormat(txt, m_format);
554 if (!dt.IsValid())
555 return;
556 }
557
558 wxCalendarEvent cev(m_cal, wxEVT_CALENDAR_SEL_CHANGED);
559 cev.SetEventObject(this);
560 cev.SetId(GetId());
561 cev.SetDate(dt);
562
563 GetParent()->ProcessEvent(cev);
564 }
565
566
567 void wxDatePickerCtrl::OnEditKey(wxKeyEvent & ev)
568 {
569 if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers())
570 DropDown();
571 else
572 ev.Skip();
573 }
574
575
576 void wxDatePickerCtrl::OnCalKey(wxKeyEvent & ev)
577 {
578 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
579 DropDown(false);
580 else
581 ev.Skip();
582 }
583