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     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 bw 
= w 
- m_marginX 
- m_borderX
; 
 188         int bh 
= h 
- m_marginY 
- m_borderY
; 
 192         wxBitmap 
bmp(bw
, bh
); 
 193         dc
.SelectObject(bmp
); 
 195         wxRect 
r(0,0,bw
, bh
); 
 196         wxRendererNative
& renderer 
= wxRendererNative::Get(); 
 197         renderer
.DrawComboBoxDropButton(this, dc
, r
); 
 200         wxBitmap 
bmpSel(bw
, bh
); 
 201         dc
.SelectObject(bmpSel
); 
 202         renderer
.DrawComboBoxDropButton(this, dc
, r
, wxCONTROL_PRESSED
); 
 203         SetBitmapSelected(bmpSel
); 
 206     wxBitmapButton::DoMoveWindow(x
, y
, w
, h
); 
 212 #include "wx/popupwin.h" 
 214 class wxDatePopupInternal 
: public wxPopupTransientWindow
 
 217     wxDatePopupInternal(wxWindow 
*parent
) : wxPopupTransientWindow(parent
) { } 
 219     void ShowAt(int x
, int y
) 
 221         Position(wxPoint(x
, y
), wxSize(0, 0)); 
 231 #else // !wxUSE_POPUPWIN 
 233 class wxDatePopupInternal 
: public wxDialog
 
 236     wxDatePopupInternal(wxWindow 
*parent
) 
 246     void ShowAt(int x
, int y
) 
 258 #endif // wxUSE_POPUPWIN/!wxUSE_POPUPWIN 
 260 // ============================================================================ 
 261 // wxDatePickerCtrlGeneric implementation 
 262 // ============================================================================ 
 264 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
) 
 265     EVT_BUTTON(CTRLID_BTN
, wxDatePickerCtrlGeneric::OnClick
) 
 266     EVT_TEXT(CTRLID_TXT
, wxDatePickerCtrlGeneric::OnText
) 
 267     EVT_CHILD_FOCUS(wxDatePickerCtrlGeneric::OnChildSetFocus
) 
 270 #ifndef wxHAS_NATIVE_DATEPICKCTRL 
 271     IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
) 
 274 // ---------------------------------------------------------------------------- 
 276 // ---------------------------------------------------------------------------- 
 278 bool wxDatePickerCtrlGeneric::Create(wxWindow 
*parent
, 
 280                                      const wxDateTime
& date
, 
 284                                      const wxValidator
& validator
, 
 285                                      const wxString
& name
) 
 287     wxASSERT_MSG( !(style 
& wxDP_SPIN
), 
 288                   _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") ); 
 290     if ( !wxControl::Create(parent
, id
, pos
, size
, 
 291                             style 
| wxCLIP_CHILDREN 
| wxWANTS_CHARS
, 
 300     m_txt 
= new wxTextCtrl(this, CTRLID_TXT
, wxEmptyString
, wxDefaultPosition
, wxDefaultSize
, TXTCTRL_FLAGS
); 
 302     m_txt
->Connect(wxEVT_KEY_DOWN
, 
 303                    wxKeyEventHandler(wxDatePickerCtrlGeneric::OnEditKey
), 
 305     m_txt
->Connect(wxEVT_KILL_FOCUS
, 
 306                    wxFocusEventHandler(wxDatePickerCtrlGeneric::OnKillFocus
), 
 309     const int height 
= m_txt
->GetBestSize().y 
- BUTTONBORDER
; 
 311     m_btn 
= new wxDropdownButton(this, CTRLID_BTN
, wxDefaultPosition
, wxSize(height
, height
)); 
 313     m_popup 
= new wxDatePopupInternal(this); 
 314     m_popup
->SetFont(GetFont()); 
 316     wxPanel 
*panel
=new wxPanel(m_popup
, CTRLID_PAN
, 
 317                                wxPoint(0, 0), wxDefaultSize
, 
 319     m_cal 
= new wxCalendarCtrl(panel
, CTRLID_CAL
, wxDefaultDateTime
, 
 320                                wxPoint(0, 0), wxDefaultSize
, 
 321                                wxCAL_SHOW_HOLIDAYS 
| wxSUNKEN_BORDER
); 
 322     m_cal
->Connect(wxEVT_CALENDAR_SEL_CHANGED
, 
 323                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 325     m_cal
->Connect(wxEVT_KEY_DOWN
, 
 326                    wxKeyEventHandler(wxDatePickerCtrlGeneric::OnCalKey
), 
 328     m_cal
->Connect(wxEVT_CALENDAR_DOUBLECLICKED
, 
 329                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 331     m_cal
->Connect(wxEVT_CALENDAR_DAY_CHANGED
, 
 332                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 334     m_cal
->Connect(wxEVT_CALENDAR_MONTH_CHANGED
, 
 335                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 337     m_cal
->Connect(wxEVT_CALENDAR_YEAR_CHANGED
, 
 338                    wxCalendarEventHandler(wxDatePickerCtrlGeneric::OnSelChange
), 
 341     wxWindow 
*yearControl 
= m_cal
->GetYearControl(); 
 343     Connect(wxEVT_SET_FOCUS
, 
 344             wxFocusEventHandler(wxDatePickerCtrlGeneric::OnSetFocus
)); 
 346     wxClientDC 
dc(yearControl
); 
 347     dc
.SetFont(yearControl
->GetFont()); 
 348     wxCoord width
, dummy
; 
 349     dc
.GetTextExtent(wxT("2000"), &width
, &dummy
); 
 350     width 
+= ConvertDialogToPixels(wxSize(20, 0)).x
; 
 352     wxSize calSize 
= m_cal
->GetBestSize(); 
 353     wxSize yearSize 
= yearControl
->GetSize(); 
 356     wxPoint yearPosition 
= yearControl
->GetPosition(); 
 358     SetFormat(wxT("%x")); 
 360     width 
= yearPosition
.x 
+ yearSize
.x
+2+CALBORDER
/2; 
 361     if (width 
< calSize
.x
-4) 
 364     int calPos 
= (width
-calSize
.x
)/2; 
 370     m_cal
->SetSize(calPos
, 0, calSize
.x
, calSize
.y
); 
 371     yearControl
->SetSize(width
-yearSize
.x
-CALBORDER
/2, yearPosition
.y
, 
 372                          yearSize
.x
, yearSize
.y
); 
 373     m_cal
->GetMonthControl()->Move(0, 0); 
 377     panel
->SetClientSize(width
+CALBORDER
/2, calSize
.y
-2+CALBORDER
); 
 378     m_popup
->SetClientSize(panel
->GetSize()); 
 381     SetValue(date
.IsValid() ? date 
: wxDateTime::Today()); 
 383     SetBestFittingSize(size
); 
 389 void wxDatePickerCtrlGeneric::Init() 
 397     m_ignoreDrop 
= false; 
 401 bool wxDatePickerCtrlGeneric::Destroy() 
 417     return wxControl::Destroy(); 
 420 // ---------------------------------------------------------------------------- 
 421 // overridden base class methods 
 422 // ---------------------------------------------------------------------------- 
 424 void wxDatePickerCtrlGeneric::DoMoveWindow(int x
, int y
, int w
, int h
) 
 426     wxControl::DoMoveWindow(x
, y
, w
, h
); 
 427     wxSize bs
=m_btn
->GetBestSize(); 
 428     int eh
=m_txt
->GetBestSize().y
; 
 430     m_txt
->SetSize(0, TXTPOSY
, w
-bs
.x
-RIGHTBUTTONBORDER
, h 
> eh 
? eh
-TXTPOSY 
: h
-TXTPOSY
); 
 431     m_btn
->SetSize(w 
- bs
.x
-RIGHTBUTTONBORDER
, TOPBUTTONBORDER
, bs
.x
, h 
> bs
.y 
? bs
.y 
: h
); 
 437 wxSize 
wxDatePickerCtrlGeneric::DoGetBestSize() const 
 439     int bh
=m_btn
->GetBestSize().y
; 
 440     int eh
=m_txt
->GetBestSize().y
; 
 441     return wxSize(DEFAULT_ITEM_WIDTH
, bh 
> eh 
? bh 
: eh
); 
 445 bool wxDatePickerCtrlGeneric::Show(bool show
) 
 447     if ( !wxControl::Show(show
) ) 
 465 bool wxDatePickerCtrlGeneric::Enable(bool enable
) 
 467     if ( !wxControl::Enable(enable
) ) 
 481         m_btn
->Enable(enable
); 
 486 // ---------------------------------------------------------------------------- 
 487 // wxDatePickerCtrlGeneric API 
 488 // ---------------------------------------------------------------------------- 
 491 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
, 
 492                                       const wxDateTime
& upperdate
) 
 494     return m_cal
->SetDateRange(lowerdate
, upperdate
); 
 497 bool wxDatePickerCtrlGeneric::SetFormat(const wxChar 
*fmt
) 
 500     dt
.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d")); 
 501     wxString str
=dt
.Format(fmt
); 
 502     wxChar 
*p
=(wxChar
*)str
.c_str(); 
 504     m_format
=wxEmptyString
; 
 509         if (n 
== dt
.GetDay()) 
 511             m_format
.Append(wxT("%d")); 
 514         else if (n 
== (int)dt
.GetMonth()+1) 
 516             m_format
.Append(wxT("%m")); 
 519         else if (n 
== dt
.GetYear()) 
 521             m_format
.Append(wxT("%Y")); 
 524         else if (n 
== (dt
.GetYear() % 100)) 
 526             if (GetWindowStyle() & wxDP_SHOWCENTURY
) 
 527                 m_format
.Append(wxT("%Y")); 
 529                 m_format
.Append(wxT("%y")); 
 533             m_format
.Append(*p
++); 
 538         wxArrayString allowedChars
; 
 539         for ( wxChar c 
= _T('0'); c 
<= _T('9'); c
++ ) 
 540             allowedChars
.Add(wxString(c
, 1)); 
 542         const wxChar 
*p 
= m_format
.c_str(); 
 548                 allowedChars
.Add(wxString(*p
++, 1)); 
 551         wxTextValidator 
tv(wxFILTER_INCLUDE_CHAR_LIST
); 
 552         tv
.SetIncludes(allowedChars
); 
 554         m_txt
->SetValidator(tv
); 
 556         if (m_currentDate
.IsValid()) 
 557             m_txt
->SetValue(m_currentDate
.Format(m_format
)); 
 564 wxDateTime 
wxDatePickerCtrlGeneric::GetValue() const 
 566     return m_currentDate
; 
 570 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
) 
 575             m_txt
->SetValue(date
.Format(m_format
)); 
 578             wxASSERT_MSG( HasFlag(wxDP_ALLOWNONE
), 
 579                             _T("this control must have a valid date") ); 
 581             m_txt
->SetValue(wxEmptyString
); 
 584         m_currentDate 
= date
; 
 589 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime 
*dt1
, wxDateTime 
*dt2
) const 
 592         *dt1 
= m_cal
->GetLowerDateLimit(); 
 594         *dt2 
= m_cal
->GetUpperDateLimit(); 
 600 wxDatePickerCtrlGeneric::SetRange(const wxDateTime 
&dt1
, const wxDateTime 
&dt2
) 
 602     m_cal
->SetDateRange(dt1
, dt2
); 
 605 // ---------------------------------------------------------------------------- 
 607 // ---------------------------------------------------------------------------- 
 609 void wxDatePickerCtrlGeneric::DropDown(bool down
) 
 616             if (!m_txt
->GetValue().empty()) 
 617                 dt
.ParseFormat(m_txt
->GetValue(), m_format
); 
 622                 m_cal
->SetDate(wxDateTime::Today()); 
 624             wxPoint pos
=GetParent()->ClientToScreen(GetPosition()); 
 625             m_popup
->ShowAt(pos
.x
, pos
.y 
+ GetSize().y
); 
 639 void wxDatePickerCtrlGeneric::OnChildSetFocus(wxChildFocusEvent 
&ev
) 
 642     m_ignoreDrop 
= false; 
 644     wxWindow  
*w
=(wxWindow
*)ev
.GetEventObject(); 
 655         if (ev
.GetEventObject() == m_btn
) 
 661 void wxDatePickerCtrlGeneric::OnClick(wxCommandEvent
& WXUNUSED(event
)) 
 665         m_ignoreDrop 
= false; 
 676 void wxDatePickerCtrlGeneric::OnSetFocus(wxFocusEvent
& WXUNUSED(ev
)) 
 681         m_txt
->SetSelection(-1, -1); // select everything 
 686 void wxDatePickerCtrlGeneric::OnKillFocus(wxFocusEvent 
&ev
) 
 691     dt
.ParseFormat(m_txt
->GetValue(), m_format
); 
 694         if ( !HasFlag(wxDP_ALLOWNONE
) ) 
 699         m_txt
->SetValue(dt
.Format(m_format
)); 
 701         m_txt
->SetValue(wxEmptyString
); 
 703     // notify that we had to change the date after validation 
 704     if ( (dt
.IsValid() && m_currentDate 
!= dt
) || 
 705             (!dt
.IsValid() && m_currentDate
.IsValid()) ) 
 708         wxDateEvent 
event(this, dt
, wxEVT_DATE_CHANGED
); 
 709         GetEventHandler()->ProcessEvent(event
); 
 714 void wxDatePickerCtrlGeneric::OnSelChange(wxCalendarEvent 
&ev
) 
 718         m_currentDate 
= m_cal
->GetDate(); 
 719         m_txt
->SetValue(m_currentDate
.Format(m_format
)); 
 720         if (ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
) 
 726     ev
.SetEventObject(this); 
 728     GetParent()->ProcessEvent(ev
); 
 730     wxDateEvent 
dev(this, ev
.GetDate(), wxEVT_DATE_CHANGED
); 
 731     GetParent()->ProcessEvent(dev
); 
 735 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent 
&ev
) 
 737     ev
.SetEventObject(this); 
 739     GetParent()->ProcessEvent(ev
); 
 741     // We'll create an additional event if the date is valid. 
 742     // If the date isn't valid, the user's probably in the middle of typing 
 743     wxString txt 
= m_txt
->GetValue(); 
 747         dt
.ParseFormat(txt
, m_format
); 
 752     wxCalendarEvent 
cev(m_cal
, wxEVT_CALENDAR_SEL_CHANGED
); 
 753     cev
.SetEventObject(this); 
 757     GetParent()->ProcessEvent(cev
); 
 759     wxDateEvent 
dev(this, dt
, wxEVT_DATE_CHANGED
); 
 760     GetParent()->ProcessEvent(dev
); 
 764 void wxDatePickerCtrlGeneric::OnEditKey(wxKeyEvent 
& ev
) 
 766     if (ev
.GetKeyCode() == WXK_DOWN 
&& !ev
.HasModifiers()) 
 773 void wxDatePickerCtrlGeneric::OnCalKey(wxKeyEvent 
& ev
) 
 775     if (ev
.GetKeyCode() == WXK_ESCAPE 
&& !ev
.HasModifiers()) 
 781 #endif // wxUSE_DATEPICKCTRL_GENERIC 
 783 #endif // wxUSE_DATEPICKCTRL