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 3 
  82     #define TOPBUTTONBORDER   0 
  83     #define BUTTONBORDER      3 
  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 #if (BTNFLAGS & wxBU_AUTODRAW ) == 0 
 159     m_windowStyle 
|= wxBU_AUTODRAW
; 
 162     const wxSize sz 
= GetSize(); 
 163     int w 
= chkBmp
.GetWidth(), 
 164         h 
= chkBmp
.GetHeight(); 
 165     m_borderX 
= sz
.x 
- m_marginX 
- w
; 
 166     m_borderY 
= sz
.y 
- m_marginY 
- h
; 
 168     w 
= size
.x 
> 0 ? size
.x 
: sz
.x
; 
 169     h 
= size
.y 
> 0 ? size
.y 
: sz
.y
; 
 171     DoMoveWindow(pos
.x
, pos
.y
, w
, h
); 
 177 void wxDropdownButton::DoMoveWindow(int x
, int y
, int w
, int h
) 
 179     if (m_borderX 
>= 0 && m_borderY 
>= 0 && (w 
>= 0 || h 
>= 0)) 
 186             w 
= m_marginX 
+ m_borderX 
+ 15; // GTK magic size 
 191         int bw 
= w 
- m_marginX 
- m_borderX
; 
 192         int bh 
= h 
- m_marginY 
- m_borderY
; 
 196         wxBitmap 
bmp(bw
, bh
); 
 197         dc
.SelectObject(bmp
); 
 199         wxRendererNative::Get().DrawComboBoxDropButton(this, dc
, wxRect(0,0,bw
, bh
)); 
 203     wxBitmapButton::DoMoveWindow(x
, y
, w
, h
); 
 209 #include "wx/popupwin.h" 
 211 class wxDatePopupInternal 
: public wxPopupTransientWindow
 
 214     wxDatePopupInternal(wxWindow 
*parent
) : wxPopupTransientWindow(parent
) { } 
 216     void ShowAt(int x
, int y
) 
 218         Position(wxPoint(x
, y
), wxSize(0, 0)); 
 228 #else // !wxUSE_POPUPWIN 
 230 class wxDatePopupInternal 
: public wxDialog
 
 233     wxDatePopupInternal(wxWindow 
*parent
) 
 243     void ShowAt(int x
, int y
) 
 255 #endif // wxUSE_POPUPWIN/!wxUSE_POPUPWIN 
 257 // ============================================================================ 
 258 // wxDatePickerCtrlGeneric implementation 
 259 // ============================================================================ 
 261 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
) 
 262     EVT_BUTTON(CTRLID_BTN
, wxDatePickerCtrlGeneric::OnClick
) 
 263     EVT_TEXT(CTRLID_TXT
, wxDatePickerCtrlGeneric::OnText
) 
 264     EVT_CHILD_FOCUS(wxDatePickerCtrlGeneric::OnChildSetFocus
) 
 267 #ifndef wxHAS_NATIVE_DATEPICKCTRL 
 268     IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
) 
 271 // ---------------------------------------------------------------------------- 
 273 // ---------------------------------------------------------------------------- 
 275 bool wxDatePickerCtrlGeneric::Create(wxWindow 
*parent
, 
 277                                      const wxDateTime
& date
, 
 281                                      const wxValidator
& validator
, 
 282                                      const wxString
& name
) 
 284     wxASSERT_MSG( !(style 
& wxDP_SPIN
), 
 285                   _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") ); 
 287     if ( !wxControl::Create(parent
, id
, pos
, size
, 
 288                             style 
| wxCLIP_CHILDREN 
| wxWANTS_CHARS
, 
 297     m_txt 
= new wxTextCtrl(this, CTRLID_TXT
, wxEmptyString
, wxDefaultPosition
, wxDefaultSize
, TXTCTRL_FLAGS
); 
 299     m_txt
->Connect(wxEVT_KEY_DOWN
, 
 300                    wxKeyEventHandler(wxDatePickerCtrlGeneric::OnEditKey
), 
 302     m_txt
->Connect(wxEVT_KILL_FOCUS
, 
 303                    wxFocusEventHandler(wxDatePickerCtrlGeneric::OnKillFocus
), 
 306     const int height 
= m_txt
->GetBestSize().y 
- BUTTONBORDER
; 
 308     m_btn 
= new wxDropdownButton(this, CTRLID_BTN
, wxDefaultPosition
, wxSize(height
, height
)); 
 310     m_popup 
= new wxDatePopupInternal(this); 
 311     m_popup
->SetFont(GetFont()); 
 313     wxPanel 
*panel
=new wxPanel(m_popup
, CTRLID_PAN
, 
 314                                wxPoint(0, 0), wxDefaultSize
, 
 316     m_cal 
= new wxCalendarCtrl(panel
, CTRLID_CAL
, wxDefaultDateTime
, 
 317                                wxPoint(0, 0), wxDefaultSize
, 
 318                                wxCAL_SHOW_HOLIDAYS 
| wxSUNKEN_BORDER
); 
 319     m_cal
->Connect(wxEVT_CALENDAR_SEL_CHANGED
, 
 320                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 322     m_cal
->Connect(wxEVT_KEY_DOWN
, 
 323                    wxKeyEventHandler(wxDatePickerCtrlGeneric::OnCalKey
), 
 325     m_cal
->Connect(wxEVT_CALENDAR_DOUBLECLICKED
, 
 326                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 328     m_cal
->Connect(wxEVT_CALENDAR_DAY_CHANGED
, 
 329                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 331     m_cal
->Connect(wxEVT_CALENDAR_MONTH_CHANGED
, 
 332                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 334     m_cal
->Connect(wxEVT_CALENDAR_YEAR_CHANGED
, 
 335                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 338     wxWindow 
*yearControl 
= m_cal
->GetYearControl(); 
 340     Connect(wxEVT_SET_FOCUS
, 
 341             wxFocusEventHandler(wxDatePickerCtrlGeneric::OnSetFocus
)); 
 343     wxClientDC 
dc(yearControl
); 
 344     dc
.SetFont(yearControl
->GetFont()); 
 345     wxCoord width
, dummy
; 
 346     dc
.GetTextExtent(wxT("2000"), &width
, &dummy
); 
 347     width 
+= ConvertDialogToPixels(wxSize(20, 0)).x
; 
 349     wxSize calSize 
= m_cal
->GetBestSize(); 
 350     wxSize yearSize 
= yearControl
->GetSize(); 
 353     wxPoint yearPosition 
= yearControl
->GetPosition(); 
 355     SetFormat(wxT("%x")); 
 357     width 
= yearPosition
.x 
+ yearSize
.x
+2+CALBORDER
/2; 
 358     if (width 
< calSize
.x
-4) 
 361     int calPos 
= (width
-calSize
.x
)/2; 
 367     m_cal
->SetSize(calPos
, 0, calSize
.x
, calSize
.y
); 
 368     yearControl
->SetSize(width
-yearSize
.x
-CALBORDER
/2, yearPosition
.y
, 
 369                          yearSize
.x
, yearSize
.y
); 
 370     m_cal
->GetMonthControl()->Move(0, 0); 
 374     panel
->SetClientSize(width
+CALBORDER
/2, calSize
.y
-2+CALBORDER
); 
 375     m_popup
->SetClientSize(panel
->GetSize()); 
 378     SetValue(date
.IsValid() ? date 
: wxDateTime::Today()); 
 380     SetBestFittingSize(size
); 
 386 void wxDatePickerCtrlGeneric::Init() 
 394     m_ignoreDrop 
= false; 
 398 bool wxDatePickerCtrlGeneric::Destroy() 
 414     return wxControl::Destroy(); 
 417 // ---------------------------------------------------------------------------- 
 418 // overridden base class methods 
 419 // ---------------------------------------------------------------------------- 
 421 void wxDatePickerCtrlGeneric::DoMoveWindow(int x
, int y
, int w
, int h
) 
 423     wxControl::DoMoveWindow(x
, y
, w
, h
); 
 424     wxSize bs
=m_btn
->GetBestSize(); 
 425     int eh
=m_txt
->GetBestSize().y
; 
 427     m_txt
->SetSize(0, TXTPOSY
, w
-bs
.x
-RIGHTBUTTONBORDER
, h 
> eh 
? eh
-TXTPOSY 
: h
-TXTPOSY
); 
 428     m_btn
->SetSize(w 
- bs
.x
-RIGHTBUTTONBORDER
, TOPBUTTONBORDER
, bs
.x
, h 
> bs
.y 
? bs
.y 
: h
); 
 434 wxSize 
wxDatePickerCtrlGeneric::DoGetBestSize() const 
 436     int bh
=m_btn
->GetBestSize().y
; 
 437     int eh
=m_txt
->GetBestSize().y
; 
 438     return wxSize(DEFAULT_ITEM_WIDTH
, bh 
> eh 
? bh 
: eh
); 
 442 bool wxDatePickerCtrlGeneric::Show(bool show
) 
 444     if ( !wxControl::Show(show
) ) 
 462 bool wxDatePickerCtrlGeneric::Enable(bool enable
) 
 464     if ( !wxControl::Enable(enable
) ) 
 476         m_btn
->Enable(enable
); 
 481 // ---------------------------------------------------------------------------- 
 482 // wxDatePickerCtrlGeneric API 
 483 // ---------------------------------------------------------------------------- 
 486 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
, 
 487                                       const wxDateTime
& upperdate
) 
 489     return m_cal
->SetDateRange(lowerdate
, upperdate
); 
 492 bool wxDatePickerCtrlGeneric::SetFormat(const wxChar 
*fmt
) 
 495     dt
.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d")); 
 496     wxString str
=dt
.Format(fmt
); 
 497     wxChar 
*p
=(wxChar
*)str
.c_str(); 
 499     m_format
=wxEmptyString
; 
 504         if (n 
== dt
.GetDay()) 
 506             m_format
.Append(wxT("%d")); 
 509         else if (n 
== (int)dt
.GetMonth()+1) 
 511             m_format
.Append(wxT("%m")); 
 514         else if (n 
== dt
.GetYear()) 
 516             m_format
.Append(wxT("%Y")); 
 519         else if (n 
== (dt
.GetYear() % 100)) 
 521             if (GetWindowStyle() & wxDP_SHOWCENTURY
) 
 522                 m_format
.Append(wxT("%Y")); 
 524                 m_format
.Append(wxT("%y")); 
 528             m_format
.Append(*p
++); 
 533         wxArrayString allowedChars
; 
 534         for ( wxChar c 
= _T('0'); c 
<= _T('9'); c
++ ) 
 535             allowedChars
.Add(wxString(c
, 1)); 
 537         const wxChar 
*p 
= m_format
.c_str(); 
 543                 allowedChars
.Add(wxString(*p
++, 1)); 
 546         wxTextValidator 
tv(wxFILTER_INCLUDE_CHAR_LIST
); 
 547         tv
.SetIncludes(allowedChars
); 
 549         m_txt
->SetValidator(tv
); 
 551         if (m_currentDate
.IsValid()) 
 552             m_txt
->SetValue(m_currentDate
.Format(m_format
)); 
 559 wxDateTime 
wxDatePickerCtrlGeneric::GetValue() const 
 561     return m_currentDate
; 
 565 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
) 
 570             m_txt
->SetValue(date
.Format(m_format
)); 
 573             wxASSERT_MSG( HasFlag(wxDP_ALLOWNONE
), 
 574                             _T("this control must have a valid date") ); 
 576             m_txt
->SetValue(wxEmptyString
); 
 579         m_currentDate 
= date
; 
 584 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime 
*dt1
, wxDateTime 
*dt2
) const 
 587         *dt1 
= m_cal
->GetLowerDateLimit(); 
 589         *dt2 
= m_cal
->GetUpperDateLimit(); 
 595 wxDatePickerCtrlGeneric::SetRange(const wxDateTime 
&dt1
, const wxDateTime 
&dt2
) 
 597     m_cal
->SetDateRange(dt1
, dt2
); 
 600 // ---------------------------------------------------------------------------- 
 602 // ---------------------------------------------------------------------------- 
 604 void wxDatePickerCtrlGeneric::DropDown(bool down
) 
 611             if (!m_txt
->GetValue().empty()) 
 612                 dt
.ParseFormat(m_txt
->GetValue(), m_format
); 
 617                 m_cal
->SetDate(wxDateTime::Today()); 
 619             wxPoint pos
=GetParent()->ClientToScreen(GetPosition()); 
 620             m_popup
->ShowAt(pos
.x
, pos
.y 
+ GetSize().y
); 
 634 void wxDatePickerCtrlGeneric::OnChildSetFocus(wxChildFocusEvent 
&ev
) 
 637     m_ignoreDrop 
= false; 
 639     wxWindow  
*w
=(wxWindow
*)ev
.GetEventObject(); 
 650         if (ev
.GetEventObject() == m_btn
) 
 656 void wxDatePickerCtrlGeneric::OnClick(wxCommandEvent
& WXUNUSED(event
)) 
 660         m_ignoreDrop 
= false; 
 671 void wxDatePickerCtrlGeneric::OnSetFocus(wxFocusEvent
& WXUNUSED(ev
)) 
 676         m_txt
->SetSelection(-1, -1); // select everything 
 681 void wxDatePickerCtrlGeneric::OnKillFocus(wxFocusEvent 
&ev
) 
 686     dt
.ParseFormat(m_txt
->GetValue(), m_format
); 
 689         if ( !HasFlag(wxDP_ALLOWNONE
) ) 
 694         m_txt
->SetValue(dt
.Format(m_format
)); 
 696         m_txt
->SetValue(wxEmptyString
); 
 698     // notify that we had to change the date after validation 
 699     if ( (dt
.IsValid() && m_currentDate 
!= dt
) || 
 700             (!dt
.IsValid() && m_currentDate
.IsValid()) ) 
 703         wxDateEvent 
event(this, dt
, wxEVT_DATE_CHANGED
); 
 704         GetEventHandler()->ProcessEvent(event
); 
 709 void wxDatePickerCtrlGeneric::OnSelChange(wxCalendarEvent 
&ev
) 
 713         m_currentDate 
= m_cal
->GetDate(); 
 714         m_txt
->SetValue(m_currentDate
.Format(m_format
)); 
 715         if (ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
) 
 721     ev
.SetEventObject(this); 
 723     GetParent()->ProcessEvent(ev
); 
 725     wxDateEvent 
dev(this, ev
.GetDate(), wxEVT_DATE_CHANGED
); 
 726     GetParent()->ProcessEvent(dev
); 
 730 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent 
&ev
) 
 732     ev
.SetEventObject(this); 
 734     GetParent()->ProcessEvent(ev
); 
 736     // We'll create an additional event if the date is valid. 
 737     // If the date isn't valid, the user's probably in the middle of typing 
 738     wxString txt 
= m_txt
->GetValue(); 
 742         dt
.ParseFormat(txt
, m_format
); 
 747     wxCalendarEvent 
cev(m_cal
, wxEVT_CALENDAR_SEL_CHANGED
); 
 748     cev
.SetEventObject(this); 
 752     GetParent()->ProcessEvent(cev
); 
 754     wxDateEvent 
dev(this, dt
, wxEVT_DATE_CHANGED
); 
 755     GetParent()->ProcessEvent(dev
); 
 759 void wxDatePickerCtrlGeneric::OnEditKey(wxKeyEvent 
& ev
) 
 761     if (ev
.GetKeyCode() == WXK_DOWN 
&& !ev
.HasModifiers()) 
 768 void wxDatePickerCtrlGeneric::OnCalKey(wxKeyEvent 
& ev
) 
 770     if (ev
.GetKeyCode() == WXK_ESCAPE 
&& !ev
.HasModifiers()) 
 776 #endif // wxUSE_DATEPICKCTRL_GENERIC 
 778 #endif // wxUSE_DATEPICKCTRL