X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7ec5c42e94de656d0ce21333ef684493a8999410..2edac25b2ea3876bf57a54d314d59050b5fbd8e7:/src/msw/calctrl.cpp diff --git a/src/msw/calctrl.cpp b/src/msw/calctrl.cpp index b9b8e18f2b..37bd62296f 100644 --- a/src/msw/calctrl.cpp +++ b/src/msw/calctrl.cpp @@ -56,16 +56,52 @@ 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; - // create the native control - if ( !MSWCreateControl(MONTHCAL_CLASS, wxEmptyString, pos, size) ) + // create the native control: this is a bit tricky as we want to receive + // double click events but the MONTHCAL_CLASS doesn't use CS_DBLCLKS style + // and so we create our own copy of it which does + static ClassRegistrar s_clsMonthCal; + if ( !s_clsMonthCal.IsInitialized() ) + { + // get a copy of standard class and modify it + WNDCLASS wc; + if ( ::GetClassInfo(NULL, MONTHCAL_CLASS, &wc) ) + { + wc.lpszClassName = wxT("_wx_SysMonthCtl32"); + wc.style |= CS_DBLCLKS; + s_clsMonthCal.Register(wc); + } + else + { + wxLogLastError(_T("GetClassInfoEx(SysMonthCal32)")); + } + } + + const wxChar * const clsname = s_clsMonthCal.IsRegistered() + ? s_clsMonthCal.GetName().wx_str() + : MONTHCAL_CLASS; + + if ( !MSWCreateControl(clsname, wxEmptyString, pos, size) ) return false; + // initialize the control + UpdateFirstDayOfWeek(); + SetDate(dt.IsValid() ? dt : wxDateTime::Today()); + UpdateMarks(); + + Connect(wxEVT_LEFT_DOWN, + wxMouseEventHandler(wxCalendarCtrl::MSWOnClick)); + Connect(wxEVT_LEFT_DCLICK, + wxMouseEventHandler(wxCalendarCtrl::MSWOnDoubleClick)); + return true; } @@ -81,15 +117,27 @@ WXDWORD wxCalendarCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const // unconditionally for now styleMSW |= MCS_NOTODAY; + // we also need this style for Mark() to work + styleMSW |= MCS_DAYSTATE; + 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; @@ -103,6 +151,52 @@ wxSize wxCalendarCtrl::DoGetBestSize() const return best; } +wxCalendarHitTestResult +wxCalendarCtrl::HitTest(const wxPoint& pos, + wxDateTime *date, + wxDateTime::WeekDay *wd) +{ + WinStruct hti; + hti.pt.x = pos.x; + hti.pt.y = pos.y; + switch ( MonthCal_HitTest(GetHwnd(), &hti) ) + { + default: + case MCHT_CALENDARWEEKNUM: + wxFAIL_MSG( "unexpected" ); + // fall through + + case MCHT_NOWHERE: + case MCHT_CALENDARBK: + case MCHT_TITLEBK: + case MCHT_TITLEMONTH: + case MCHT_TITLEYEAR: + return wxCAL_HITTEST_NOWHERE; + + case MCHT_CALENDARDATE: + if ( date ) + wxMSWDateControls::FromSystemTime(date, hti.st); + return wxCAL_HITTEST_DAY; + + case MCHT_CALENDARDAY: + if ( wd ) + { + *wd = wx_static_cast(wxDateTime::WeekDay, hti.st.wDayOfWeek); + } + return wxCAL_HITTEST_HEADER; + + case MCHT_TITLEBTNNEXT: + return wxCAL_HITTEST_INCMONTH; + + case MCHT_TITLEBTNPREV: + return wxCAL_HITTEST_DECMONTH; + + case MCHT_CALENDARDATENEXT: + case MCHT_CALENDARDATEPREV: + return wxCAL_HITTEST_SURROUNDING_WEEK; + } +} + // ---------------------------------------------------------------------------- // wxCalendarCtrl operations // ---------------------------------------------------------------------------- @@ -120,18 +214,29 @@ bool wxCalendarCtrl::SetDate(const wxDateTime& dt) return false; } + m_date = dt; + return true; } wxDateTime wxCalendarCtrl::GetDate() const { +#ifdef __WXDEBUG__ SYSTEMTIME st; if ( !MonthCal_GetCurSel(GetHwnd(), &st) ) + { + wxASSERT_MSG( !m_date.IsValid(), "mismatch between data and control" ); + return wxDefaultDateTime; + } wxDateTime dt; wxMSWDateControls::FromSystemTime(&dt, st); - return dt; + + wxASSERT_MSG( dt == m_date, "mismatch between data and control" ); +#endif // __WXDEBUG__ + + return m_date; } bool wxCalendarCtrl::SetDateRange(const wxDateTime& dt1, const wxDateTime& dt2) @@ -209,7 +314,36 @@ bool wxCalendarCtrl::EnableMonthChange(bool enable) void wxCalendarCtrl::Mark(size_t day, bool mark) { - wxFAIL_MSG( "not implemented" ); + wxCHECK_RET( day > 0 && day < 32, "invalid day" ); + + int mask = 1 << (day - 1); + if ( mark ) + m_marks |= mask; + else + m_marks &= ~mask; + + // calling Refresh() here is not enough to change the day appearance + UpdateMarks(); +} + +void wxCalendarCtrl::UpdateMarks() +{ + MONTHDAYSTATE states[3]; + const int nMonths = MonthCal_GetMonthRange(GetHwnd(), GMR_DAYSTATE, NULL); + wxCHECK_RET( nMonths <= (int)WXSIZEOF(states), "unexpected months range" ); + + for ( int i = 0; i < nMonths; i++ ) + states[i] = m_marks; + + if ( !MonthCal_SetDayState(GetHwnd(), nMonths, states) ) + { + wxLogLastError(_T("MonthCal_SetDayState")); + } +} + +void wxCalendarCtrl::UpdateFirstDayOfWeek() +{ + MonthCal_SetFirstDayOfWeek(GetHwnd(), HasFlag(wxCAL_MONDAY_FIRST) ? 0 : 6); } // ---------------------------------------------------------------------------- @@ -221,14 +355,60 @@ bool wxCalendarCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) NMHDR* hdr = (NMHDR *)lParam; switch ( hdr->code ) { - case MCN_SELECT: - NMSELCHANGE *sch = (NMSELCHANGE *)hdr; - GenerateEvent(wxEVT_CALENDAR_SEL_CHANGED); - *result = 0; - return true; + case MCN_SELCHANGE: + { + // we need to update m_date first, before calling the user code + // 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); + + // 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); + } + } + break; + + case MCN_GETDAYSTATE: + { + const NMDAYSTATE * const ds = (NMDAYSTATE *)lParam; + for ( int i = 0; i < ds->cDayState; i++ ) + { + ds->prgDayState[i] = m_marks; + } + } + break; + + default: + return wxCalendarCtrlBase::MSWOnNotify(idCtrl, lParam, result); + } + + *result = 0; + return true; +} + +void wxCalendarCtrl::MSWOnDoubleClick(wxMouseEvent& event) +{ + if ( HitTest(event.GetPosition()) == wxCAL_HITTEST_DAY ) + { + if ( GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED) ) + return; // skip event.Skip() below } - return wxCalendarCtrlBase::MSWOnNotify(idCtrl, lParam, result); + 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