1 /////////////////////////////////////////////////////////////////////////////
2 // Name: generic/datectlg.cpp
3 // Purpose: generic wxDatePickerCtrlGeneric implementation
4 // Author: Andreas Pflug
8 // Copyright: (c) 2005 Andreas Pflug <pgadmin@pse-consulting.de>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
26 #if wxUSE_DATEPICKCTRL
28 #include "wx/datectrl.h"
30 // use this version if we're explicitly requested to do it or if it's the only
32 #if wxUSE_DATEPICKCTRL_GENERIC || !defined(wxHAS_NATIVE_DATEPICKCTRL)
35 #include "wx/bmpbuttn.h"
36 #include "wx/dialog.h"
37 #include "wx/dcmemory.h"
39 #include "wx/textctrl.h"
40 #include "wx/valtext.h"
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"
48 // we need to define _WX_DEFINE_DATE_EVENTS_ before including wx/dateevt.h to
49 // define the event types we use if we're the only date picker control version
50 // being compiled -- otherwise it's defined in the native version implementation
51 #define _WX_DEFINE_DATE_EVENTS_
54 #include "wx/dateevt.h"
56 #include "wx/calctrl.h"
57 #include "wx/renderer.h"
59 // ----------------------------------------------------------------------------
61 // ----------------------------------------------------------------------------
71 #ifndef DEFAULT_ITEM_WIDTH
72 #define DEFAULT_ITEM_WIDTH 100
77 #define wxUSE_POPUPWIN 0 // Popup not working
78 #define TXTCTRL_FLAGS wxNO_BORDER
79 #define BTN_FLAGS wxNO_BORDER
81 #define RIGHTBUTTONBORDER 4
82 #define TOPBUTTONBORDER 0
83 #define BUTTONBORDER 4
86 #define TXTCTRL_FLAGS 0
87 #define BTN_FLAGS wxBU_AUTODRAW
89 #define RIGHTBUTTONBORDER 0
90 #define TOPBUTTONBORDER 0
91 #define BUTTONBORDER 0
96 // ----------------------------------------------------------------------------
98 // ----------------------------------------------------------------------------
101 class wxDropdownButton
: public wxBitmapButton
104 wxDropdownButton() { Init(); }
105 wxDropdownButton(wxWindow
*parent
,
107 const wxPoint
& pos
= wxDefaultPosition
,
108 const wxSize
& size
= wxDefaultSize
,
110 const wxValidator
& validator
= wxDefaultValidator
);
117 bool Create(wxWindow
*parent
,
119 const wxPoint
& pos
= wxDefaultPosition
,
120 const wxSize
& size
= wxDefaultSize
,
122 const wxValidator
& validator
= wxDefaultValidator
);
124 void DoMoveWindow(int x
, int y
, int w
, int h
);
127 int m_borderX
, m_borderY
;
131 wxDropdownButton::wxDropdownButton(wxWindow
*parent
,
136 const wxValidator
& validator
)
139 Create(parent
, id
, pos
, size
, style
, validator
);
143 bool wxDropdownButton::Create(wxWindow
*parent
,
147 long WXUNUSED(style
),
148 const wxValidator
& validator
)
153 wxBitmap
chkBmp(15,15); // arbitrary
154 if ( !wxBitmapButton::Create(parent
, id
, chkBmp
,
155 pos
, wxDefaultSize
, BTN_FLAGS
, validator
) )
158 const wxSize sz
= GetSize();
159 int w
= chkBmp
.GetWidth(),
160 h
= chkBmp
.GetHeight();
161 m_borderX
= sz
.x
- m_marginX
- w
;
162 m_borderY
= sz
.y
- m_marginY
- h
;
164 w
= size
.x
> 0 ? size
.x
: sz
.x
;
165 h
= size
.y
> 0 ? size
.y
: sz
.y
;
167 DoMoveWindow(pos
.x
, pos
.y
, w
, h
);
173 void wxDropdownButton::DoMoveWindow(int x
, int y
, int w
, int h
)
175 if (m_borderX
>= 0 && m_borderY
>= 0 && (w
>= 0 || h
>= 0))
182 w
= m_marginX
+ m_borderX
+ 15; // GTK magic size
187 int borderX
= m_marginX
+ m_borderX
;
188 int borderY
= m_marginY
+ m_borderY
;
189 int bw
= w
- borderX
;
190 int bh
= h
- borderY
;
194 wxBitmap
bmp(bw
, bh
);
195 dc
.SelectObject(bmp
);
197 wxRendererNative
& renderer
= wxRendererNative::Get();
200 wxRect
r(-(borderX
/2),-(borderY
/2),w
,h
);
201 wxColour
magic(255,0,255);
202 dc
.SetBrush( wxBrush( magic
) );
203 dc
.SetPen( *wxTRANSPARENT_PEN
);
204 dc
.DrawRectangle(0,0,bw
,bh
);
205 renderer
.DrawDropArrow(this, dc
, r
);
206 wxMask
*mask
= new wxMask( bmp
, magic
);
210 renderer
.DrawComboBoxDropButton(this, dc
, r
);
214 wxBitmap
bmpSel(bw
, bh
);
215 dc
.SelectObject(bmpSel
);
218 dc
.SetBrush( wxBrush( magic
) );
219 dc
.SetPen( *wxTRANSPARENT_PEN
);
220 dc
.DrawRectangle(0,0,bw
,bh
);
221 renderer
.DrawDropArrow(this, dc
, r
, wxCONTROL_PRESSED
);
222 mask
= new wxMask( bmpSel
, magic
);
223 bmpSel
.SetMask( mask
);
225 renderer
.DrawComboBoxDropButton(this, dc
, r
, wxCONTROL_PRESSED
);
227 SetBitmapSelected(bmpSel
);
230 wxBitmapButton::DoMoveWindow(x
, y
, w
, h
);
236 #include "wx/popupwin.h"
238 class wxDatePopupInternal
: public wxPopupTransientWindow
241 wxDatePopupInternal(wxWindow
*parent
) : wxPopupTransientWindow(parent
) { }
243 void ShowAt(int x
, int y
)
245 Position(wxPoint(x
, y
), wxSize(0, 0));
255 #else // !wxUSE_POPUPWIN
257 class wxDatePopupInternal
: public wxDialog
260 wxDatePopupInternal(wxWindow
*parent
)
270 void ShowAt(int x
, int y
)
282 #endif // wxUSE_POPUPWIN/!wxUSE_POPUPWIN
284 // ============================================================================
285 // wxDatePickerCtrlGeneric implementation
286 // ============================================================================
288 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
)
289 EVT_BUTTON(CTRLID_BTN
, wxDatePickerCtrlGeneric::OnClick
)
290 EVT_TEXT(CTRLID_TXT
, wxDatePickerCtrlGeneric::OnText
)
291 EVT_CHILD_FOCUS(wxDatePickerCtrlGeneric::OnChildSetFocus
)
294 #ifndef wxHAS_NATIVE_DATEPICKCTRL
295 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
298 // ----------------------------------------------------------------------------
300 // ----------------------------------------------------------------------------
302 bool wxDatePickerCtrlGeneric::Create(wxWindow
*parent
,
304 const wxDateTime
& date
,
308 const wxValidator
& validator
,
309 const wxString
& name
)
311 wxASSERT_MSG( !(style
& wxDP_SPIN
),
312 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
314 if ( !wxControl::Create(parent
, id
, pos
, size
,
315 style
| wxCLIP_CHILDREN
| wxWANTS_CHARS
,
324 m_txt
= new wxTextCtrl(this, CTRLID_TXT
, wxEmptyString
, wxDefaultPosition
, wxDefaultSize
, TXTCTRL_FLAGS
);
326 m_txt
->Connect(wxEVT_KEY_DOWN
,
327 wxKeyEventHandler(wxDatePickerCtrlGeneric::OnEditKey
),
329 m_txt
->Connect(wxEVT_KILL_FOCUS
,
330 wxFocusEventHandler(wxDatePickerCtrlGeneric::OnKillFocus
),
333 const int height
= m_txt
->GetBestSize().y
- BUTTONBORDER
;
335 m_btn
= new wxDropdownButton(this, CTRLID_BTN
, wxDefaultPosition
, wxSize(height
, height
));
337 m_popup
= new wxDatePopupInternal(this);
338 m_popup
->SetFont(GetFont());
340 wxPanel
*panel
=new wxPanel(m_popup
, CTRLID_PAN
,
341 wxPoint(0, 0), wxDefaultSize
,
343 m_cal
= new wxCalendarCtrl(panel
, CTRLID_CAL
, wxDefaultDateTime
,
344 wxPoint(0, 0), wxDefaultSize
,
345 wxCAL_SHOW_HOLIDAYS
| wxSUNKEN_BORDER
);
346 m_cal
->Connect(wxEVT_CALENDAR_SEL_CHANGED
,
347 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
),
349 m_cal
->Connect(wxEVT_KEY_DOWN
,
350 wxKeyEventHandler(wxDatePickerCtrlGeneric::OnCalKey
),
352 m_cal
->Connect(wxEVT_CALENDAR_DOUBLECLICKED
,
353 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
),
355 m_cal
->Connect(wxEVT_CALENDAR_DAY_CHANGED
,
356 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
),
358 m_cal
->Connect(wxEVT_CALENDAR_MONTH_CHANGED
,
359 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
),
361 m_cal
->Connect(wxEVT_CALENDAR_YEAR_CHANGED
,
362 wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
),
365 wxWindow
*yearControl
= m_cal
->GetYearControl();
367 Connect(wxEVT_SET_FOCUS
,
368 wxFocusEventHandler(wxDatePickerCtrlGeneric::OnSetFocus
));
370 wxClientDC
dc(yearControl
);
371 dc
.SetFont(yearControl
->GetFont());
372 wxCoord width
, dummy
;
373 dc
.GetTextExtent(wxT("2000"), &width
, &dummy
);
374 width
+= ConvertDialogToPixels(wxSize(20, 0)).x
;
376 wxSize calSize
= m_cal
->GetBestSize();
377 wxSize yearSize
= yearControl
->GetSize();
380 wxPoint yearPosition
= yearControl
->GetPosition();
382 SetFormat(wxT("%x"));
384 width
= yearPosition
.x
+ yearSize
.x
+2+CALBORDER
/2;
385 if (width
< calSize
.x
-4)
388 int calPos
= (width
-calSize
.x
)/2;
394 m_cal
->SetSize(calPos
, 0, calSize
.x
, calSize
.y
);
395 yearControl
->SetSize(width
-yearSize
.x
-CALBORDER
/2, yearPosition
.y
,
396 yearSize
.x
, yearSize
.y
);
397 m_cal
->GetMonthControl()->Move(0, 0);
401 panel
->SetClientSize(width
+CALBORDER
/2, calSize
.y
-2+CALBORDER
);
402 m_popup
->SetClientSize(panel
->GetSize());
405 SetValue(date
.IsValid() ? date
: wxDateTime::Today());
407 SetBestFittingSize(size
);
413 void wxDatePickerCtrlGeneric::Init()
421 m_ignoreDrop
= false;
425 bool wxDatePickerCtrlGeneric::Destroy()
441 return wxControl::Destroy();
444 // ----------------------------------------------------------------------------
445 // overridden base class methods
446 // ----------------------------------------------------------------------------
448 void wxDatePickerCtrlGeneric::DoMoveWindow(int x
, int y
, int w
, int h
)
450 wxControl::DoMoveWindow(x
, y
, w
, h
);
451 wxSize bs
=m_btn
->GetBestSize();
452 int eh
=m_txt
->GetBestSize().y
;
454 m_txt
->SetSize(0, TXTPOSY
, w
-bs
.x
-RIGHTBUTTONBORDER
, h
> eh
? eh
-TXTPOSY
: h
-TXTPOSY
);
455 m_btn
->SetSize(w
- bs
.x
-RIGHTBUTTONBORDER
, TOPBUTTONBORDER
, bs
.x
, h
> bs
.y
? bs
.y
: h
);
461 wxSize
wxDatePickerCtrlGeneric::DoGetBestSize() const
463 int bh
=m_btn
->GetBestSize().y
;
464 int eh
=m_txt
->GetBestSize().y
;
465 return wxSize(DEFAULT_ITEM_WIDTH
, bh
> eh
? bh
: eh
);
469 bool wxDatePickerCtrlGeneric::Show(bool show
)
471 if ( !wxControl::Show(show
) )
489 bool wxDatePickerCtrlGeneric::Enable(bool enable
)
491 if ( !wxControl::Enable(enable
) )
503 m_btn
->Enable(enable
);
508 // ----------------------------------------------------------------------------
509 // wxDatePickerCtrlGeneric API
510 // ----------------------------------------------------------------------------
513 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
,
514 const wxDateTime
& upperdate
)
516 return m_cal
->SetDateRange(lowerdate
, upperdate
);
519 bool wxDatePickerCtrlGeneric::SetFormat(const wxChar
*fmt
)
522 dt
.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
523 wxString str
=dt
.Format(fmt
);
524 wxChar
*p
=(wxChar
*)str
.c_str();
526 m_format
=wxEmptyString
;
531 if (n
== dt
.GetDay())
533 m_format
.Append(wxT("%d"));
536 else if (n
== (int)dt
.GetMonth()+1)
538 m_format
.Append(wxT("%m"));
541 else if (n
== dt
.GetYear())
543 m_format
.Append(wxT("%Y"));
546 else if (n
== (dt
.GetYear() % 100))
548 if (GetWindowStyle() & wxDP_SHOWCENTURY
)
549 m_format
.Append(wxT("%Y"));
551 m_format
.Append(wxT("%y"));
555 m_format
.Append(*p
++);
560 wxArrayString allowedChars
;
561 for ( wxChar c
= _T('0'); c
<= _T('9'); c
++ )
562 allowedChars
.Add(wxString(c
, 1));
564 const wxChar
*p
= m_format
.c_str();
570 allowedChars
.Add(wxString(*p
++, 1));
574 wxTextValidator
tv(wxFILTER_INCLUDE_CHAR_LIST
);
575 tv
.SetIncludes(allowedChars
);
576 m_txt
->SetValidator(tv
);
579 if (m_currentDate
.IsValid())
580 m_txt
->SetValue(m_currentDate
.Format(m_format
));
587 wxDateTime
wxDatePickerCtrlGeneric::GetValue() const
589 return m_currentDate
;
593 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
)
598 m_txt
->SetValue(date
.Format(m_format
));
601 wxASSERT_MSG( HasFlag(wxDP_ALLOWNONE
),
602 _T("this control must have a valid date") );
604 m_txt
->SetValue(wxEmptyString
);
607 m_currentDate
= date
;
612 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
615 *dt1
= m_cal
->GetLowerDateLimit();
617 *dt2
= m_cal
->GetUpperDateLimit();
623 wxDatePickerCtrlGeneric::SetRange(const wxDateTime
&dt1
, const wxDateTime
&dt2
)
625 m_cal
->SetDateRange(dt1
, dt2
);
628 // ----------------------------------------------------------------------------
630 // ----------------------------------------------------------------------------
632 void wxDatePickerCtrlGeneric::DropDown(bool down
)
639 if (!m_txt
->GetValue().empty())
640 dt
.ParseFormat(m_txt
->GetValue(), m_format
);
645 m_cal
->SetDate(wxDateTime::Today());
647 wxPoint pos
=GetParent()->ClientToScreen(GetPosition());
648 m_popup
->ShowAt(pos
.x
, pos
.y
+ GetSize().y
);
662 void wxDatePickerCtrlGeneric::OnChildSetFocus(wxChildFocusEvent
&ev
)
665 m_ignoreDrop
= false;
667 wxWindow
*w
=(wxWindow
*)ev
.GetEventObject();
678 if (ev
.GetEventObject() == m_btn
)
684 void wxDatePickerCtrlGeneric::OnClick(wxCommandEvent
& WXUNUSED(event
))
688 m_ignoreDrop
= false;
699 void wxDatePickerCtrlGeneric::OnSetFocus(wxFocusEvent
& WXUNUSED(ev
))
704 m_txt
->SetSelection(-1, -1); // select everything
709 void wxDatePickerCtrlGeneric::OnKillFocus(wxFocusEvent
&ev
)
714 dt
.ParseFormat(m_txt
->GetValue(), m_format
);
717 if ( !HasFlag(wxDP_ALLOWNONE
) )
722 m_txt
->SetValue(dt
.Format(m_format
));
724 m_txt
->SetValue(wxEmptyString
);
726 // notify that we had to change the date after validation
727 if ( (dt
.IsValid() && m_currentDate
!= dt
) ||
728 (!dt
.IsValid() && m_currentDate
.IsValid()) )
731 wxDateEvent
event(this, dt
, wxEVT_DATE_CHANGED
);
732 GetEventHandler()->ProcessEvent(event
);
737 void wxDatePickerCtrlGeneric::OnSelChange(wxCalendarEvent
&ev
)
741 m_currentDate
= m_cal
->GetDate();
742 m_txt
->SetValue(m_currentDate
.Format(m_format
));
743 if (ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
)
749 ev
.SetEventObject(this);
751 GetParent()->ProcessEvent(ev
);
753 wxDateEvent
dev(this, ev
.GetDate(), wxEVT_DATE_CHANGED
);
754 GetParent()->ProcessEvent(dev
);
758 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent
&ev
)
760 ev
.SetEventObject(this);
762 GetParent()->ProcessEvent(ev
);
764 // We'll create an additional event if the date is valid.
765 // If the date isn't valid, the user's probably in the middle of typing
766 wxString txt
= m_txt
->GetValue();
770 dt
.ParseFormat(txt
, m_format
);
775 wxCalendarEvent
cev(m_cal
, wxEVT_CALENDAR_SEL_CHANGED
);
776 cev
.SetEventObject(this);
780 GetParent()->ProcessEvent(cev
);
782 wxDateEvent
dev(this, dt
, wxEVT_DATE_CHANGED
);
783 GetParent()->ProcessEvent(dev
);
787 void wxDatePickerCtrlGeneric::OnEditKey(wxKeyEvent
& ev
)
789 if (ev
.GetKeyCode() == WXK_DOWN
&& !ev
.HasModifiers())
796 void wxDatePickerCtrlGeneric::OnCalKey(wxKeyEvent
& ev
)
798 if (ev
.GetKeyCode() == WXK_ESCAPE
&& !ev
.HasModifiers())
804 #endif // wxUSE_DATEPICKCTRL_GENERIC
806 #endif // wxUSE_DATEPICKCTRL