X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/82c6027b47b738c86cfd1225e86e445f3523e744..9741fd45f734ea30e094f5b15f092b8394621204:/src/msw/calctrl.cpp?ds=sidebyside diff --git a/src/msw/calctrl.cpp b/src/msw/calctrl.cpp index 69c5abeb6b..859a549bd6 100644 --- a/src/msw/calctrl.cpp +++ b/src/msw/calctrl.cpp @@ -36,6 +36,27 @@ IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl) +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +namespace +{ + +// values of week days used by the native control +enum +{ + MonthCal_Monday, + MonthCal_Tuesday, + MonthCal_Wednesday, + MonthCal_Thursday, + MonthCal_Friday, + MonthCal_Saturday, + MonthCal_Sunday +}; + +} // anonymous namespace + // ============================================================================ // implementation // ============================================================================ @@ -44,6 +65,12 @@ IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl) // wxCalendarCtrl creation // ---------------------------------------------------------------------------- +void wxCalendarCtrl::Init() +{ + m_marks = + m_holidays = 0; +} + bool wxCalendarCtrl::Create(wxWindow *parent, wxWindowID id, @@ -56,6 +83,9 @@ wxCalendarCtrl::Create(wxWindow *parent, if ( !wxMSWDateControls::CheckInitialization() ) return false; + // we need the arrows for the navigation + style |= wxWANTS_CHARS; + // initialize the base class if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) ) return false; @@ -76,7 +106,7 @@ wxCalendarCtrl::Create(wxWindow *parent, } else { - wxLogLastError(_T("GetClassInfoEx(SysMonthCal32)")); + wxLogLastError(wxT("GetClassInfoEx(SysMonthCal32)")); } } @@ -87,10 +117,16 @@ wxCalendarCtrl::Create(wxWindow *parent, if ( !MSWCreateControl(clsname, wxEmptyString, pos, size) ) return false; + // initialize the control + UpdateFirstDayOfWeek(); + SetDate(dt.IsValid() ? dt : wxDateTime::Today()); - UpdateMarks(); + if ( SetHolidayAttrs() ) + UpdateMarks(); + Connect(wxEVT_LEFT_DOWN, + wxMouseEventHandler(wxCalendarCtrl::MSWOnClick)); Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(wxCalendarCtrl::MSWOnDoubleClick)); @@ -101,9 +137,8 @@ WXDWORD wxCalendarCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const { WXDWORD styleMSW = wxCalendarCtrlBase::MSWGetStyle(style, exstyle); - // right now we don't support any native styles but we should add wx styles - // corresponding to MCS_NOTODAY, MCS_NOTODAYCIRCLE and MCS_WEEKNUMBERS - // probably (TODO) + // right now we don't support all native styles but we should add wx styles + // corresponding to MCS_NOTODAY and MCS_NOTODAYCIRCLE probably (TODO) // for compatibility with the other versions, just turn off today display // unconditionally for now @@ -112,15 +147,27 @@ WXDWORD wxCalendarCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const // we also need this style for Mark() to work styleMSW |= MCS_DAYSTATE; + if ( style & wxCAL_SHOW_WEEK_NUMBERS ) + styleMSW |= MCS_WEEKNUMBERS; + return styleMSW; } -// TODO: handle WM_WININICHANGE +void wxCalendarCtrl::SetWindowStyleFlag(long style) +{ + const bool hadMondayFirst = HasFlag(wxCAL_MONDAY_FIRST); + + wxCalendarCtrlBase::SetWindowStyleFlag(style); + + if ( HasFlag(wxCAL_MONDAY_FIRST) != hadMondayFirst ) + UpdateFirstDayOfWeek(); +} // ---------------------------------------------------------------------------- // wxCalendarCtrl geometry // ---------------------------------------------------------------------------- +// TODO: handle WM_WININICHANGE wxSize wxCalendarCtrl::DoGetBestSize() const { RECT rc; @@ -140,6 +187,15 @@ wxCalendarCtrl::HitTest(const wxPoint& pos, wxDateTime::WeekDay *wd) { WinStruct hti; + + // Vista and later SDKs add a few extra fields to MCHITTESTINFO which are + // not supported by the previous versions, as we don't use them anyhow we + // should pretend that we always use the old struct format to make the call + // below work on pre-Vista systems (see #11057) +#ifdef MCHITTESTINFO_V1_SIZE + hti.cbSize = MCHITTESTINFO_V1_SIZE; +#endif + hti.pt.x = pos.x; hti.pt.y = pos.y; switch ( MonthCal_HitTest(GetHwnd(), &hti) ) @@ -158,13 +214,32 @@ wxCalendarCtrl::HitTest(const wxPoint& pos, case MCHT_CALENDARDATE: if ( date ) - wxMSWDateControls::FromSystemTime(date, hti.st); + date->SetFromMSWSysDate(hti.st); return wxCAL_HITTEST_DAY; case MCHT_CALENDARDAY: if ( wd ) { - *wd = wx_static_cast(wxDateTime::WeekDay, hti.st.wDayOfWeek); + int day = hti.st.wDayOfWeek; + + // the native control returns incorrect day of the week when + // the first day isn't Monday, i.e. the first column is always + // "Monday" even if its label is "Sunday", compensate for it + const int first = LOWORD(MonthCal_GetFirstDayOfWeek(GetHwnd())); + if ( first == MonthCal_Monday ) + { + // as MonthCal_Monday is 0 while wxDateTime::Mon is 1, + // normally we need to do this to transform from MSW + // convention to wx one + day++; + day %= 7; + } + //else: but when the first day is MonthCal_Sunday, the native + // control still returns 0 (i.e. MonthCal_Monday) for the + // first column which looks like a bug in it but to work + // around it it's enough to not apply the correction above + + *wd = static_cast(day); } return wxCAL_HITTEST_HEADER; @@ -189,23 +264,24 @@ bool wxCalendarCtrl::SetDate(const wxDateTime& dt) wxCHECK_MSG( dt.IsValid(), false, "invalid date" ); SYSTEMTIME st; - wxMSWDateControls::ToSystemTime(&st, dt); + dt.GetAsMSWSysDate(&st); if ( !MonthCal_SetCurSel(GetHwnd(), &st) ) { - wxLogDebug(_T("DateTime_SetSystemtime() failed")); + wxLogDebug(wxT("DateTime_SetSystemtime() failed")); return false; } - m_date = dt; + m_date = dt.GetDateOnly(); return true; } wxDateTime wxCalendarCtrl::GetDate() const { -#ifdef __WXDEBUG__ +#if wxDEBUG_LEVEL SYSTEMTIME st; + if ( !MonthCal_GetCurSel(GetHwnd(), &st) ) { wxASSERT_MSG( !m_date.IsValid(), "mismatch between data and control" ); @@ -214,10 +290,14 @@ wxDateTime wxCalendarCtrl::GetDate() const } wxDateTime dt; - wxMSWDateControls::FromSystemTime(&dt, st); + dt.SetFromMSWSysDate(st); - wxASSERT_MSG( dt == m_date, "mismatch between data and control" ); -#endif // __WXDEBUG__ + // Windows XP and earlier didn't include the time component into the + // returned date but Windows 7 does, so we can't compare the full objects + // in the same way under all the Windows versions, just compare their date + // parts + wxASSERT_MSG( dt.IsSameDate(m_date), "mismatch between data and control" ); +#endif // wxDEBUG_LEVEL return m_date; } @@ -229,19 +309,19 @@ bool wxCalendarCtrl::SetDateRange(const wxDateTime& dt1, const wxDateTime& dt2) DWORD flags = 0; if ( dt1.IsValid() ) { - wxMSWDateControls::ToSystemTime(&st[0], dt1); + dt1.GetAsMSWSysTime(st + 0); flags |= GDTR_MIN; } if ( dt2.IsValid() ) { - wxMSWDateControls::ToSystemTime(&st[1], dt2); + dt2.GetAsMSWSysTime(st + 1); flags |= GDTR_MAX; } if ( !MonthCal_SetRange(GetHwnd(), flags, st) ) { - wxLogDebug(_T("MonthCal_SetRange() failed")); + wxLogDebug(wxT("MonthCal_SetRange() failed")); } return flags != 0; @@ -255,7 +335,7 @@ bool wxCalendarCtrl::GetDateRange(wxDateTime *dt1, wxDateTime *dt2) const if ( dt1 ) { if ( flags & GDTR_MIN ) - wxMSWDateControls::FromSystemTime(dt1, st[0]); + dt1->SetFromMSWSysDate(st[0]); else *dt1 = wxDefaultDateTime; } @@ -263,7 +343,7 @@ bool wxCalendarCtrl::GetDateRange(wxDateTime *dt1, wxDateTime *dt2) const if ( dt2 ) { if ( flags & GDTR_MAX ) - wxMSWDateControls::FromSystemTime(dt2, st[1]); + dt2->SetFromMSWSysDate(st[1]); else *dt2 = wxDefaultDateTime; } @@ -309,19 +389,53 @@ void wxCalendarCtrl::Mark(size_t day, bool mark) UpdateMarks(); } -void wxCalendarCtrl::UpdateMarks() +void wxCalendarCtrl::SetHoliday(size_t day) { - MONTHDAYSTATE states[3]; - const int nMonths = MonthCal_GetMonthRange(GetHwnd(), GMR_DAYSTATE, NULL); - wxCHECK_RET( nMonths <= WXSIZEOF(states), "unexpected months range" ); + wxCHECK_RET( day > 0 && day < 32, "invalid day" ); - for ( int i = 0; i < nMonths; i++ ) - states[i] = m_marks; + m_holidays |= 1 << (day - 1); +} - if ( !MonthCal_SetDayState(GetHwnd(), nMonths, states) ) +void wxCalendarCtrl::UpdateMarks() +{ + // we show only one full month but there can be some days from the month + // before it and from the one after it so days from 3 different months can + // be partially shown + MONTHDAYSTATE states[3] = { 0 }; + const DWORD nMonths = MonthCal_GetMonthRange(GetHwnd(), GMR_DAYSTATE, NULL); + + // although in principle the calendar might not show any days from the + // preceding months, it seems like it always does, consider e.g. Feb 2010 + // which starts on Monday and ends on Sunday and so could fit on 4 lines + // without showing any subsequent months -- the standard control still + // shows it on 6 lines and the number of visible months is still 3 + // + // OTOH Windows 7 control can show all 12 months or even years or decades + // in its window if you "zoom out" of it by double clicking on free areas + // so the return value can be (much, in case of decades view) greater than + // 3 but in this case marks are not visible anyhow so simply ignore it + if ( nMonths < WXSIZEOF(states) ) { - wxLogLastError(_T("MonthCal_SetDayState")); + wxFAIL_MSG("unexpectedly few months shown in the control"); } + else if ( nMonths == WXSIZEOF(states) ) + { + // the fully visible month is the one in the middle + states[1] = m_marks | m_holidays; + + if ( !MonthCal_SetDayState(GetHwnd(), nMonths, states) ) + { + wxLogLastError(wxT("MonthCal_SetDayState")); + } + } + //else: not a month view at all +} + +void wxCalendarCtrl::UpdateFirstDayOfWeek() +{ + MonthCal_SetFirstDayOfWeek(GetHwnd(), + HasFlag(wxCAL_MONDAY_FIRST) ? MonthCal_Monday + : MonthCal_Sunday); } // ---------------------------------------------------------------------------- @@ -339,14 +453,20 @@ bool wxCalendarCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // which expects GetDate() to return the new date const wxDateTime dateOld = m_date; const NMSELCHANGE * const sch = (NMSELCHANGE *)lParam; - wxMSWDateControls::FromSystemTime(&m_date, sch->stSelStart); + m_date.SetFromMSWSysDate(sch->stSelStart); // changing the year or the month results in a second dummy // MCN_SELCHANGE event on this system which doesn't really // change anything -- filter it out if ( m_date != dateOld ) { - GenerateAllChangeEvents(dateOld); + if ( GenerateAllChangeEvents(dateOld) ) + { + // month changed, need to update the holidays if we use + // them + if ( SetHolidayAttrs() ) + UpdateMarks(); + } } } break; @@ -356,7 +476,7 @@ bool wxCalendarCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) const NMDAYSTATE * const ds = (NMDAYSTATE *)lParam; for ( int i = 0; i < ds->cDayState; i++ ) { - ds->prgDayState[i] = m_marks; + ds->prgDayState[i] = m_marks | m_holidays; } } break; @@ -380,4 +500,13 @@ void wxCalendarCtrl::MSWOnDoubleClick(wxMouseEvent& event) event.Skip(); } +void wxCalendarCtrl::MSWOnClick(wxMouseEvent& event) +{ + // for some reason, the control doesn't get focus on its own when the user + // clicks in it + SetFocus(); + + event.Skip(); +} + #endif // wxUSE_CALENDARCTRL