]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datectlg.cpp
ce8fb71cf1646551fc0c9b05b719fce9582f6579
[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 #ifdef wxHAS_NATIVE_DATEPICKCTRL
44 // this header is not included from wx/datectrl.h if we have a native
45 // version, but we do need it here
46 #include "wx/generic/datectrl.h"
47 #endif
48
49 // we need to define _WX_DEFINE_DATE_EVENTS_ before including wx/dateevt.h to
50 // define the event types we use if we're the only date picker control version
51 // being compiled -- otherwise it's defined in the native version implementation
52 #ifndef wxHAS_NATIVE_DATEPICKCTRL
53 #define _WX_DEFINE_DATE_EVENTS_
54 #endif
55
56 #include "wx/dateevt.h"
57
58 #include "wx/calctrl.h"
59 #include "wx/renderer.h"
60
61 // ----------------------------------------------------------------------------
62 // constants
63 // ----------------------------------------------------------------------------
64
65 enum
66 {
67 CTRLID_TXT = 101,
68 CTRLID_CAL,
69 CTRLID_BTN,
70 CTRLID_PAN
71 };
72
73 #ifndef DEFAULT_ITEM_WIDTH
74 #define DEFAULT_ITEM_WIDTH 100
75 #endif
76
77 // ----------------------------------------------------------------------------
78 // local classes
79 // ----------------------------------------------------------------------------
80
81
82 class wxDropdownButton : public wxBitmapButton
83 {
84 public:
85 wxDropdownButton() { Init(); }
86 wxDropdownButton(wxWindow *parent,
87 wxWindowID id,
88 const wxPoint& pos = wxDefaultPosition,
89 const wxSize& size = wxDefaultSize,
90 long style=0,
91 const wxValidator& validator = wxDefaultValidator);
92
93 void Init()
94 {
95 m_borderX = -1;
96 m_borderY = -1;
97 }
98 void Create(wxWindow *parent,
99 wxWindowID id,
100 const wxPoint& pos = wxDefaultPosition,
101 const wxSize& size = wxDefaultSize,
102 long style = 0,
103 const wxValidator& validator = wxDefaultValidator);
104
105 void DoMoveWindow(int x, int y, int w, int h);
106
107 protected:
108 int m_borderX, m_borderY;
109 };
110
111
112 wxDropdownButton::wxDropdownButton(wxWindow *parent,
113 wxWindowID id,
114 const wxPoint& pos,
115 const wxSize& size,
116 long style,
117 const wxValidator& validator)
118 {
119 Init();
120 Create(parent, id, pos, size, style, validator);
121 }
122
123
124 void wxDropdownButton::Create(wxWindow *parent,
125 wxWindowID id,
126 const wxPoint& pos,
127 const wxSize& size,
128 long style,
129 const wxValidator& validator)
130 {
131 wxBitmap chkBmp(15,15); // arbitrary
132 wxBitmapButton::Create(parent, id, chkBmp, pos, wxDefaultSize, wxBU_AUTODRAW, validator);
133
134 int w, h;
135
136 w=chkBmp.GetWidth();
137 h=chkBmp.GetHeight();
138 m_borderX = GetSize().x - m_marginX - w;
139 m_borderY = GetSize().y - m_marginY - h;
140
141 w = (size.x > 0 ? size.x : GetSize().x);
142 h = (size.y > 0 ? size.y : GetSize().y);
143
144 DoMoveWindow(pos.x, pos.y, w, h);
145 }
146
147
148 void wxDropdownButton::DoMoveWindow(int x, int y, int w, int h)
149 {
150 if (m_borderX >= 0 && m_borderY >= 0 && (w >= 0 || h >= 0))
151 {
152 wxMemoryDC dc;
153 if (w < 0)
154 w = GetSize().x;
155 #ifdef __WXGTK__
156 else
157 w = m_marginX + m_borderX + 15; // GTK magic size
158 #endif
159 if (h < 0)
160 h = GetSize().y;
161
162 int bw = w - m_marginX - m_borderX;
163 int bh = h - m_marginY - m_borderY;
164 if (bh < 11) bh=11;
165 if (bw < 9) bw=9;
166
167 wxBitmap bmp(bw, bh);
168 dc.SelectObject(bmp);
169
170 wxRendererNative::Get().DrawComboBoxDropButton(this, dc, wxRect(0,0,bw, bh));
171
172 SetBitmapLabel(bmp);
173 }
174
175 wxBitmapButton::DoMoveWindow(x, y, w, h);
176 }
177
178
179 #if wxUSE_POPUPWIN
180
181 #include "wx/popupwin.h"
182
183 class wxDatePopupInternal : public wxPopupTransientWindow
184 {
185 public:
186 wxDatePopupInternal(wxWindow *parent) : wxPopupTransientWindow(parent) { }
187
188 void ShowAt(int x, int y)
189 {
190 Position(wxPoint(x, y), wxSize(0, 0));
191 Popup();
192 }
193
194 void Hide()
195 {
196 Dismiss();
197 }
198 };
199
200 #else // !wxUSE_POPUPWIN
201
202 class wxDatePopupInternal : public wxDialog
203 {
204 public:
205 wxDatePopupInternal(wxWindow *parent)
206 : wxDialog(parent,
207 wxID_ANY,
208 wxEmptyString,
209 wxDefaultPosition,
210 wxDefaultSize,
211 wxSIMPLE_BORDER)
212 {
213 }
214
215 void ShowAt(int x, int y)
216 {
217 Show();
218 Move(x, y);
219 }
220
221 void Hide()
222 {
223 wxDialog::Hide();
224 }
225 };
226
227 #endif // wxUSE_POPUPWIN/!wxUSE_POPUPWIN
228
229 // ============================================================================
230 // wxDatePickerCtrlGeneric implementation
231 // ============================================================================
232
233 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
234 EVT_BUTTON(CTRLID_BTN, wxDatePickerCtrlGeneric::OnClick)
235 EVT_TEXT(CTRLID_TXT, wxDatePickerCtrlGeneric::OnText)
236 EVT_CHILD_FOCUS(wxDatePickerCtrlGeneric::OnChildSetFocus)
237 END_EVENT_TABLE()
238
239 #ifndef wxHAS_NATIVE_DATEPICKCTRL
240 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxDatePickerCtrlBase)
241 #endif
242
243 // ----------------------------------------------------------------------------
244 // creation
245 // ----------------------------------------------------------------------------
246
247 bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
248 wxWindowID id,
249 const wxDateTime& date,
250 const wxPoint& pos,
251 const wxSize& size,
252 long style,
253 const wxValidator& validator,
254 const wxString& name)
255 {
256 wxASSERT_MSG( !(style & wxDP_SPIN),
257 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
258
259 if ( !wxControl::Create(parent, id, pos, size,
260 style | wxCLIP_CHILDREN | wxWANTS_CHARS,
261 validator, name) )
262
263 {
264 return false;
265 }
266
267 InheritAttributes();
268
269 m_txt = new wxTextCtrl(this, CTRLID_TXT);
270
271 m_txt->Connect(wxEVT_KEY_DOWN,
272 wxKeyEventHandler(wxDatePickerCtrlGeneric::OnEditKey),
273 NULL, this);
274 m_txt->Connect(wxEVT_KILL_FOCUS,
275 wxFocusEventHandler(wxDatePickerCtrlGeneric::OnKillFocus),
276 NULL, this);
277
278 const int height = m_txt->GetBestSize().y;
279
280 m_btn = new wxDropdownButton(this, CTRLID_BTN, wxDefaultPosition, wxSize(height, height));
281
282 m_popup = new wxDatePopupInternal(this);
283 m_popup->SetFont(GetFont());
284
285 wxPanel *panel=new wxPanel(m_popup, CTRLID_PAN,
286 wxPoint(0, 0), wxDefaultSize,
287 wxSUNKEN_BORDER);
288 m_cal = new wxCalendarCtrl(panel, CTRLID_CAL, wxDefaultDateTime,
289 wxPoint(0, 0), wxDefaultSize,
290 wxCAL_SHOW_HOLIDAYS | wxSUNKEN_BORDER);
291 m_cal->Connect(wxEVT_CALENDAR_SEL_CHANGED,
292 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange),
293 NULL, this);
294 m_cal->Connect(wxEVT_KEY_DOWN,
295 wxKeyEventHandler(wxDatePickerCtrlGeneric::OnCalKey),
296 NULL, this);
297 m_cal->Connect(wxEVT_CALENDAR_DOUBLECLICKED,
298 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange),
299 NULL, this);
300 m_cal->Connect(wxEVT_CALENDAR_DAY_CHANGED,
301 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange),
302 NULL, this);
303 m_cal->Connect(wxEVT_CALENDAR_MONTH_CHANGED,
304 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange),
305 NULL, this);
306 m_cal->Connect(wxEVT_CALENDAR_YEAR_CHANGED,
307 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange),
308 NULL, this);
309
310 wxWindow *yearControl = m_cal->GetYearControl();
311
312 Connect(wxEVT_SET_FOCUS,
313 wxFocusEventHandler(wxDatePickerCtrlGeneric::OnSetFocus));
314
315 wxClientDC dc(yearControl);
316 dc.SetFont(m_font);
317 wxCoord width, dummy;
318 dc.GetTextExtent(wxT("2000"), &width, &dummy);
319 width += ConvertDialogToPixels(wxSize(20, 0)).x;
320
321 wxSize calSize = m_cal->GetBestSize();
322 wxSize yearSize = yearControl->GetSize();
323 yearSize.x = width;
324
325 wxPoint yearPosition = yearControl->GetPosition();
326
327 SetFormat(wxT("%x"));
328
329
330 #ifdef __WXMSW__
331 #define CALBORDER 0
332 #define RIGHTBUTTONBORDER 2
333 #define TOPBUTTONBORDER 1
334 #else
335 #define CALBORDER 4
336 #define RIGHTBUTTONBORDER 0
337 #define TOPBUTTONBORDER 0
338 #endif
339
340 width = yearPosition.x + yearSize.x+2+CALBORDER/2;
341 if (width < calSize.x-4)
342 width = calSize.x-4;
343
344 int calPos = (width-calSize.x)/2;
345 if (calPos == -1)
346 {
347 calPos = 0;
348 width += 2;
349 }
350 m_cal->SetSize(calPos, 0, calSize.x, calSize.y);
351 yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
352 yearSize.x, yearSize.y);
353 m_cal->GetMonthControl()->Move(0, 0);
354
355
356
357 panel->SetClientSize(width+CALBORDER/2, calSize.y-2+CALBORDER);
358 m_popup->SetClientSize(panel->GetSize());
359 m_popup->Hide();
360
361 if (!date.IsValid())
362 date.Today();
363
364 SetValue(date);
365
366 return true;
367 }
368
369
370 void wxDatePickerCtrlGeneric::Init()
371 {
372 m_popup = NULL;
373 m_txt = NULL;
374 m_cal = NULL;
375 m_btn = NULL;
376
377 m_dropped = false;
378 m_ignoreDrop = false;
379 }
380
381
382 bool wxDatePickerCtrlGeneric::Destroy()
383 {
384 if (m_cal)
385 m_cal->Destroy();
386 if (m_popup)
387 m_popup->Destroy();
388 if (m_txt)
389 m_txt->Destroy();
390 if (m_btn)
391 m_btn->Destroy();
392
393 m_popup = NULL;
394 m_txt = NULL;
395 m_cal = NULL;
396 m_btn = NULL;
397
398 return wxControl::Destroy();
399 }
400
401 // ----------------------------------------------------------------------------
402 // overridden base class methods
403 // ----------------------------------------------------------------------------
404
405 void wxDatePickerCtrlGeneric::DoMoveWindow(int x, int y, int w, int h)
406 {
407 wxControl::DoMoveWindow(x, y, w, h);
408 wxSize bs=m_btn->GetBestSize();
409 int eh=m_txt->GetBestSize().y;
410
411 m_txt->SetSize(0, 0, w-bs.x-1, h > eh ? eh : h);
412 m_btn->SetSize(w - bs.x-RIGHTBUTTONBORDER, TOPBUTTONBORDER, bs.x, h > bs.y ? bs.y : h);
413
414 if (m_dropped)
415 DropDown(true);
416 }
417
418 wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
419 {
420 int bh=m_btn->GetBestSize().y;
421 int eh=m_txt->GetBestSize().y;
422 return wxSize(DEFAULT_ITEM_WIDTH, bh > eh ? bh : eh);
423 }
424
425
426 bool wxDatePickerCtrlGeneric::Show(bool show)
427 {
428 if ( !wxControl::Show(show) )
429 {
430 return false;
431 }
432
433 if ( !show )
434 {
435 if ( m_popup )
436 {
437 m_popup->Hide();
438 m_dropped = false;
439 }
440 }
441
442 return true;
443 }
444
445
446 bool wxDatePickerCtrlGeneric::Enable(bool enable)
447 {
448 if ( !wxControl::Enable(enable) )
449 {
450 return false;
451 }
452
453 if ( !enable )
454 {
455 if ( m_cal )
456 m_cal->Hide();
457 }
458
459 if ( m_btn )
460 m_btn->Enable(enable);
461
462 return true;
463 }
464
465 // ----------------------------------------------------------------------------
466 // wxDatePickerCtrlGeneric API
467 // ----------------------------------------------------------------------------
468
469 bool
470 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
471 const wxDateTime& upperdate)
472 {
473 return m_cal->SetDateRange(lowerdate, upperdate);
474 }
475
476 bool wxDatePickerCtrlGeneric::SetFormat(const wxChar *fmt)
477 {
478 wxDateTime dt;
479 dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
480 wxString str=dt.Format(fmt);
481 wxChar *p=(wxChar*)str.c_str();
482
483 m_format=wxEmptyString;
484
485 while (*p)
486 {
487 int n=wxAtoi(p);
488 if (n == dt.GetDay())
489 {
490 m_format.Append(wxT("%d"));
491 p += 2;
492 }
493 else if (n == (int)dt.GetMonth()+1)
494 {
495 m_format.Append(wxT("%m"));
496 p += 2;
497 }
498 else if (n == dt.GetYear())
499 {
500 m_format.Append(wxT("%Y"));
501 p += 4;
502 }
503 else if (n == (dt.GetYear() % 100))
504 {
505 if (GetWindowStyle() & wxDP_SHOWCENTURY)
506 m_format.Append(wxT("%Y"));
507 else
508 m_format.Append(wxT("%y"));
509 p += 2;
510 }
511 else
512 m_format.Append(*p++);
513 }
514
515 if (m_txt)
516 {
517 wxArrayString allowedChars;
518 for ( wxChar c = _T('0'); c <= _T('9'); c++ )
519 allowedChars.Add(wxString(c, 1));
520
521 const wxChar *p = m_format.c_str();
522 while (*p)
523 {
524 if (*p == '%')
525 p += 2;
526 else
527 allowedChars.Add(wxString(*p++, 1));
528 }
529
530 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
531 tv.SetIncludes(allowedChars);
532
533 m_txt->SetValidator(tv);
534
535 if (m_currentDate.IsValid())
536 m_txt->SetValue(m_currentDate.Format(m_format));
537 }
538
539 return true;
540 }
541
542
543 wxDateTime wxDatePickerCtrlGeneric::GetValue() const
544 {
545 return m_currentDate;
546 }
547
548
549 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
550 {
551 if (m_cal)
552 {
553 if (date.IsValid())
554 m_txt->SetValue(date.Format(m_format));
555 else
556 {
557 wxASSERT_MSG( HasFlag(wxDP_ALLOWNONE),
558 _T("this control must have a valid date") );
559
560 m_txt->SetValue(wxEmptyString);
561 }
562
563 m_currentDate = date;
564 }
565 }
566
567
568 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
569 {
570 if (dt1)
571 *dt1 = m_cal->GetLowerDateLimit();
572 if (dt1)
573 *dt2 = m_cal->GetUpperDateLimit();
574 return true;
575 }
576
577
578 void
579 wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
580 {
581 m_cal->SetDateRange(dt1, dt2);
582 }
583
584 // ----------------------------------------------------------------------------
585 // event handlers
586 // ----------------------------------------------------------------------------
587
588 void wxDatePickerCtrlGeneric::DropDown(bool down)
589 {
590 if (m_popup)
591 {
592 if (down)
593 {
594 wxDateTime dt;
595 if (!m_txt->GetValue().empty())
596 dt.ParseFormat(m_txt->GetValue(), m_format);
597
598 if (dt.IsValid())
599 m_cal->SetDate(dt);
600 else
601 m_cal->SetDate(wxDateTime::Today());
602
603 wxPoint pos=GetParent()->ClientToScreen(GetPosition());
604 m_popup->ShowAt(pos.x, pos.y + GetSize().y);
605 m_dropped = true;
606 m_cal->SetFocus();
607 }
608 else
609 {
610 if (m_dropped)
611 m_popup->Hide();
612 m_dropped = false;
613 }
614 }
615 }
616
617
618 void wxDatePickerCtrlGeneric::OnChildSetFocus(wxChildFocusEvent &ev)
619 {
620 ev.Skip();
621 m_ignoreDrop = false;
622
623 wxWindow *w=(wxWindow*)ev.GetEventObject();
624 while (w)
625 {
626 if (w == m_popup)
627 return;
628 w = w->GetParent();
629 }
630
631 if (m_dropped)
632 {
633 DropDown(false);
634 if (ev.GetEventObject() == m_btn)
635 m_ignoreDrop = true;
636 }
637 }
638
639
640 void wxDatePickerCtrlGeneric::OnClick(wxCommandEvent& WXUNUSED(event))
641 {
642 if (m_ignoreDrop)
643 {
644 m_ignoreDrop = false;
645 m_txt->SetFocus();
646 }
647 else
648 {
649 DropDown();
650 m_cal->SetFocus();
651 }
652 }
653
654
655 void wxDatePickerCtrlGeneric::OnSetFocus(wxFocusEvent& WXUNUSED(ev))
656 {
657 if (m_txt)
658 {
659 m_txt->SetFocus();
660 m_txt->SetSelection(-1, -1); // select everything
661 }
662 }
663
664
665 void wxDatePickerCtrlGeneric::OnKillFocus(wxFocusEvent &ev)
666 {
667 ev.Skip();
668
669 wxDateTime dt;
670 dt.ParseFormat(m_txt->GetValue(), m_format);
671 if ( !dt.IsValid() )
672 {
673 if ( !HasFlag(wxDP_ALLOWNONE) )
674 dt = m_currentDate;
675 }
676
677 if(dt.IsValid())
678 m_txt->SetValue(dt.Format(m_format));
679 else
680 m_txt->SetValue(wxEmptyString);
681
682 // notify that we had to change the date after validation
683 if ( (dt.IsValid() && m_currentDate != dt) ||
684 (!dt.IsValid() && m_currentDate.IsValid()) )
685 {
686 m_currentDate = dt;
687 wxDateEvent event(this, dt, wxEVT_DATE_CHANGED);
688 GetEventHandler()->ProcessEvent(event);
689 }
690 }
691
692
693 void wxDatePickerCtrlGeneric::OnSelChange(wxCalendarEvent &ev)
694 {
695 if (m_cal)
696 {
697 m_currentDate = m_cal->GetDate();
698 m_txt->SetValue(m_currentDate.Format(m_format));
699 if (ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED)
700 {
701 DropDown(false);
702 m_txt->SetFocus();
703 }
704 }
705 ev.SetEventObject(this);
706 ev.SetId(GetId());
707 GetParent()->ProcessEvent(ev);
708
709 wxDateEvent dev(this, ev.GetDate(), wxEVT_DATE_CHANGED);
710 GetParent()->ProcessEvent(dev);
711 }
712
713
714 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
715 {
716 ev.SetEventObject(this);
717 ev.SetId(GetId());
718 GetParent()->ProcessEvent(ev);
719
720 // We'll create an additional event if the date is valid.
721 // If the date isn't valid, the user's probably in the middle of typing
722 wxString txt = m_txt->GetValue();
723 wxDateTime dt;
724 if (!txt.empty())
725 {
726 dt.ParseFormat(txt, m_format);
727 if (!dt.IsValid())
728 return;
729 }
730
731 wxCalendarEvent cev(m_cal, wxEVT_CALENDAR_SEL_CHANGED);
732 cev.SetEventObject(this);
733 cev.SetId(GetId());
734 cev.SetDate(dt);
735
736 GetParent()->ProcessEvent(cev);
737
738 wxDateEvent dev(this, dt, wxEVT_DATE_CHANGED);
739 GetParent()->ProcessEvent(dev);
740 }
741
742
743 void wxDatePickerCtrlGeneric::OnEditKey(wxKeyEvent & ev)
744 {
745 if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers())
746 DropDown(true);
747 else
748 ev.Skip();
749 }
750
751
752 void wxDatePickerCtrlGeneric::OnCalKey(wxKeyEvent & ev)
753 {
754 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
755 DropDown(false);
756 else
757 ev.Skip();
758 }
759
760 #endif // wxUSE_DATEPICKCTRL_GENERIC
761
762 #endif // wxUSE_DATEPICKCTRL
763