1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/datectrl.cpp 
   3 // Purpose:     wxDatePickerCtrl implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2005 Vadim Zeitlin <vadim@wxwindows.org> 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #include "wx/wxprec.h" 
  26 #if wxUSE_DATEPICKCTRL 
  29     #include "wx/msw/wrapwin.h" 
  30     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly" 
  33     #include "wx/dcclient.h" 
  34     #include "wx/settings.h" 
  35     #include "wx/msw/private.h" 
  38 #include "wx/datectrl.h" 
  40 #include "wx/msw/private/datecontrols.h" 
  42 #include "wx/dateevt.h" 
  44 // apparently some versions of mingw define these macros erroneously 
  45 #ifndef DateTime_GetSystemtime 
  46     #define DateTime_GetSystemtime DateTime_GetSystemTime 
  49 #ifndef DateTime_SetSystemtime 
  50     #define DateTime_SetSystemtime DateTime_SetSystemTime 
  53 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
) 
  55 // ============================================================================ 
  57 // ============================================================================ 
  59 // ---------------------------------------------------------------------------- 
  60 // wxDatePickerCtrl creation 
  61 // ---------------------------------------------------------------------------- 
  64 wxDatePickerCtrl::Create(wxWindow 
*parent
, 
  70                          const wxValidator
& validator
, 
  73     if ( !wxMSWDateControls::CheckInitialization() ) 
  76     // use wxDP_SPIN if wxDP_DEFAULT (0) was given as style 
  77     if ( !(style 
& wxDP_DROPDOWN
) ) 
  80     // initialize the base class 
  81     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
  84     // create the native control 
  85     if ( !MSWCreateControl(DATETIMEPICK_CLASS
, wxEmptyString
, pos
, size
) ) 
  88     if ( dt
.IsValid() || (style 
& wxDP_ALLOWNONE
) ) 
  91         SetValue(wxDateTime::Today()); 
  96 WXDWORD 
wxDatePickerCtrl::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
  98     WXDWORD styleMSW 
= wxDatePickerCtrlBase::MSWGetStyle(style
, exstyle
); 
 100     // although MSDN doesn't mention it, DTS_UPDOWN doesn't work with 
 102     if ( wxApp::GetComCtl32Version() > 472 && (style 
& wxDP_SPIN
) ) 
 103         styleMSW 
|= DTS_UPDOWN
; 
 104     //else: drop down by default 
 106 #ifdef DTS_SHORTDATECENTURYFORMAT 
 107     if ( style 
& wxDP_SHOWCENTURY 
) 
 108         styleMSW 
|= DTS_SHORTDATECENTURYFORMAT
; 
 110 #endif // DTS_SHORTDATECENTURYFORMAT 
 111         styleMSW 
|= DTS_SHORTDATEFORMAT
; 
 113     if ( style 
& wxDP_ALLOWNONE 
) 
 114         styleMSW 
|= DTS_SHOWNONE
; 
 119 // TODO: handle WM_WININICHANGE 
 121 // ---------------------------------------------------------------------------- 
 122 // wxDatePickerCtrl geometry 
 123 // ---------------------------------------------------------------------------- 
 125 wxSize 
wxDatePickerCtrl::DoGetBestSize() const 
 127     wxClientDC 
dc(const_cast<wxDatePickerCtrl 
*>(this)); 
 129     // we can't use FormatDate() here as the CRT doesn't always use the same 
 130     // format as the date picker control 
 132     for ( int len 
= 100; ; len 
*= 2 ) 
 136                     LOCALE_USER_DEFAULT
,    // the control should use the same 
 137                     DATE_SHORTDATE
,         // the format used by the control 
 138                     NULL
,                   // use current date (we don't care) 
 139                     NULL
,                   // no custom format 
 140                     wxStringBuffer(s
, len
), // output buffer 
 141                     len                     
// and its length 
 148         const DWORD rc 
= ::GetLastError(); 
 149         if ( rc 
!= ERROR_INSUFFICIENT_BUFFER 
) 
 151             wxLogApiError(_T("GetDateFormat"), rc
); 
 153             // fall back on wxDateTime, what else to do? 
 154             s 
= wxDateTime::Today().FormatDate(); 
 159     // the best size for the control is bigger than just the string 
 160     // representation of todays date because the control must accommodate any 
 161     // date and while the widths of all digits are usually about the same, the 
 162     // width of the month string varies a lot, so try to account for it 
 166     dc
.GetTextExtent(s
, &x
, &y
); 
 168     // account for the drop-down arrow or spin arrows 
 169     x 
+= wxSystemSettings::GetMetric(wxSYS_HSCROLL_ARROW_X
); 
 171     // and for the checkbox if we have it 
 172     if ( HasFlag(wxDP_ALLOWNONE
) ) 
 173         x 
+= 3*GetCharWidth(); 
 175     wxSize 
best(x
, EDIT_HEIGHT_FROM_CHAR_HEIGHT(y
)); 
 180 // ---------------------------------------------------------------------------- 
 181 // wxDatePickerCtrl operations 
 182 // ---------------------------------------------------------------------------- 
 184 void wxDatePickerCtrl::SetValue(const wxDateTime
& dt
) 
 186     wxCHECK_RET( dt
.IsValid() || HasFlag(wxDP_ALLOWNONE
), 
 187                     _T("this control requires a valid date") ); 
 191         dt
.GetAsMSWSysTime(&st
); 
 192     if ( !DateTime_SetSystemtime(GetHwnd(), 
 193                                  dt
.IsValid() ? GDT_VALID 
: GDT_NONE
, 
 196         wxLogDebug(_T("DateTime_SetSystemtime() failed")); 
 199     // we need to keep only the date part, times don't make sense for this 
 200     // control (in particular, comparisons with other dates would fail) 
 202     if ( m_date
.IsValid() ) 
 206 wxDateTime 
wxDatePickerCtrl::GetValue() const 
 211     if ( DateTime_GetSystemtime(GetHwnd(), &st
) == GDT_VALID 
) 
 213         dt
.SetFromMSWSysTime(st
); 
 216     wxASSERT_MSG( m_date
.IsValid() == dt
.IsValid() && 
 217                     (!dt
.IsValid() || dt 
== m_date
), 
 218                   _T("bug in wxDatePickerCtrl: m_date not in sync") ); 
 219 #endif // wxDEBUG_LEVEL 
 224 void wxDatePickerCtrl::SetRange(const wxDateTime
& dt1
, const wxDateTime
& dt2
) 
 231         dt1
.GetAsMSWSysTime(st 
+ 0); 
 237         dt2
.GetAsMSWSysTime(st 
+ 1); 
 241     if ( !DateTime_SetRange(GetHwnd(), flags
, st
) ) 
 243         wxLogDebug(_T("DateTime_SetRange() failed")); 
 247 bool wxDatePickerCtrl::GetRange(wxDateTime 
*dt1
, wxDateTime 
*dt2
) const 
 251     DWORD flags 
= DateTime_GetRange(GetHwnd(), st
); 
 254         if ( flags 
& GDTR_MIN 
) 
 255             dt1
->SetFromMSWSysTime(st
[0]); 
 257             *dt1 
= wxDefaultDateTime
; 
 262         if ( flags 
& GDTR_MAX 
) 
 263             dt2
->SetFromMSWSysTime(st
[1]); 
 265             *dt2 
= wxDefaultDateTime
; 
 271 // ---------------------------------------------------------------------------- 
 272 // wxDatePickerCtrl events 
 273 // ---------------------------------------------------------------------------- 
 276 wxDatePickerCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM 
*result
) 
 278     NMHDR
* hdr 
= (NMHDR 
*)lParam
; 
 281         case DTN_DATETIMECHANGE
: 
 283             NMDATETIMECHANGE 
*dtch 
= (NMDATETIMECHANGE 
*)hdr
; 
 285             if ( dtch
->dwFlags 
== GDT_VALID 
) 
 286                 dt
.SetFromMSWSysTime(dtch
->st
); 
 288             // filter out duplicate DTN_DATETIMECHANGE events which the native 
 289             // control sends us when using wxDP_DROPDOWN style 
 290             if ( (m_date
.IsValid() != dt
.IsValid()) || 
 291                     (m_date
.IsValid() && dt 
!= m_date
) ) 
 294                 wxDateEvent 
event(this, dt
, wxEVT_DATE_CHANGED
); 
 295                 if ( HandleWindowEvent(event
) ) 
 301             //else: both the old and new values are invalid, nothing changed 
 305     return wxDatePickerCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
); 
 308 #endif // wxUSE_DATEPICKCTRL